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:
authorCorinna Vinschen <corinna@vinschen.de>2022-08-04 17:58:50 +0300
committerCorinna Vinschen <corinna@vinschen.de>2022-08-05 13:02:11 +0300
commit007e23d6390af11582e55453269b7a51c723d2dd (patch)
tree8e8cff3ca23f5e56d9766a5ee6c6abb366611b07 /winsup/cygwin/fhandler/pipe.cc
parent1e428bee1c5ef7c76ba4e46e6693b913edc9bbf3 (diff)
Cygwin: Reorganize cygwin source dir
Create subdirs and move files accordingly: - DevDocs: doc files - fhandler: fhandler sources, split fhandler.cc into base.cc and null.cc - local_includes: local include files - scripts: scripts called during build - sec: security sources Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup/cygwin/fhandler/pipe.cc')
-rw-r--r--winsup/cygwin/fhandler/pipe.cc1385
1 files changed, 1385 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/pipe.cc b/winsup/cygwin/fhandler/pipe.cc
new file mode 100644
index 000000000..720e4efd3
--- /dev/null
+++ b/winsup/cygwin/fhandler/pipe.cc
@@ -0,0 +1,1385 @@
+/* fhandler_pipe.cc: pipes for Cygwin.
+
+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 <stdlib.h>
+#include <sys/socket.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "tls_pbuf.h"
+#include <assert.h>
+
+/* This is only to be used for writing. 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; })
+
+fhandler_pipe_fifo::fhandler_pipe_fifo ()
+ : fhandler_base (), pipe_buf_size (DEFAULT_PIPEBUFSIZE)
+{
+}
+
+
+fhandler_pipe::fhandler_pipe ()
+ : fhandler_pipe_fifo (), popen_pid (0)
+{
+ need_fork_fixup (true);
+}
+
+/* The following function is intended for fhandler_pipe objects
+ created by the second version of fhandler_pipe::create below. See
+ the comment preceding the latter.
+
+ In addition to setting the blocking mode of the pipe handle, it
+ also sets the pipe's read mode to byte_stream unconditionally. */
+void
+fhandler_pipe::set_pipe_non_blocking (bool nonblocking)
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ FILE_PIPE_INFORMATION fpi;
+
+ fpi.ReadMode = FILE_PIPE_BYTE_STREAM_MODE;
+ fpi.CompletionMode = nonblocking ? FILE_PIPE_COMPLETE_OPERATION
+ : FILE_PIPE_QUEUE_OPERATION;
+ status = NtSetInformationFile (get_handle (), &io, &fpi, sizeof fpi,
+ FilePipeInformation);
+ if (!NT_SUCCESS (status))
+ debug_printf ("NtSetInformationFile(FilePipeInformation): %y", status);
+}
+
+int
+fhandler_pipe::init (HANDLE f, DWORD a, mode_t mode, int64_t uniq_id)
+{
+ /* FIXME: Have to clean this up someday
+ FIXME: Do we have to check for both !get_win32_name() and
+ !*get_win32_name()? */
+ if ((!get_win32_name () || !*get_win32_name ()) && get_name ())
+ {
+ char *d;
+ const char *s;
+ char *hold_normalized_name = (char *) alloca (strlen (get_name ()) + 1);
+ for (s = get_name (), d = hold_normalized_name; *s; s++, d++)
+ if (*s == '/')
+ *d = '\\';
+ else
+ *d = *s;
+ *d = '\0';
+ set_name (hold_normalized_name);
+ }
+
+ bool opened_properly = a & FILE_CREATE_PIPE_INSTANCE;
+ a &= ~FILE_CREATE_PIPE_INSTANCE;
+ fhandler_base::init (f, a, mode);
+ close_on_exec (mode & O_CLOEXEC);
+ set_ino (uniq_id);
+ set_unique_id (uniq_id | !!(mode & GENERIC_WRITE));
+ if (opened_properly)
+ /* Set read pipe always nonblocking to allow signal handling
+ even with FILE_SYNCHRONOUS_IO_NONALERT. */
+ set_pipe_non_blocking (get_device () == FH_PIPER ?
+ true : is_nonblocking ());
+ return 1;
+}
+
+extern "C" int sscanf (const char *, const char *, ...);
+
+int
+fhandler_pipe::open (int flags, mode_t mode)
+{
+ HANDLE proc, nio_hdl = NULL;
+ int64_t uniq_id;
+ fhandler_pipe *fh = NULL, *fhr = NULL, *fhw = NULL;
+ size_t size;
+ int pid, rwflags = (flags & O_ACCMODE);
+ bool inh;
+ bool got_one = false;
+
+ if (sscanf (get_name (), "/proc/%d/fd/pipe:[%llu]",
+ &pid, (long long *) &uniq_id) < 2)
+ {
+ set_errno (ENOENT);
+ return 0;
+ }
+ if (pid == myself->pid)
+ {
+ cygheap_fdenum cfd (true);
+ while (cfd.next () >= 0)
+ {
+ /* Windows doesn't allow to copy a pipe HANDLE with another access
+ mode. So we check for read and write side of pipe and try to
+ find the one matching the requested access mode. */
+ if (cfd->get_unique_id () == uniq_id)
+ got_one = true;
+ else if (cfd->get_unique_id () == uniq_id + 1)
+ got_one = true;
+ else
+ continue;
+ if ((rwflags == O_RDONLY && !(cfd->get_access () & GENERIC_READ))
+ || (rwflags == O_WRONLY && !(cfd->get_access () & GENERIC_WRITE)))
+ continue;
+ copy_from (cfd);
+ set_handle (NULL);
+ pc.close_conv_handle ();
+ if (!cfd->dup (this, flags))
+ return 1;
+ return 0;
+ }
+ /* Found the pipe but access mode didn't match? EACCES.
+ Otherwise ENOENT */
+ set_errno (got_one ? EACCES : ENOENT);
+ return 0;
+ }
+
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ return 0;
+ }
+ if (!(proc = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId)))
+ {
+ __seterrno ();
+ return 0;
+ }
+ fhr = p->pipe_fhandler (uniq_id, size);
+ if (fhr && rwflags == O_RDONLY)
+ fh = fhr;
+ else
+ {
+ fhw = p->pipe_fhandler (uniq_id + 1, size);
+ if (fhw && rwflags == O_WRONLY)
+ fh = fhw;
+ }
+ if (!fh)
+ {
+ /* Too bad, but Windows only allows the same access mode when dup'ing
+ the pipe. */
+ set_errno (fhr || fhw ? EACCES : ENOENT);
+ goto out;
+ }
+ inh = !(flags & O_CLOEXEC);
+ if (!DuplicateHandle (proc, fh->get_handle (), GetCurrentProcess (),
+ &nio_hdl, 0, inh, DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto out;
+ }
+ init (nio_hdl, fh->get_access (), mode & O_TEXT ?: O_BINARY,
+ fh->get_plain_ino ());
+ cfree (fh);
+ CloseHandle (proc);
+ return 1;
+out:
+ if (nio_hdl)
+ CloseHandle (nio_hdl);
+ if (fh)
+ free (fh);
+ if (proc)
+ CloseHandle (proc);
+ return 0;
+}
+
+bool
+fhandler_pipe::open_setup (int flags)
+{
+ bool read_mtx_created = false;
+
+ if (!fhandler_base::open_setup (flags))
+ goto err;
+ if (get_dev () == FH_PIPER && !read_mtx)
+ {
+ SECURITY_ATTRIBUTES *sa = sec_none_cloexec (flags);
+ read_mtx = CreateMutex (sa, FALSE, NULL);
+ if (read_mtx)
+ read_mtx_created = true;
+ else
+ {
+ debug_printf ("CreateMutex read_mtx failed: %E");
+ goto err;
+ }
+ }
+ if (!hdl_cnt_mtx)
+ {
+ SECURITY_ATTRIBUTES *sa = sec_none_cloexec (flags);
+ hdl_cnt_mtx = CreateMutex (sa, FALSE, NULL);
+ if (!hdl_cnt_mtx)
+ {
+ debug_printf ("CreateMutex hdl_cnt_mtx failed: %E");
+ goto err_close_read_mtx;
+ }
+ }
+ return true;
+
+err_close_read_mtx:
+ if (read_mtx_created)
+ CloseHandle (read_mtx);
+err:
+ return false;
+}
+
+off_t
+fhandler_pipe::lseek (off_t offset, int whence)
+{
+ debug_printf ("(%D, %d)", offset, whence);
+ set_errno (ESPIPE);
+ return -1;
+}
+
+int
+fhandler_pipe::fadvise (off_t offset, off_t length, int advice)
+{
+ return ESPIPE;
+}
+
+int
+fhandler_pipe::ftruncate (off_t length, bool allow_truncate)
+{
+ return allow_truncate ? EINVAL : ESPIPE;
+}
+
+char *
+fhandler_pipe::get_proc_fd_name (char *buf)
+{
+ __small_sprintf (buf, "pipe:[%U]", get_plain_ino ());
+ return buf;
+}
+
+void
+fhandler_pipe::release_select_sem (const char *from)
+{
+ LONG n_release;
+ if (get_dev () == FH_PIPER) /* Number of select() and writer */
+ n_release = get_obj_handle_count (select_sem)
+ - get_obj_handle_count (read_mtx);
+ else /* Number of select() call and reader */
+ n_release = get_obj_handle_count (select_sem)
+ - get_obj_handle_count (get_handle ());
+ debug_printf("%s(%s) release %d", from,
+ get_dev () == FH_PIPER ? "PIPER" : "PIPEW", n_release);
+ if (n_release)
+ ReleaseSemaphore (select_sem, n_release, NULL);
+}
+
+void
+fhandler_pipe::raw_read (void *ptr, size_t& len)
+{
+ size_t nbytes = 0;
+ NTSTATUS status = STATUS_SUCCESS;
+ IO_STATUS_BLOCK io;
+
+ if (!len)
+ return;
+
+ DWORD timeout = is_nonblocking () ? 0 : INFINITE;
+ DWORD waitret = cygwait (read_mtx, timeout);
+ switch (waitret)
+ {
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_TIMEOUT:
+ set_errno (EAGAIN);
+ len = (size_t) -1;
+ return;
+ case WAIT_SIGNALED:
+ set_errno (EINTR);
+ len = (size_t) -1;
+ return;
+ case WAIT_CANCELED:
+ pthread::static_cancel_self ();
+ /* NOTREACHED */
+ default:
+ /* Should not reach here. */
+ __seterrno ();
+ len = (size_t) -1;
+ return;
+ }
+ while (nbytes < len)
+ {
+ ULONG_PTR nbytes_now = 0;
+ ULONG len1 = (ULONG) (len - nbytes);
+
+ FILE_PIPE_LOCAL_INFORMATION fpli;
+ status = NtQueryInformationFile (get_handle (), &io,
+ &fpli, sizeof (fpli),
+ FilePipeLocalInformation);
+ if (NT_SUCCESS (status))
+ {
+ if (fpli.ReadDataAvailable == 0 && nbytes != 0)
+ break;
+ }
+ else if (nbytes != 0)
+ break;
+ status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr,
+ len1, NULL, NULL);
+ if (isclosed ()) /* A signal handler might have closed the fd. */
+ {
+ set_errno (EBADF);
+ nbytes = (size_t) -1;
+ }
+ else if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
+ {
+ nbytes_now = io.Information;
+ ptr = ((char *) ptr) + nbytes_now;
+ nbytes += nbytes_now;
+ if (select_sem && nbytes_now > 0)
+ release_select_sem ("raw_read");
+ }
+ else
+ {
+ /* Some errors are not really errors. Detect such cases here. */
+ switch (status)
+ {
+ case STATUS_END_OF_FILE:
+ case STATUS_PIPE_BROKEN:
+ /* This is really EOF. */
+ break;
+ case STATUS_PIPE_LISTENING:
+ case STATUS_PIPE_EMPTY:
+ if (nbytes != 0)
+ break;
+ if (is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ nbytes = (size_t) -1;
+ break;
+ }
+ waitret = cygwait (select_sem, 1);
+ if (waitret == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ else if (waitret == WAIT_SIGNALED)
+ {
+ set_errno (EINTR);
+ nbytes = (size_t) -1;
+ break;
+ }
+ continue;
+ default:
+ __seterrno_from_nt_status (status);
+ nbytes = (size_t) -1;
+ break;
+ }
+ }
+
+ if ((nbytes_now == 0 && !NT_SUCCESS (status))
+ || status == STATUS_BUFFER_OVERFLOW)
+ break;
+ }
+ ReleaseMutex (read_mtx);
+ len = nbytes;
+}
+
+bool
+fhandler_pipe::reader_closed ()
+{
+ if (!query_hdl)
+ return false;
+ WaitForSingleObject (hdl_cnt_mtx, INFINITE);
+ int n_reader = get_obj_handle_count (query_hdl);
+ int n_writer = get_obj_handle_count (get_handle ());
+ ReleaseMutex (hdl_cnt_mtx);
+ return n_reader == n_writer;
+}
+
+ssize_t
+fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
+{
+ size_t nbytes = 0;
+ ULONG chunk;
+ NTSTATUS status = STATUS_SUCCESS;
+ IO_STATUS_BLOCK io;
+ HANDLE evt;
+
+ if (!len)
+ return 0;
+
+ if (reader_closed ())
+ {
+ set_errno (EPIPE);
+ raise (SIGPIPE);
+ return -1;
+ }
+
+ if (len <= pipe_buf_size || pipe_buf_size == 0)
+ chunk = len;
+ else if (is_nonblocking ())
+ chunk = len = pipe_buf_size;
+ else
+ chunk = pipe_buf_size;
+
+ if (!(evt = CreateEvent (NULL, false, false, NULL)))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ /* Write in chunks, accumulating a total. If there's an error, just
+ return the accumulated total unless the first write fails, in
+ which case return -1. */
+ while (nbytes < len)
+ {
+ ULONG_PTR nbytes_now = 0;
+ size_t left = len - nbytes;
+ ULONG len1;
+ DWORD waitret = WAIT_OBJECT_0;
+
+ if (left > chunk)
+ len1 = chunk;
+ else
+ len1 = (ULONG) left;
+ /* NtWriteFile returns success with # of bytes written == 0 if writing
+ on a non-blocking pipe fails because the pipe buffer doesn't have
+ sufficient space.
+
+ POSIX requires
+ - A write request for {PIPE_BUF} or fewer bytes shall have the
+ following effect: if there is sufficient space available in the
+ pipe, write() shall transfer all the data and return the number
+ of bytes requested. Otherwise, write() shall transfer no data and
+ return -1 with errno set to [EAGAIN].
+
+ - A write request for more than {PIPE_BUF} bytes shall cause one
+ of the following:
+
+ - When at least one byte can be written, transfer what it can and
+ return the number of bytes written. When all data previously
+ written to the pipe is read, it shall transfer at least {PIPE_BUF}
+ bytes.
+
+ - When no data can be written, transfer no data, and return -1 with
+ errno set to [EAGAIN]. */
+ while (len1 > 0)
+ {
+ status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
+ (PVOID) ptr, len1, NULL, NULL);
+ if (status == STATUS_PENDING)
+ {
+ while (WAIT_TIMEOUT ==
+ (waitret = cygwait (evt, (DWORD) 0, cw_cancel | cw_sig)))
+ {
+ if (reader_closed ())
+ {
+ CancelIo (get_handle ());
+ set_errno (EPIPE);
+ raise (SIGPIPE);
+ goto out;
+ }
+ else
+ cygwait (select_sem, 10);
+ }
+ /* If io.Status is STATUS_CANCELLED after CancelIo, IO has
+ actually been cancelled and io.Information contains the
+ number of bytes processed so far.
+ Otherwise IO has been finished regulary and io.Status
+ contains valid success or error information. */
+ CancelIo (get_handle ());
+ if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED)
+ waitret = WAIT_OBJECT_0;
+
+ if (waitret == WAIT_CANCELED)
+ status = STATUS_THREAD_CANCELED;
+ else if (waitret == WAIT_SIGNALED)
+ status = STATUS_THREAD_SIGNALED;
+ else
+ status = io.Status;
+ }
+ if (!is_nonblocking () || !NT_SUCCESS (status) || io.Information > 0
+ || len <= PIPE_BUF)
+ break;
+ len1 >>= 1;
+ }
+ if (isclosed ()) /* A signal handler might have closed the fd. */
+ {
+ if (waitret == WAIT_OBJECT_0)
+ set_errno (EBADF);
+ else
+ __seterrno ();
+ }
+ else if (NT_SUCCESS (status)
+ || status == STATUS_THREAD_CANCELED
+ || status == STATUS_THREAD_SIGNALED)
+ {
+ nbytes_now = io.Information;
+ ptr = ((char *) ptr) + nbytes_now;
+ nbytes += nbytes_now;
+ if (select_sem && nbytes_now > 0)
+ release_select_sem ("raw_write");
+ /* 0 bytes returned? EAGAIN. See above. */
+ if (NT_SUCCESS (status) && nbytes == 0)
+ set_errno (EAGAIN);
+ }
+ else if (STATUS_PIPE_IS_CLOSED (status))
+ {
+ set_errno (EPIPE);
+ raise (SIGPIPE);
+ }
+ else
+ __seterrno_from_nt_status (status);
+
+ if (nbytes_now == 0)
+ break;
+ }
+out:
+ CloseHandle (evt);
+ if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
+ set_errno (EINTR);
+ else if (status == STATUS_THREAD_CANCELED)
+ pthread::static_cancel_self ();
+ return nbytes ?: -1;
+}
+
+void
+fhandler_pipe::set_close_on_exec (bool val)
+{
+ fhandler_base::set_close_on_exec (val);
+ if (read_mtx)
+ set_no_inheritance (read_mtx, val);
+ if (select_sem)
+ set_no_inheritance (select_sem, val);
+ if (query_hdl)
+ set_no_inheritance (query_hdl, val);
+ set_no_inheritance (hdl_cnt_mtx, val);
+}
+
+void
+fhandler_pipe::fixup_after_fork (HANDLE parent)
+{
+ fork_fixup (parent, hdl_cnt_mtx, "hdl_cnt_mtx");
+ WaitForSingleObject (hdl_cnt_mtx, INFINITE);
+ if (read_mtx)
+ fork_fixup (parent, read_mtx, "read_mtx");
+ if (select_sem)
+ fork_fixup (parent, select_sem, "select_sem");
+ if (query_hdl)
+ fork_fixup (parent, query_hdl, "query_hdl");
+ if (query_hdl_close_req_evt)
+ fork_fixup (parent, query_hdl_close_req_evt, "query_hdl_close_req_evt");
+
+ fhandler_base::fixup_after_fork (parent);
+ ReleaseMutex (hdl_cnt_mtx);
+}
+
+int
+fhandler_pipe::dup (fhandler_base *child, int flags)
+{
+ fhandler_pipe *ftp = (fhandler_pipe *) child;
+ ftp->set_popen_pid (0);
+
+ int res = 0;
+ WaitForSingleObject (hdl_cnt_mtx, INFINITE);
+ if (fhandler_base::dup (child, flags))
+ res = -1;
+ else if (read_mtx &&
+ !DuplicateHandle (GetCurrentProcess (), read_mtx,
+ GetCurrentProcess (), &ftp->read_mtx,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ ftp->close ();
+ res = -1;
+ }
+ else if (select_sem &&
+ !DuplicateHandle (GetCurrentProcess (), select_sem,
+ GetCurrentProcess (), &ftp->select_sem,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ ftp->close ();
+ res = -1;
+ }
+ else if (query_hdl &&
+ !DuplicateHandle (GetCurrentProcess (), query_hdl,
+ GetCurrentProcess (), &ftp->query_hdl,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ ftp->close ();
+ res = -1;
+ }
+ else if (!DuplicateHandle (GetCurrentProcess (), hdl_cnt_mtx,
+ GetCurrentProcess (), &ftp->hdl_cnt_mtx,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ ftp->close ();
+ res = -1;
+ }
+ else if (query_hdl_close_req_evt &&
+ !DuplicateHandle (GetCurrentProcess (), query_hdl_close_req_evt,
+ GetCurrentProcess (),
+ &ftp->query_hdl_close_req_evt,
+ 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ ftp->close ();
+ res = -1;
+ }
+ ReleaseMutex (hdl_cnt_mtx);
+
+ debug_printf ("res %d", res);
+ return res;
+}
+
+int
+fhandler_pipe::close ()
+{
+ if (select_sem)
+ {
+ release_select_sem ("close");
+ CloseHandle (select_sem);
+ }
+ if (read_mtx)
+ CloseHandle (read_mtx);
+ WaitForSingleObject (hdl_cnt_mtx, INFINITE);
+ if (query_hdl)
+ CloseHandle (query_hdl);
+ if (query_hdl_close_req_evt)
+ CloseHandle (query_hdl_close_req_evt);
+ int ret = fhandler_base::close ();
+ ReleaseMutex (hdl_cnt_mtx);
+ CloseHandle (hdl_cnt_mtx);
+ if (query_hdl_proc)
+ CloseHandle (query_hdl_proc);
+ return ret;
+}
+
+#define PIPE_INTRO "\\\\.\\pipe\\cygwin-"
+
+/* Create a pipe, and return handles to the read and write ends,
+ just like CreatePipe, but ensure that the write end permits
+ FILE_READ_ATTRIBUTES access, on later versions of win32 where
+ this is supported. This access is needed by NtQueryInformationFile,
+ which is used to implement select and nonblocking writes.
+ Note that the return value is either 0 or GetLastError,
+ unlike CreatePipe, which returns a bool for success or failure. */
+DWORD
+fhandler_pipe::create (LPSECURITY_ATTRIBUTES sa_ptr, PHANDLE r, PHANDLE w,
+ DWORD psize, const char *name, DWORD open_mode,
+ int64_t *unique_id)
+{
+ /* Default to error. */
+ if (r)
+ *r = NULL;
+ if (w)
+ *w = NULL;
+
+ /* Ensure that there is enough pipe buffer space for atomic writes. */
+ if (!psize)
+ psize = DEFAULT_PIPEBUFSIZE;
+
+ char pipename[MAX_PATH];
+ size_t len = __small_sprintf (pipename, PIPE_INTRO "%S-",
+ &cygheap->installation_key);
+ DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
+ if (!name)
+ pipe_mode |= pipe_byte ? PIPE_TYPE_BYTE : PIPE_TYPE_MESSAGE;
+ else
+ pipe_mode |= PIPE_TYPE_MESSAGE;
+
+ if (!name || (open_mode & PIPE_ADD_PID))
+ {
+ len += __small_sprintf (pipename + len, "%u-", GetCurrentProcessId ());
+ open_mode &= ~PIPE_ADD_PID;
+ }
+
+ if (name)
+ len += __small_sprintf (pipename + len, "%s", name);
+
+ open_mode |= PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
+
+ /* Retry CreateNamedPipe as long as the pipe name is in use.
+ Retrying will probably never be necessary, but we want
+ to be as robust as possible. */
+ DWORD err = 0;
+ while (r && !*r)
+ {
+ static volatile ULONG pipe_unique_id;
+ if (!name)
+ {
+ LONG id = InterlockedIncrement ((LONG *) &pipe_unique_id);
+ __small_sprintf (pipename + len, "pipe-%p", id);
+ if (unique_id)
+ *unique_id = ((int64_t) id << 32 | GetCurrentProcessId ());
+ }
+
+ debug_printf ("name %s, size %u, mode %s", pipename, psize,
+ (pipe_mode & PIPE_TYPE_MESSAGE)
+ ? "PIPE_TYPE_MESSAGE" : "PIPE_TYPE_BYTE");
+
+ /* Use CreateNamedPipe instead of CreatePipe, because the latter
+ returns a write handle that does not permit FILE_READ_ATTRIBUTES
+ access, on versions of win32 earlier than WinXP SP2.
+ CreatePipe also stupidly creates a full duplex pipe, which is
+ a waste, since only a single direction is actually used.
+ It's important to only allow a single instance, to ensure that
+ the pipe was not created earlier by some other process, even if
+ the pid has been reused.
+
+ Note that the write side of the pipe is opened as PIPE_TYPE_MESSAGE.
+ This *seems* to more closely mimic Linux pipe behavior and is
+ definitely required for pty handling since fhandler_pty_master
+ writes to the pipe in chunks, terminated by newline when CANON mode
+ is specified. */
+ *r = CreateNamedPipe (pipename, open_mode, pipe_mode, 1, psize,
+ psize, NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
+
+ if (*r != INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("pipe read handle %p", *r);
+ err = 0;
+ break;
+ }
+
+ err = GetLastError ();
+ switch (err)
+ {
+ case ERROR_PIPE_BUSY:
+ /* The pipe is already open with compatible parameters.
+ Pick a new name and retry. */
+ debug_printf ("pipe busy", !name ? ", retrying" : "");
+ if (!name)
+ *r = NULL;
+ break;
+ case ERROR_ACCESS_DENIED:
+ /* The pipe is already open with incompatible parameters.
+ Pick a new name and retry. */
+ debug_printf ("pipe access denied%s", !name ? ", retrying" : "");
+ if (!name)
+ *r = NULL;
+ break;
+ default:
+ {
+ err = GetLastError ();
+ debug_printf ("failed, %E");
+ }
+ }
+ }
+
+ if (err)
+ {
+ *r = NULL;
+ return err;
+ }
+
+ if (!w)
+ debug_printf ("pipe write handle NULL");
+ else
+ {
+ debug_printf ("CreateFile: name %s", pipename);
+
+ /* Open the named pipe for writing.
+ Be sure to permit FILE_READ_ATTRIBUTES access. */
+ DWORD access = GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+ if ((open_mode & PIPE_ACCESS_DUPLEX) == PIPE_ACCESS_DUPLEX)
+ access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
+ *w = CreateFile (pipename, access, 0, sa_ptr, OPEN_EXISTING,
+ open_mode & FILE_FLAG_OVERLAPPED, 0);
+
+ if (!*w || *w == INVALID_HANDLE_VALUE)
+ {
+ /* Failure. */
+ DWORD err = GetLastError ();
+ debug_printf ("CreateFile failed, r %p, %E", r);
+ if (r)
+ CloseHandle (*r);
+ *w = NULL;
+ return err;
+ }
+
+ debug_printf ("pipe write handle %p", *w);
+ }
+
+ /* Success. */
+ return 0;
+}
+
+inline static bool
+is_running_as_service (void)
+{
+ return check_token_membership (well_known_service_sid)
+ || cygheap->user.saved_sid () == well_known_system_sid;
+}
+
+/* The next version of fhandler_pipe::create used to call the previous
+ version. But the read handle created by the latter doesn't have
+ FILE_WRITE_ATTRIBUTES access unless the pipe is created with
+ PIPE_ACCESS_DUPLEX, and it doesn't seem possible to add that access
+ right. This causes set_pipe_non_blocking to fail.
+
+ To fix this we will define a helper function 'nt_create' that is
+ similar to the above fhandler_pipe::create but uses
+ NtCreateNamedPipeFile instead of CreateNamedPipe; this gives more
+ flexibility in setting the access rights. We will use this helper
+ function in the version of fhandler_pipe::create below, which
+ suffices for all of our uses of set_pipe_non_blocking. For
+ simplicity, nt_create will omit the 'open_mode' and 'name'
+ parameters, which aren't needed for our purposes. */
+
+static int nt_create (LPSECURITY_ATTRIBUTES, HANDLE &, HANDLE &, DWORD,
+ int64_t *);
+
+int
+fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode)
+{
+ HANDLE r, w;
+ SECURITY_ATTRIBUTES *sa = sec_none_cloexec (mode);
+ int res = -1;
+ int64_t unique_id;
+
+ int ret = nt_create (sa, r, w, psize, &unique_id);
+ if (ret)
+ {
+ __seterrno_from_win_error (ret);
+ goto out;
+ }
+ if ((fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev)) == NULL)
+ goto err_close_rw_handle;
+ if ((fhs[1] = (fhandler_pipe *) build_fh_dev (*pipew_dev)) == NULL)
+ goto err_delete_fhs0;
+ mode |= mode & O_TEXT ?: O_BINARY;
+ fhs[0]->init (r, FILE_CREATE_PIPE_INSTANCE | GENERIC_READ, mode, unique_id);
+ fhs[1]->init (w, FILE_CREATE_PIPE_INSTANCE | GENERIC_WRITE, mode, unique_id);
+
+ /* For the read side of the pipe, add a mutex. See raw_read for the
+ usage. */
+ fhs[0]->read_mtx = CreateMutexW (sa, FALSE, NULL);
+ if (!fhs[0]->read_mtx)
+ goto err_delete_fhs1;
+
+ fhs[0]->select_sem = CreateSemaphore (sa, 0, INT32_MAX, NULL);
+ if (!fhs[0]->select_sem)
+ goto err_close_read_mtx;
+ if (!DuplicateHandle (GetCurrentProcess (), fhs[0]->select_sem,
+ GetCurrentProcess (), &fhs[1]->select_sem,
+ 0, sa->bInheritHandle, DUPLICATE_SAME_ACCESS))
+ goto err_close_select_sem0;
+
+ if (is_running_as_service () &&
+ !DuplicateHandle (GetCurrentProcess (), r,
+ GetCurrentProcess (), &fhs[1]->query_hdl,
+ FILE_READ_DATA, sa->bInheritHandle, 0))
+ goto err_close_select_sem1;
+
+ fhs[0]->hdl_cnt_mtx = CreateMutexW (sa, FALSE, NULL);
+ if (!fhs[0]->hdl_cnt_mtx)
+ goto err_close_query_hdl;
+ if (!DuplicateHandle (GetCurrentProcess (), fhs[0]->hdl_cnt_mtx,
+ GetCurrentProcess (), &fhs[1]->hdl_cnt_mtx,
+ 0, sa->bInheritHandle, DUPLICATE_SAME_ACCESS))
+ goto err_close_hdl_cnt_mtx0;
+
+ if (fhs[1]->query_hdl)
+ {
+ fhs[1]->query_hdl_close_req_evt = CreateEvent (sa, TRUE, FALSE, NULL);
+ if (!fhs[1]->query_hdl_close_req_evt)
+ goto err_close_hdl_cnt_mtx1;
+ }
+
+ res = 0;
+ goto out;
+
+err_close_hdl_cnt_mtx1:
+ CloseHandle (fhs[1]->hdl_cnt_mtx);
+err_close_hdl_cnt_mtx0:
+ CloseHandle (fhs[0]->hdl_cnt_mtx);
+err_close_query_hdl:
+ if (fhs[1]->query_hdl)
+ CloseHandle (fhs[1]->query_hdl);
+err_close_select_sem1:
+ CloseHandle (fhs[1]->select_sem);
+err_close_select_sem0:
+ CloseHandle (fhs[0]->select_sem);
+err_close_read_mtx:
+ CloseHandle (fhs[0]->read_mtx);
+err_delete_fhs1:
+ delete fhs[1];
+err_delete_fhs0:
+ delete fhs[0];
+err_close_rw_handle:
+ NtClose (r);
+ NtClose (w);
+out:
+ debug_printf ("%R = pipe([%p, %p], %d, %y)",
+ res, fhs[0], fhs[1], psize, mode);
+ return res;
+}
+
+static int
+nt_create (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE &r, HANDLE &w,
+ DWORD psize, int64_t *unique_id)
+{
+ NTSTATUS status;
+ HANDLE npfsh;
+ ACCESS_MASK access;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ LARGE_INTEGER timeout;
+
+ /* Default to error. */
+ r = NULL;
+ w = NULL;
+
+ status = fhandler_base::npfs_handle (npfsh);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return GetLastError ();
+ }
+
+ /* Ensure that there is enough pipe buffer space for atomic writes. */
+ if (!psize)
+ psize = DEFAULT_PIPEBUFSIZE;
+
+ UNICODE_STRING pipename;
+ WCHAR pipename_buf[MAX_PATH];
+ size_t len = __small_swprintf (pipename_buf, L"%S-%u-",
+ &cygheap->installation_key,
+ GetCurrentProcessId ());
+
+ access = GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE;
+ access |= FILE_WRITE_EA; /* Add this right as a marker of cygwin read pipe */
+
+ ULONG pipe_type = pipe_byte ? FILE_PIPE_BYTE_STREAM_TYPE
+ : FILE_PIPE_MESSAGE_TYPE;
+
+ /* Retry NtCreateNamedPipeFile as long as the pipe name is in use.
+ Retrying will probably never be necessary, but we want
+ to be as robust as possible. */
+ DWORD err = 0;
+ while (!r)
+ {
+ static volatile ULONG pipe_unique_id;
+ LONG id = InterlockedIncrement ((LONG *) &pipe_unique_id);
+ __small_swprintf (pipename_buf + len, L"pipe-nt-%p", id);
+ if (unique_id)
+ *unique_id = ((int64_t) id << 32 | GetCurrentProcessId ());
+
+ debug_printf ("name %W, size %u, mode %s", pipename_buf, psize,
+ (pipe_type & FILE_PIPE_MESSAGE_TYPE)
+ ? "PIPE_TYPE_MESSAGE" : "PIPE_TYPE_BYTE");
+
+ RtlInitUnicodeString (&pipename, pipename_buf);
+
+ InitializeObjectAttributes (&attr, &pipename,
+ sa_ptr->bInheritHandle ? OBJ_INHERIT : 0,
+ npfsh, sa_ptr->lpSecurityDescriptor);
+
+ timeout.QuadPart = -500000;
+ /* Set FILE_SYNCHRONOUS_IO_NONALERT flag so that native
+ C# programs work with cygwin pipe. */
+ status = NtCreateNamedPipeFile (&r, access, &attr, &io,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_SYNCHRONOUS_IO_NONALERT, pipe_type,
+ FILE_PIPE_BYTE_STREAM_MODE,
+ 0, 1, psize, psize, &timeout);
+
+ if (NT_SUCCESS (status))
+ {
+ debug_printf ("pipe read handle %p", r);
+ err = 0;
+ break;
+ }
+
+ switch (status)
+ {
+ case STATUS_PIPE_BUSY:
+ case STATUS_INSTANCE_NOT_AVAILABLE:
+ case STATUS_PIPE_NOT_AVAILABLE:
+ /* The pipe is already open with compatible parameters.
+ Pick a new name and retry. */
+ debug_printf ("pipe busy, retrying");
+ r = NULL;
+ break;
+ case STATUS_ACCESS_DENIED:
+ /* The pipe is already open with incompatible parameters.
+ Pick a new name and retry. */
+ debug_printf ("pipe access denied, retrying");
+ r = NULL;
+ break;
+ default:
+ {
+ __seterrno_from_nt_status (status);
+ err = GetLastError ();
+ debug_printf ("failed, %E");
+ r = NULL;
+ }
+ }
+ }
+
+ if (err)
+ {
+ r = NULL;
+ return err;
+ }
+
+ debug_printf ("NtOpenFile: name %S", &pipename);
+
+ access = GENERIC_WRITE | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
+ status = NtOpenFile (&w, access, &attr, &io, 0, 0);
+ if (!NT_SUCCESS (status))
+ {
+ DWORD err = GetLastError ();
+ debug_printf ("NtOpenFile failed, r %p, %E", r);
+ if (r)
+ NtClose (r);
+ w = NULL;
+ return err;
+ }
+
+ /* Success. */
+ return 0;
+}
+
+/* Called by dtable::init_std_file_from_handle for stdio handles
+ inherited from non-Cygwin processes. */
+void
+fhandler_pipe::set_pipe_buf_size ()
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ FILE_PIPE_LOCAL_INFORMATION fpli;
+
+ status = NtQueryInformationFile (get_handle (), &io, &fpli, sizeof fpli,
+ FilePipeLocalInformation);
+ if (NT_SUCCESS (status))
+ pipe_buf_size = fpli.InboundQuota;
+}
+
+int
+fhandler_pipe::ioctl (unsigned int cmd, void *p)
+{
+ int n;
+
+ switch (cmd)
+ {
+ case FIONREAD:
+ if (get_device () == FH_PIPEW)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (!PeekNamedPipe (get_handle (), NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ break;
+ default:
+ return fhandler_base::ioctl (cmd, p);
+ break;
+ }
+ *(int *) p = n;
+ return 0;
+}
+
+int
+fhandler_pipe::fcntl (int cmd, intptr_t arg)
+{
+ if (cmd != F_SETFL)
+ 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 ();
+ /* Do not set blocking mode for read pipe to allow signal handling
+ even with FILE_SYNCHRONOUS_IO_NONALERT. */
+ if (now_nonblocking != was_nonblocking && get_device () != FH_PIPER)
+ set_pipe_non_blocking (now_nonblocking);
+ return res;
+}
+
+int
+fhandler_pipe::fstat (struct stat *buf)
+{
+ int ret = fhandler_base::fstat (buf);
+ if (!ret)
+ {
+ buf->st_dev = FH_PIPE;
+ if (!(buf->st_ino = get_plain_ino ()))
+ sscanf (get_name (), "/proc/%*d/fd/pipe:[%llu]",
+ (long long *) &buf->st_ino);
+ }
+ return ret;
+}
+
+int
+fhandler_pipe::fstatvfs (struct statvfs *sfs)
+{
+ set_errno (EBADF);
+ return -1;
+}
+
+HANDLE
+fhandler_pipe::temporary_query_hdl ()
+{
+ if (get_dev () != FH_PIPEW)
+ return NULL;
+
+ ULONG len;
+ NTSTATUS status;
+ tmp_pathbuf tp;
+ OBJECT_NAME_INFORMATION *ntfn = (OBJECT_NAME_INFORMATION *) tp.w_get ();
+
+ /* Try process handle opened and pipe handle value cached first
+ in order to reduce overhead. */
+ if (query_hdl_proc && query_hdl_value)
+ {
+ HANDLE h;
+ if (!DuplicateHandle (query_hdl_proc, query_hdl_value,
+ GetCurrentProcess (), &h, FILE_READ_DATA, 0, 0))
+ goto cache_err;
+ /* Check name */
+ status = NtQueryObject (h, ObjectNameInformation, ntfn, 65536, &len);
+ if (!NT_SUCCESS (status) || !ntfn->Name.Buffer)
+ goto hdl_err;
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = L'\0';
+ uint64_t key;
+ DWORD pid;
+ LONG id;
+ if (swscanf (ntfn->Name.Buffer,
+ L"\\Device\\NamedPipe\\%llx-%u-pipe-nt-0x%x",
+ &key, &pid, &id) == 3 &&
+ key == pipename_key && pid == pipename_pid && id == pipename_id)
+ return h;
+hdl_err:
+ CloseHandle (h);
+cache_err:
+ CloseHandle (query_hdl_proc);
+ query_hdl_proc = NULL;
+ query_hdl_value = NULL;
+ }
+
+ status = NtQueryObject (get_handle (), ObjectNameInformation, ntfn,
+ 65536, &len);
+ if (!NT_SUCCESS (status) || !ntfn->Name.Buffer)
+ return NULL; /* Non cygwin pipe? */
+ WCHAR name[MAX_PATH];
+ int namelen = min (ntfn->Name.Length / sizeof (WCHAR), MAX_PATH-1);
+ memcpy (name, ntfn->Name.Buffer, namelen * sizeof (WCHAR));
+ name[namelen] = L'\0';
+ if (swscanf (name, L"\\Device\\NamedPipe\\%llx-%u-pipe-nt-0x%x",
+ &pipename_key, &pipename_pid, &pipename_id) != 3)
+ return NULL; /* Non cygwin pipe? */
+
+ if (wincap.has_query_process_handle_info ())
+ return get_query_hdl_per_process (name, ntfn); /* Since Win8 */
+ else
+ return get_query_hdl_per_system (name, ntfn); /* Win7 */
+}
+
+/* This function is faster than get_query_hdl_per_system(), however,
+ only works since Windows 8 because ProcessHandleInformation is not
+ suppoted by NtQueryInformationProcess() before Windows 8. */
+HANDLE
+fhandler_pipe::get_query_hdl_per_process (WCHAR *name,
+ OBJECT_NAME_INFORMATION *ntfn)
+{
+ NTSTATUS status;
+ ULONG len;
+ DWORD n_process = 256;
+ PSYSTEM_PROCESS_INFORMATION spi;
+ do
+ { /* Enumerate processes */
+ DWORD nbytes = n_process * sizeof (SYSTEM_PROCESS_INFORMATION);
+ spi = (PSYSTEM_PROCESS_INFORMATION) HeapAlloc (GetProcessHeap (),
+ 0, nbytes);
+ if (!spi)
+ return NULL;
+ status = NtQuerySystemInformation (SystemProcessInformation,
+ spi, nbytes, &len);
+ if (NT_SUCCESS (status))
+ break;
+ HeapFree (GetProcessHeap (), 0, spi);
+ n_process *= 2;
+ }
+ while (n_process < (1L<<20) && status == STATUS_INFO_LENGTH_MISMATCH);
+ if (!NT_SUCCESS (status))
+ return NULL;
+
+ /* In most cases, it is faster to check the processes in reverse order.
+ To do this, store PIDs into an array. */
+ DWORD *proc_pids = (DWORD *) HeapAlloc (GetProcessHeap (), 0,
+ n_process * sizeof (DWORD));
+ if (!proc_pids)
+ {
+ HeapFree (GetProcessHeap (), 0, spi);
+ return NULL;
+ }
+ PSYSTEM_PROCESS_INFORMATION p = spi;
+ n_process = 0;
+ while (true)
+ {
+ proc_pids[n_process++] = (DWORD)(intptr_t) p->UniqueProcessId;
+ if (!p->NextEntryOffset)
+ break;
+ p = (PSYSTEM_PROCESS_INFORMATION) ((char *) p + p->NextEntryOffset);
+ }
+ HeapFree (GetProcessHeap (), 0, spi);
+
+ for (LONG i = (LONG) n_process - 1; i >= 0; i--)
+ {
+ HANDLE proc = OpenProcess (PROCESS_DUP_HANDLE
+ | PROCESS_QUERY_INFORMATION,
+ 0, proc_pids[i]);
+ if (!proc)
+ continue;
+
+ /* Retrieve process handles */
+ DWORD n_handle = 256;
+ PPROCESS_HANDLE_SNAPSHOT_INFORMATION phi;
+ do
+ {
+ DWORD nbytes = 2 * sizeof (ULONG_PTR) +
+ n_handle * sizeof (PROCESS_HANDLE_TABLE_ENTRY_INFO);
+ phi = (PPROCESS_HANDLE_SNAPSHOT_INFORMATION)
+ HeapAlloc (GetProcessHeap (), 0, nbytes);
+ if (!phi)
+ goto close_proc;
+ /* NtQueryInformationProcess can return STATUS_SUCCESS with
+ invalid handle data for certain processes. See
+ https://github.com/processhacker/processhacker/blob/05f5e9fa477dcaa1709d9518170d18e1b3b8330d/phlib/native.c#L5754.
+ We need to ensure that NumberOfHandles is zero in this
+ case to avoid a crash in the for loop below. */
+ phi->NumberOfHandles = 0;
+ status = NtQueryInformationProcess (proc, ProcessHandleInformation,
+ phi, nbytes, &len);
+ if (NT_SUCCESS (status))
+ break;
+ HeapFree (GetProcessHeap (), 0, phi);
+ n_handle *= 2;
+ }
+ while (n_handle < (1L<<20) && status == STATUS_INFO_LENGTH_MISMATCH);
+ if (!NT_SUCCESS (status))
+ goto close_proc;
+
+ /* Sanity check in case Microsoft changes
+ NtQueryInformationProcess and the initialization of
+ NumberOfHandles above is no longer sufficient. */
+ assert (phi->NumberOfHandles <= n_handle);
+ for (ULONG j = 0; j < phi->NumberOfHandles; j++)
+ {
+ /* Check for the peculiarity of cygwin read pipe */
+ const ULONG access = FILE_READ_DATA | FILE_READ_EA
+ | FILE_WRITE_EA /* marker */
+ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
+ | READ_CONTROL | SYNCHRONIZE;
+ if (phi->Handles[j].GrantedAccess != access)
+ continue;
+
+ /* Retrieve handle */
+ HANDLE h = (HANDLE)(intptr_t) phi->Handles[j].HandleValue;
+ BOOL res = DuplicateHandle (proc, h, GetCurrentProcess (), &h,
+ FILE_READ_DATA, 0, 0);
+ if (!res)
+ continue;
+
+ /* Check object name */
+ status = NtQueryObject (h, ObjectNameInformation,
+ ntfn, 65536, &len);
+ if (!NT_SUCCESS (status) || !ntfn->Name.Buffer)
+ goto close_handle;
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = L'\0';
+ if (wcscmp (name, ntfn->Name.Buffer) == 0)
+ {
+ query_hdl_proc = proc;
+ query_hdl_value = (HANDLE)(intptr_t) phi->Handles[j].HandleValue;
+ HeapFree (GetProcessHeap (), 0, phi);
+ HeapFree (GetProcessHeap (), 0, proc_pids);
+ return h;
+ }
+close_handle:
+ CloseHandle (h);
+ }
+ HeapFree (GetProcessHeap (), 0, phi);
+close_proc:
+ CloseHandle (proc);
+ }
+ HeapFree (GetProcessHeap (), 0, proc_pids);
+ return NULL;
+}
+
+/* This function is slower than get_query_hdl_per_process(), however,
+ works even before Windows 8. */
+HANDLE
+fhandler_pipe::get_query_hdl_per_system (WCHAR *name,
+ OBJECT_NAME_INFORMATION *ntfn)
+{
+ NTSTATUS status;
+ SIZE_T n_handle = 65536;
+ PSYSTEM_HANDLE_INFORMATION shi;
+ do
+ { /* Enumerate handles */
+ SIZE_T nbytes =
+ sizeof (ULONG) + n_handle * sizeof (SYSTEM_HANDLE_TABLE_ENTRY_INFO);
+ shi = (PSYSTEM_HANDLE_INFORMATION) HeapAlloc (GetProcessHeap (),
+ 0, nbytes);
+ if (!shi)
+ return NULL;
+ status = NtQuerySystemInformation (SystemHandleInformation,
+ shi, nbytes, NULL);
+ if (NT_SUCCESS (status))
+ break;
+ HeapFree (GetProcessHeap (), 0, shi);
+ n_handle *= 2;
+ }
+ while (n_handle < (1L<<23) && status == STATUS_INFO_LENGTH_MISMATCH);
+ if (!NT_SUCCESS (status))
+ return NULL;
+
+ for (LONG i = (LONG) shi->NumberOfHandles - 1; i >= 0; i--)
+ {
+ /* Check for the peculiarity of cygwin read pipe */
+ const ULONG access = FILE_READ_DATA | FILE_READ_EA
+ | FILE_WRITE_EA /* marker */
+ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
+ | READ_CONTROL | SYNCHRONIZE;
+ if (shi->Handles[i].GrantedAccess != access)
+ continue;
+
+ /* Retrieve handle */
+ HANDLE proc = OpenProcess (PROCESS_DUP_HANDLE, 0,
+ shi->Handles[i].UniqueProcessId);
+ if (!proc)
+ continue;
+ HANDLE h = (HANDLE)(intptr_t) shi->Handles[i].HandleValue;
+ BOOL res = DuplicateHandle (proc, h, GetCurrentProcess (), &h,
+ FILE_READ_DATA, 0, 0);
+ if (!res)
+ goto close_proc;
+
+ /* Check object name */
+ ULONG len;
+ status = NtQueryObject (h, ObjectNameInformation, ntfn, 65536, &len);
+ if (!NT_SUCCESS (status) || !ntfn->Name.Buffer)
+ goto close_handle;
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = L'\0';
+ if (wcscmp (name, ntfn->Name.Buffer) == 0)
+ {
+ query_hdl_proc = proc;
+ query_hdl_value = (HANDLE)(intptr_t) shi->Handles[i].HandleValue;
+ HeapFree (GetProcessHeap (), 0, shi);
+ return h;
+ }
+close_handle:
+ CloseHandle (h);
+close_proc:
+ CloseHandle (proc);
+ }
+ HeapFree (GetProcessHeap (), 0, shi);
+ return NULL;
+}