/* fhandler_fifo.cc - See fhandler.h for a description of the fhandler classes. Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. 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 "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" fhandler_fifo::fhandler_fifo (): wait_state (fifo_unknown) { get_overlapped ()->hEvent = NULL; need_fork_fixup (true); } HANDLE fhandler_fifo::open_nonserver (const char *npname, unsigned low_flags, LPSECURITY_ATTRIBUTES sa_buf) { DWORD mode = 0; if (low_flags == O_RDONLY) mode = GENERIC_READ; else if (low_flags == O_WRONLY) mode = GENERIC_WRITE; else mode = GENERIC_READ | GENERIC_WRITE; while (1) { HANDLE h = CreateFile (npname, mode, 0, sa_buf, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (h != INVALID_HANDLE_VALUE || GetLastError () != ERROR_PIPE_NOT_CONNECTED) return h; if (&_my_tls != _main_tls) low_priority_sleep (0); else if (WaitForSingleObject (signal_arrived, 0) == WAIT_OBJECT_0) { set_errno (EINTR); return NULL; } } } #define FIFO_PIPE_MODE (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE) char * fhandler_fifo::fifo_name (char *buf) { /* Generate a semi-unique name to associate with this fifo. */ __small_sprintf (buf, "\\\\.\\pipe\\__cygfifo__%08x_%016X", get_dev (), get_ino ()); return buf; } int fhandler_fifo::open (int flags, mode_t) { int res = 1; char npname[MAX_PATH]; fifo_name (npname); unsigned low_flags = flags & O_ACCMODE; DWORD mode = 0; if (low_flags == O_WRONLY) /* ok */; else if (low_flags == O_RDONLY) mode = PIPE_ACCESS_INBOUND; else if (low_flags == O_RDWR) mode = PIPE_ACCESS_DUPLEX; else { set_errno (EINVAL); res = 0; } if (res) { char char_sa_buf[1024]; LPSECURITY_ATTRIBUTES sa_buf = sec_user ((PSECURITY_ATTRIBUTES) char_sa_buf, cygheap->user.sid()); mode |= FILE_FLAG_OVERLAPPED; HANDLE h; DWORD err; bool nonblocking_write = !!((flags & (O_WRONLY | O_NONBLOCK)) == (O_WRONLY | O_NONBLOCK)); if (flags & O_WRONLY) { h = INVALID_HANDLE_VALUE; err = ERROR_ACCESS_DENIED; } else { h = CreateNamedPipe(npname, mode, FIFO_PIPE_MODE, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, sa_buf); err = GetLastError (); } if (h != INVALID_HANDLE_VALUE) wait_state = fifo_wait_for_client; else switch (err) { case ERROR_ACCESS_DENIED: h = open_nonserver (npname, low_flags, sa_buf); if (h != INVALID_HANDLE_VALUE) { wait_state = fifo_ok; break; } if (GetLastError () != ERROR_FILE_NOT_FOUND) __seterrno (); else if (nonblocking_write) set_errno (ENXIO); else { h = NULL; nohandle (true); wait_state = fifo_wait_for_server; } break; default: __seterrno (); break; } if (h == INVALID_HANDLE_VALUE) res = 0; else if (!setup_overlapped ()) { __seterrno (); res = 0; } else { set_io_handle (h); set_flags (flags); res = 1; } } debug_printf ("returning %d, errno %d", res, get_errno ()); return res; } bool fhandler_fifo::wait (bool iswrite) { switch (wait_state) { case fifo_wait_for_next_client: DisconnectNamedPipe (get_handle ()); case fifo_wait_for_client: { int res; if ((res = ConnectNamedPipe (get_handle (), get_overlapped ())) || GetLastError () == ERROR_PIPE_CONNECTED) { wait_state = fifo_ok; return true; } if (wait_state == fifo_wait_for_next_client) { CancelIo (get_handle ()); res = 0; } else { DWORD dummy_bytes; res = wait_overlapped (res, iswrite, &dummy_bytes); } wait_state = res ? fifo_ok : fifo_eof; } break; case fifo_wait_for_server: if (get_io_handle ()) break; char npname[MAX_PATH]; fifo_name (npname); char char_sa_buf[1024]; LPSECURITY_ATTRIBUTES sa_buf; sa_buf = sec_user ((PSECURITY_ATTRIBUTES) char_sa_buf, cygheap->user.sid()); while (1) { if (WaitNamedPipe (npname, 10)) /* connected, maybe */; else if (GetLastError () != ERROR_SEM_TIMEOUT) { __seterrno (); return false; } else if (WaitForSingleObject (signal_arrived, 0) != WAIT_OBJECT_0) continue; else if (_my_tls.call_signal_handler ()) continue; else { set_errno (EINTR); return false; } HANDLE h = open_nonserver (npname, get_flags () & O_ACCMODE, sa_buf); if (h != INVALID_HANDLE_VALUE) { wait_state = fifo_ok; set_io_handle (h); nohandle (false); break; } if (GetLastError () == ERROR_PIPE_LISTENING) continue; else { __seterrno (); return false; } } default: break; } return true; } void fhandler_fifo::raw_read (void *in_ptr, size_t& len) { while (wait_state != fifo_eof) if (!wait (false)) len = 0; else { read_overlapped (in_ptr, len); if (!len) wait_state = fifo_wait_for_next_client; } } int fhandler_fifo::raw_write (const void *ptr, size_t len) { return wait (true) ? write_overlapped (ptr, len) : -1; } int __stdcall fhandler_fifo::fstatvfs (struct statvfs *sfs) { fhandler_disk_file fh (pc); fh.get_device () = FH_FS; return fh.fstatvfs (sfs); }