If O_PATH is set, then the fhandler_fifo object has a handle that can
be used for getting the statvfs information. Use it by calling
fhandler_base::fstatvfs_by_handle. Before this change,
fhandler_disk_file::fstatfvs was called on a new fhandler_disk_file
object, which would then have to be opened.
If the O_PATH flag is set, fhandler_fifo::open now simply calls
fhandler_base::open_fs.
The previous attempt to handle O_PATH in commit aa55d22c, "Cygwin:
honor the O_PATH flag when opening a FIFO", fixed a hang but otherwise
didn't do anything useful.
Add that flag to the pipe type argument when creating the Windows
named pipe. And add a definition of that flag to ntdll.h (copied from
/usr/include/w32api/ddk/ntifs.h).
Make it a manual reset event. It's only used once to allow a reader
to open, and there's no reason to ever reset it. Defensively set it
when a client connection is recorded, even though it should be set by
the writer that connected.
Call NtReadFile directly instead of calling fhandler_base::raw_read.
In addition to being simpler, this gives us access to the return value
from NtReadFile.
Add a method fifo_client_handler::pipe_state that queries Windows for
the state of a pipe instance. Use this to help terminate the
listen_client thread cleanly.
If the last client handler is useless, delete it instead of declaring
it invalid.
fhandler_fifo::close could be called from a signal handler or another
thread at a time when another function is holding the fifo_client
lock. This could prevent the listen_client thread from acting on the
thread termination event. Avoid a deadlock by calling
fifo_client_unlock at the beginning of fhandler_fifo::close.
There's no longer a need to consider the connect_evt after fork/exec.
After stopping the listen_client thread, all client handlers should be
in the fc_connected or fc_invalid states, so their connect_evt members
won't be used again.
Also remove code in fhandler_fifo::dup that just repeats things
already done in stop_listen_client.
Add a method fhandler_fifo::check_listen_client_thread that checks
whether the thread is running. Use it in raw_read instead of just
testing the handle listen_client_thr.
On exit from the listen_client thread, make sure there's no pending
FSCTL_PIPE_LISTEN request. Otherwise we might get a client connection
after restarting the thread, and we won't have a handle for
communicating with that client.
Remove the retry loop in the case of STATUS_PIPE_LISTENING; that case
shouldn't occur.
Remove the now-unused fc_connecting value from
fifo_client_connect_state.
It's now up to the caller to pass a handle to open_pipe and, if
desired, to call set_handle on return.
This will be useful for a future commit, in which we will open a
client connection without setting an io_handle.
When opening a duplexer, open a client connection to the first client
handler. Previously we gave the duplexer a bogus write handle, which
was just a duplicate of the first client handler's handle. This meant
that we had a pipe server with no clients connected, and all I/O
attempts failed with STATUS_PIPE_LISTENING.
Extend the last fcntl change to duplexers.
Remove a now unused fifo_client_handler constructor, as well as the
long unusued method fifo_client_handler::connect.
Don't create the pipe in duplex mode; the server handle will only be
used for reading.
The blocking mode of the Windows pipe underlying a writer is set to
match that of the writer itself when the latter is opened. Define
fhandler_fifo::fcntl to keep the pipe and the writer in sync if the
blocking mode is changed via fcntl.
Add methods need_fixup_before, init_fixup_before, and
fixup_before_fork_exec to accomplish this. Stopping the thread makes
sure that the client handler lists of the parent and child remain in
sync while the forking/execing is in progress.
Don't use the same i/o handle for the first client handler as is used
for the fhandler itself; this can lead to a later attempt to close the
same handle twice. Instead use a duplicate.
Define fhandler:fifo::fixup_after_exec, which sets listen_client_thr
and lct_termination_evt to NULL. This forces the listen_client thread
to restart on the first attempt to read after an exec. Previously the
exec'd process could hang in fhandler_fifo::raw_read.
Remove fifo_client_handler::connect and move its code into
listen_client_thread. That way we can check the return status when a
client handler's connect_evt is signaled. Previously we incorrectly
assumed there was a successful connection.
Also simplify listen_client_thread in the following ways:
- Replace fhandler_fifo::disconnect_and_reconnect by a new
delete_client_handler method. Now we just delete invalid client
handlers rather than trying to re-use them.
- Try to maintain a client handler list that consists of connected
client handlers and exactly one that is listening for a connection.
This allows us to call WaitForMultipleObjects with only two wait
objects.
- Remove 'dummy_evt' from the fifo_client_handler struct; it is no
longer needed.
- On exit from listen_client_thread, delete the "extra" (listening)
client handler. Otherwise there could be a connection that doesn't
get recorded in the client handler list. This could happen when a
file descriptor is being duplicated.
Don't set the write end of the pipe to non-blocking mode if the FIFO
is opened in blocking mode.
In fhandler_fifo::raw_write in blocking mode, wait for the write to
complete rather than returning -1 with EAGAIN.
If the amount to write is large, write in smaller chunks (of size
determined by a new data member max_atomic_write), as in
fhandler_base_overlapped.
For convenience, add two new NTSTATUS codes, STATUS_THREAD_SIGNALED
and STATUS_THREAD_CANCELED, to ntdll.h.
After copyto is called, make the new fhandler's pipe_name point to the
new fhandler's pipe_name_buf, which is a *copy* of the old fhandler's
pipe_name_buf. Previously, get_pipe_name would return the wrong
result after a clone/dup, causing create_pipe_instance and open_pipe
to fail.
Also, stop the listen_client thread when cloning. Otherwise the
thread can keep accepting connections that the cloned fhandler won't
know about.
Do this via a new method fhandler_fifo::stop_listen_client, extracted
from fhandler_fifo::close.
Make read_ready a manual reset event. It should always be set shortly
after startup of the listen_client thread and remain set until the
thread terminates. (We don't want writers to connect without being
recorded in the client handler list.)
Remove the unnecessary code that checks for read_ready when a reader
is opening.
Add data members 'reader', 'writer', and 'duplexer' to the
fhandler_fifo class. Set them in fhandler_fifo::open. ('duplexer'
replaces the previous '_duplexer'.)
This will be useful in later commits.
The word "client" suggests something that holds a handle to the client
side of the pipe (in Windows terminology). But our
fifo_client_handlers hold a handle the server side of the pipe, and
they *connect* to clients.
If a FIFO is opened with O_RDWR access, create the pipe with
read/write access, and make the first client have the handle of that
pipe as its I/O handle.
Adjust fhandler_fifo::raw_read to account for the result of trying to
read from that client if there's no data.