summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2003-09-19 03:55:54 +0000
committerChristopher Faylor <cgf@redhat.com>2003-09-19 03:55:54 +0000
commit8baf9504efc1777cfc7642114bcbc927e80aee1d (patch)
tree082b1c75322f0b607742ce6d65a1f2e0c99b80e4 /winsup
parent6fd775d900181e93204b6c58065fcb9be07cd437 (diff)
downloadgdb-8baf9504efc1777cfc7642114bcbc927e80aee1d.tar.gz
* fhandler_tty.cc (fhandler_pty_master::process_slave_output): Handle buf ==
NULL as flushing the buffer. (fhandler_tty_slave::read): Handle ptr == NULL as flushing the buffer. (fhandler_tty_slave::tcflush): Implement input queue flushing by calling read with NULL buffer. (fhandler_pty_master::tcflush): Ditto, calling process_slave_output. * termios.cc (tcflush): Check for legal `queue' value. Return EINVAL otherwise. * syscalls.cc (gethostid): Add lpFreeBytesAvailable argument to GetDiskFreeSpaceEx call since NT4 requires it.
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/ChangeLog20
-rw-r--r--winsup/cygwin/fhandler_tty.cc1361
-rw-r--r--winsup/cygwin/syscalls.cc2985
-rw-r--r--winsup/cygwin/termios.cc307
4 files changed, 4671 insertions, 2 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 490a1883378..021a445453e 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,19 @@
+2003-09-18 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_tty.cc (fhandler_pty_master::process_slave_output): Handle
+ buf == NULL as flushing the buffer.
+ (fhandler_tty_slave::read): Handle ptr == NULL as flushing the buffer.
+ (fhandler_tty_slave::tcflush): Implement input queue flushing by
+ calling read with NULL buffer.
+ (fhandler_pty_master::tcflush): Ditto, calling process_slave_output.
+ * termios.cc (tcflush): Check for legal `queue' value. Return EINVAL
+ otherwise.
+
+2003-09-18 Brian Ford <ford@vss.fsi.com>
+
+ * syscalls.cc (gethostid): Add lpFreeBytesAvailable argument to
+ GetDiskFreeSpaceEx call since NT4 requires it.
+
2003-09-18 Christopher Faylor <cgf@redhat.com>
* fhandler_disk_file.cc (path_conv::ndisk_links): Fix potential
@@ -169,11 +185,11 @@
* security.h: Add _SECURITY_H guard.
(sec_user): Use sec_none in the no ntsec case.
* spawn.cc (spawn_guts): Remove call to load_registry_hive.
- * syscalls (seteuid32): If warranted, call load_registry_hive,
+ * syscalls (seteuid32): If warranted, call load_registry_hive,
user_shared_initialize and RegCloseKey(HKEY_CURRENT_USER).
* shared.cc (user_shared_initialize): New.
(open_shared): Add and use psa argument.
- (memory_init): Move mount table initialization to
+ (memory_init): Move mount table initialization to
user_shared_initialize. Call it.
2003-09-09 Corinna Vinschen <corinna@vinschen.de>
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
new file mode 100644
index 00000000000..1bb3d87a762
--- /dev/null
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -0,0 +1,1361 @@
+/* fhandler_tty.cc
+
+ Copyright 1997, 1998, 2000, 2001, 2002, 2003 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 <wingdi.h>
+#include <winuser.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "cygwin/cygserver.h"
+#include "cygthread.h"
+
+/* Tty master stuff */
+
+fhandler_tty_master NO_COPY *tty_master;
+
+static DWORD WINAPI process_input (void *); // Input queue thread
+static DWORD WINAPI process_output (void *); // Output queue thread
+static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread
+
+fhandler_tty_master::fhandler_tty_master (int unit)
+ : fhandler_pty_master (FH_TTYM, unit), console (NULL)
+{
+}
+
+void
+fhandler_tty_master::set_winsize (bool sendSIGWINCH)
+{
+ winsize w;
+ console->ioctl (TIOCGWINSZ, &w);
+ get_ttyp ()->winsize = w;
+ if (sendSIGWINCH)
+ tc->kill_pgrp (SIGWINCH);
+}
+
+int
+fhandler_tty_master::init (int ntty)
+{
+ termios_printf ("Creating master for tty%d", ntty);
+
+ if (init_console ())
+ {
+ termios_printf ("can't create fhandler");
+ return -1;
+ }
+
+ termios ti;
+ memset (&ti, 0, sizeof (ti));
+ console->tcsetattr (0, &ti);
+
+ ttynum = ntty;
+
+ cygwin_shared->tty[ttynum]->common_init (this);
+
+ set_winsize (false);
+
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+
+ cygthread *h;
+ h = new cygthread (process_input, cygself, "ttyin");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_ioctl, cygself, "ttyioctl");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_output, cygself, "ttyout");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ return 0;
+}
+
+#ifdef DEBUGGING
+static class mutex_stack
+{
+public:
+ const char *fn;
+ int ln;
+ const char *tname;
+} ostack[100];
+
+static int osi;
+#endif /*DEBUGGING*/
+
+DWORD
+fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln,
+ DWORD ms)
+{
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms);
+ DWORD res = WaitForSingleObject (output_mutex, ms);
+ if (res == WAIT_OBJECT_0)
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res);
+#else
+ ostack[osi].fn = fn;
+ ostack[osi].ln = ln;
+ ostack[osi].tname = cygthread::name ();
+ termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi);
+ osi++;
+#endif
+ }
+ return res;
+}
+
+void
+fhandler_tty_common::__release_output_mutex (const char *fn, int ln)
+{
+ if (ReleaseMutex (output_mutex))
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln);
+#else
+ if (osi > 0)
+ osi--;
+ termios_printf ("released at %s:%d, osi %d", fn, ln, osi);
+ termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname);
+ ostack[osi].ln = -ln;
+#endif
+ }
+}
+
+/* Process tty input. */
+
+void
+fhandler_pty_master::doecho (const void *str, DWORD len)
+{
+ acquire_output_mutex (INFINITE);
+ if (!WriteFile (get_ttyp ()->to_master, str, len, &len, NULL))
+ termios_printf ("Write to %p failed, %E", get_ttyp ()->to_master);
+// WaitForSingleObject (output_done_event, INFINITE);
+ release_output_mutex ();
+}
+
+int
+fhandler_pty_master::accept_input ()
+{
+ DWORD bytes_left;
+ int ret = 1;
+
+ (void) WaitForSingleObject (input_mutex, INFINITE);
+
+ bytes_left = eat_readahead (-1);
+
+ if (!bytes_left)
+ {
+ termios_printf ("sending EOF to slave");
+ get_ttyp ()->read_retval = 0;
+ }
+ else
+ {
+ char *p = rabuf;
+ DWORD rc;
+ DWORD written = 0;
+
+ termios_printf ("about to write %d chars to slave", bytes_left);
+ rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL);
+ if (!rc)
+ {
+ debug_printf ("error writing to pipe %E");
+ get_ttyp ()->read_retval = -1;
+ ret = -1;
+ }
+ else
+ {
+ get_ttyp ()->read_retval = 1;
+ p += written;
+ bytes_left -= written;
+ if (bytes_left > 0)
+ {
+ debug_printf ("to_slave pipe is full");
+ puts_readahead (p, bytes_left);
+ ret = 0;
+ }
+ }
+ }
+
+ SetEvent (input_available_event);
+ ReleaseMutex (input_mutex);
+ return ret;
+}
+
+static DWORD WINAPI
+process_input (void *)
+{
+ char rawbuf[INP_BUFFER_SIZE];
+
+ while (1)
+ {
+ size_t nraw = INP_BUFFER_SIZE;
+ tty_master->console->read ((void *) rawbuf, nraw);
+ if (tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti)
+ == line_edit_signalled)
+ tty_master->console->eat_readahead (-1);
+ }
+}
+
+bool
+fhandler_pty_master::hit_eof ()
+{
+ if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ())
+ {
+ /* We have the only remaining open handle to this pty, and
+ the slave pty has been opened at least once. We treat
+ this as EOF. */
+ termios_printf ("all other handles closed");
+ return 1;
+ }
+ return 0;
+}
+
+/* Process tty output requests */
+
+int
+fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on)
+{
+ size_t rlen;
+ char outbuf[OUT_BUFFER_SIZE + 1];
+ DWORD n;
+ int column = 0;
+ int rc = 0;
+
+ if (len == 0)
+ goto out;
+
+ if (need_nl)
+ {
+ /* We need to return a left over \n character, resulting from
+ \r\n conversion. Note that we already checked for FLUSHO and
+ output_stopped at the time that we read the character, so we
+ don't check again here. */
+ if (buf)
+ buf[0] = '\n';
+ need_nl = 0;
+ rc = 1;
+ goto out;
+ }
+
+
+ for (;;)
+ {
+ /* Set RLEN to the number of bytes to read from the pipe. */
+ rlen = len;
+ if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR)
+ {
+ /* We are going to expand \n to \r\n, so don't read more than
+ half of the number of bytes requested. */
+ rlen /= 2;
+ if (rlen == 0)
+ rlen = 1;
+ }
+ if (rlen > sizeof outbuf)
+ rlen = sizeof outbuf;
+
+ HANDLE handle = get_io_handle ();
+
+ n = 0; // get_readahead_into_buffer (outbuf, len);
+ if (!n)
+ {
+ /* Doing a busy wait like this is quite inefficient, but nothing
+ else seems to work completely. Windows should provide some sort
+ of overlapped I/O for pipes, or something, but it doesn't. */
+ while (1)
+ {
+ if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL))
+ goto err;
+ if (n > 0)
+ break;
+ if (hit_eof ())
+ goto out;
+ /* DISCARD (FLUSHO) and tcflush can finish here. */
+ if (n == 0 && (get_ttyp ()->ti.c_lflag & FLUSHO || !buf))
+ goto out;
+ if (n == 0 && is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ rc = -1;
+ break;
+ }
+
+ Sleep (10);
+ }
+
+ if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE)
+ goto err;
+ }
+
+ termios_printf ("bytes read %u", n);
+ get_ttyp ()->write_error = 0;
+ if (output_done_event != NULL)
+ SetEvent (output_done_event);
+
+ if (get_ttyp ()->ti.c_lflag & FLUSHO || !buf)
+ continue;
+
+ char *optr;
+ optr = buf;
+ if (pktmode_on)
+ *optr++ = TIOCPKT_DATA;
+
+ if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output
+ {
+ memcpy (optr, outbuf, n);
+ optr += n;
+ }
+ else // raw output mode
+ {
+ char *iptr = outbuf;
+
+ while (n--)
+ {
+ switch (*iptr)
+ {
+ case '\r':
+ if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0)
+ {
+ iptr++;
+ continue;
+ }
+ if (get_ttyp ()->ti.c_oflag & OCRNL)
+ *iptr = '\n';
+ else
+ column = 0;
+ break;
+ case '\n':
+ if (get_ttyp ()->ti.c_oflag & ONLCR)
+ {
+ *optr++ = '\r';
+ column = 0;
+ }
+ if (get_ttyp ()->ti.c_oflag & ONLRET)
+ column = 0;
+ break;
+ default:
+ column++;
+ break;
+ }
+
+ /* Don't store data past the end of the user's buffer. This
+ can happen if the user requests a read of 1 byte when
+ doing \r\n expansion. */
+ if (optr - buf >= (int) len)
+ {
+ if (*iptr != '\n' || n != 0)
+ system_printf ("internal error: %d unexpected characters", n);
+ need_nl = 1;
+ break;
+ }
+
+ *optr++ = *iptr++;
+ }
+ }
+ rc = optr - buf;
+ break;
+
+ err:
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ rc = 0;
+ else
+ {
+ __seterrno ();
+ rc = -1;
+ }
+ break;
+ }
+
+out:
+ termios_printf ("returning %d", rc);
+ return rc;
+}
+
+static DWORD WINAPI
+process_output (void *)
+{
+ char buf[OUT_BUFFER_SIZE * 2];
+
+ for (;;)
+ {
+ int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0);
+ if (n <= 0)
+ {
+ if (n < 0)
+ termios_printf ("ReadFile %E");
+ ExitThread (0);
+ }
+ n = tty_master->console->write ((void *) buf, (size_t) n);
+ tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0;
+ }
+}
+
+
+/* Process tty ioctl requests */
+
+static DWORD WINAPI
+process_ioctl (void *)
+{
+ while (1)
+ {
+ WaitForSingleObject (tty_master->ioctl_request_event, INFINITE);
+ termios_printf ("ioctl() request");
+ tty_master->get_ttyp ()->ioctl_retval =
+ tty_master->console->ioctl (tty_master->get_ttyp ()->cmd,
+ (void *) &tty_master->get_ttyp ()->arg);
+ SetEvent (tty_master->ioctl_done_event);
+ }
+}
+
+/**********************************************************************/
+/* Tty slave stuff */
+
+fhandler_tty_slave::fhandler_tty_slave (int num)
+ : fhandler_tty_common (FH_TTYS, num)
+{
+ set_r_no_interrupt (1);
+}
+
+fhandler_tty_slave::fhandler_tty_slave ()
+ : fhandler_tty_common (FH_TTYS, 0)
+{
+ set_r_no_interrupt (1);
+}
+
+/* FIXME: This function needs to close handles when it has
+ a failing condition. */
+int
+fhandler_tty_slave::open (path_conv *, int flags, mode_t)
+{
+ tcinit (cygwin_shared->tty[ttynum]);
+
+ attach_tty (ttynum);
+ tc->set_ctty (ttynum, flags);
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ /* Create synchronisation events */
+ char buf[40];
+
+ /* output_done_event may or may not exist. It will exist if the tty
+ was opened by fhandler_tty_master::init, normally called at
+ startup if use_tty is non-zero. It will not exist if this is a
+ pty opened by fhandler_pty_master::open. In the former case, tty
+ output is handled by a separate thread which controls output. */
+ __small_sprintf (buf, OUTPUT_DONE_EVENT, ttynum);
+ output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ if (!(output_mutex = get_ttyp ()->open_output_mutex ()))
+ {
+ termios_printf ("open output mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ if (!(input_mutex = get_ttyp ()->open_input_mutex ()))
+ {
+ termios_printf ("open input mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ __small_sprintf (buf, INPUT_AVAILABLE_EVENT, ttynum);
+ if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf)))
+ {
+ termios_printf ("open input event failed, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ /* The ioctl events may or may not exist. See output_done_event,
+ above. */
+ __small_sprintf (buf, IOCTL_REQUEST_EVENT, ttynum);
+ ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+ __small_sprintf (buf, IOCTL_DONE_EVENT, ttynum);
+ ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ /* FIXME: Needs a method to eliminate tty races */
+ {
+ acquire_output_mutex (500);
+ inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE);
+ get_ttyp ()->was_opened = TRUE;
+ release_output_mutex ();
+ }
+
+ /* Duplicate tty handles. */
+
+ if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave)
+ {
+ termios_printf ("tty handles have been closed");
+ set_errno (EACCES);
+ return 0;
+ }
+
+ HANDLE from_master_local, to_master_local;
+
+#ifdef USE_SERVER
+ if (!wincap.has_security ()
+ || cygserver_running == CYGSERVER_UNAVAIL
+ || !cygserver_attach_tty (&from_master_local, &to_master_local))
+#endif
+ {
+ termios_printf ("cannot dup handles via server. using old method.");
+
+ HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+ get_ttyp ()->master_pid);
+ termios_printf ("tty own handle %p",tty_owner);
+ if (tty_owner == NULL)
+ {
+ termios_printf ("can't open tty (%d) handle process %d",
+ ttynum, get_ttyp ()->master_pid);
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
+ hMainProc, &from_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate input, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
+ hMainProc, &to_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate output, %E");
+ __seterrno ();
+ return 0;
+ }
+ CloseHandle (tty_owner);
+ }
+
+ termios_printf ("duplicated from_master %p->%p from tty_owner",
+ get_ttyp ()->from_master, from_master_local);
+ termios_printf ("duplicated to_master %p->%p from tty_owner",
+ get_ttyp ()->to_master, to_master_local);
+
+ set_io_handle (from_master_local);
+ set_output_handle (to_master_local);
+
+ set_open_status ();
+ if (fhandler_console::open_fhs++ == 0 && !GetConsoleCP ()
+ && !output_done_event && wincap.pty_needs_alloc_console ())
+ {
+ BOOL b;
+ HWINSTA h = CreateWindowStation (NULL, 0, GENERIC_READ | GENERIC_WRITE, &sec_none_nih);
+ termios_printf ("CreateWindowStation %p, %E", h);
+ if (h)
+ {
+ b = SetProcessWindowStation (h);
+ termios_printf ("SetProcessWindowStation %d, %E", b);
+ }
+ b = AllocConsole (); // will cause flashing if workstation
+ // stuff fails
+ termios_printf ("%d = AllocConsole (), %E", b);
+ if (b)
+ init_console_handler ();
+ }
+ termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs);
+ termios_printf ("tty%d opened", ttynum);
+
+ return 1;
+}
+
+int
+fhandler_tty_slave::close ()
+{
+ if (!--fhandler_console::open_fhs && myself->ctty == -1)
+ FreeConsole ();
+ termios_printf ("decremented open_fhs %d", fhandler_console::open_fhs);
+ return fhandler_tty_common::close ();
+}
+
+int
+fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
+ LPHANDLE to_master_ptr)
+{
+#ifndef USE_SERVER
+ return 0;
+#else
+ if (!from_master_ptr || !to_master_ptr)
+ return 0;
+
+ client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid,
+ (HANDLE) get_ttyp ()->from_master,
+ (HANDLE) get_ttyp ()->to_master);
+
+ if (req.make_request () == -1 || req.error_code ())
+ return 0;
+
+ *from_master_ptr = req.from_master ();
+ *to_master_ptr = req.to_master ();
+
+ return 1;
+#endif
+}
+
+void
+fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
+{
+ int mode = 0;
+
+ a &= GENERIC_READ | GENERIC_WRITE;
+ if (a == GENERIC_READ)
+ mode = O_RDONLY;
+ if (a == GENERIC_WRITE)
+ mode = O_WRONLY;
+ if (a == (GENERIC_READ | GENERIC_WRITE))
+ mode = O_RDWR;
+
+ open (0, mode);
+}
+
+int
+fhandler_tty_slave::write (const void *ptr, size_t len)
+{
+ DWORD n, towrite = len;
+
+ termios_printf ("tty%d, write(%x, %d)", ttynum, ptr, len);
+
+ acquire_output_mutex (INFINITE);
+
+ while (len)
+ {
+ n = min (OUT_BUFFER_SIZE, len);
+ char *buf = (char *)ptr;
+ ptr = (char *) ptr + n;
+ len -= n;
+
+ /* Previous write may have set write_error to != 0. Check it here.
+ This is less than optimal, but the alternative slows down tty
+ writes enormously. */
+ if (get_ttyp ()->write_error)
+ {
+ set_errno (get_ttyp ()->write_error);
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE)
+ {
+ DWORD err = GetLastError ();
+ termios_printf ("WriteFile failed, %E");
+ switch (err)
+ {
+ case ERROR_NO_DATA:
+ err = ERROR_IO_DEVICE;
+ default:
+ __seterrno_from_win_error (err);
+ }
+ raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (output_done_event != NULL)
+ {
+ DWORD rc;
+ DWORD x = n * 1000;
+ rc = WaitForSingleObject (output_done_event, x);
+ termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc);
+ }
+ }
+ release_output_mutex ();
+ return towrite;
+}
+
+void __stdcall
+fhandler_tty_slave::read (void *ptr, size_t& len)
+{
+ int totalread = 0;
+ int vmin = 0;
+ int vtime = 0; /* Initialized to prevent -Wuninitialized warning */
+ size_t readlen;
+ DWORD bytes_in_pipe;
+ char buf[INP_BUFFER_SIZE];
+ char peek_buf[INP_BUFFER_SIZE];
+ DWORD time_to_wait;
+ DWORD rc;
+ HANDLE w4[2];
+
+ termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ());
+
+ if (!ptr) /* Indicating tcflush(). */
+ time_to_wait = 0;
+ else if ((get_ttyp ()->ti.c_lflag & ICANON))
+ time_to_wait = INFINITE;
+ else
+ {
+ vmin = get_ttyp ()->ti.c_cc[VMIN];
+ if (vmin > INP_BUFFER_SIZE)
+ vmin = INP_BUFFER_SIZE;
+ vtime = get_ttyp ()->ti.c_cc[VTIME];
+ if (vmin < 0)
+ vmin = 0;
+ if (vtime < 0)
+ vtime = 0;
+ if (!vmin && !vtime)
+ time_to_wait = 0;
+ else
+ time_to_wait = !vtime ? INFINITE : 100 * vtime;
+ }
+
+ w4[0] = signal_arrived;
+ w4[1] = input_available_event;
+
+ DWORD waiter = !ptr ? 0 : INFINITE;
+ while (len)
+ {
+ rc = WaitForMultipleObjects (2, w4, FALSE, waiter);
+
+ if (rc == WAIT_TIMEOUT)
+ break;
+
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input event failed, %E");
+ break;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ {
+ /* if we've received signal after successfully reading some data,
+ just return all data successfully read */
+ if (totalread > 0)
+ break;
+ set_sig_errno (EINTR);
+ len = (size_t) -1;
+ return;
+ }
+
+ rc = WaitForSingleObject (input_mutex, 1000);
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input mutex failed, %E");
+ break;
+ }
+ else if (rc == WAIT_TIMEOUT)
+ {
+ termios_printf ("failed to acquire input mutex after input event arrived");
+ break;
+ }
+ if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+
+ /* On first peek determine no. of bytes to flush. */
+ if (!ptr && len == UINT_MAX)
+ len = (size_t) bytes_in_pipe;
+
+ if (ptr && !vmin && !time_to_wait)
+ {
+ ReleaseMutex (input_mutex);
+ len = (size_t) bytes_in_pipe;
+ return;
+ }
+
+ readlen = min (bytes_in_pipe, min (len, sizeof (buf)));
+
+ if (ptr && vmin && readlen > (unsigned) vmin)
+ readlen = vmin;
+
+ DWORD n = 0;
+ if (readlen)
+ {
+ termios_printf ("reading %d bytes (vtime %d)", readlen, vtime);
+ if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE)
+ {
+ termios_printf ("read failed, %E");
+ raise (SIGHUP);
+ }
+ /* MSDN states that 5th prameter can be used to determine total
+ number of bytes in pipe, but for some reason this number doesn't
+ change after successful read. So we have to peek into the pipe
+ again to see if input is still available */
+ if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+ if (n)
+ {
+ len -= n;
+ totalread += n;
+ if (ptr)
+ {
+ memcpy (ptr, buf, n);
+ ptr = (char *) ptr + n;
+ }
+ }
+ }
+
+ if (!bytes_in_pipe)
+ ResetEvent (input_available_event);
+
+ ReleaseMutex (input_mutex);
+
+ if (!ptr)
+ {
+ if (!bytes_in_pipe)
+ break;
+ continue;
+ }
+
+ if (get_ttyp ()->read_retval < 0) // read error
+ {
+ set_errno (-get_ttyp ()->read_retval);
+ totalread = -1;
+ break;
+ }
+ if (get_ttyp ()->read_retval == 0) //EOF
+ {
+ termios_printf ("saw EOF");
+ break;
+ }
+ if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ())
+ break;
+ if (vmin && totalread >= vmin)
+ break;
+
+ /* vmin == 0 && vtime == 0:
+ * we've already read all input, if any, so return immediately
+ * vmin == 0 && vtime > 0:
+ * we've waited for input 10*vtime ms in WFSO(input_available_event),
+ * no matter whether any input arrived, we shouldn't wait any longer,
+ * so return immediately
+ * vmin > 0 && vtime == 0:
+ * here, totalread < vmin, so continue waiting until more data
+ * arrive
+ * vmin > 0 && vtime > 0:
+ * similar to the previous here, totalread < vmin, and timer
+ * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT,
+ * so "restart timer" and wait until more data arrive
+ */
+
+ if (vmin == 0)
+ break;
+
+ if (n)
+ waiter = time_to_wait;
+ }
+ termios_printf ("%d=read(%x, %d)", totalread, ptr, len);
+ len = (size_t) totalread;
+ return;
+}
+
+int
+fhandler_tty_slave::dup (fhandler_base *child)
+{
+ fhandler_console::open_fhs++;
+ termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs);
+ return fhandler_tty_common::dup (child);
+}
+
+int
+fhandler_tty_common::dup (fhandler_base *child)
+{
+ fhandler_tty_slave *fts = (fhandler_tty_slave *) child;
+ int errind;
+
+ fts->ttynum = ttynum;
+ fts->tcinit (get_ttyp ());
+
+ attach_tty (ttynum);
+ tc->set_ctty (ttynum, openflags);
+
+ HANDLE nh;
+
+ if (output_done_event == NULL)
+ fts->output_done_event = NULL;
+ else if (!DuplicateHandle (hMainProc, output_done_event, hMainProc,
+ &fts->output_done_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 1;
+ goto err;
+ }
+ if (ioctl_request_event == NULL)
+ fts->ioctl_request_event = NULL;
+ else if (!DuplicateHandle (hMainProc, ioctl_request_event, hMainProc,
+ &fts->ioctl_request_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 2;
+ goto err;
+ }
+ if (ioctl_done_event == NULL)
+ fts->ioctl_done_event = NULL;
+ else if (!DuplicateHandle (hMainProc, ioctl_done_event, hMainProc,
+ &fts->ioctl_done_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 3;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, input_available_event, hMainProc,
+ &fts->input_available_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 4;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, output_mutex, hMainProc,
+ &fts->output_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 5;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, input_mutex, hMainProc,
+ &fts->input_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 6;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, get_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 7;
+ goto err;
+ }
+ fts->set_io_handle (nh);
+
+ if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 8;
+ goto err;
+ }
+ fts->set_output_handle (nh);
+
+ if (inuse == NULL)
+ fts->inuse = NULL;
+ else if (!DuplicateHandle (hMainProc, inuse, hMainProc,
+ &fts->inuse, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 9;
+ goto err;
+ }
+ return 0;
+
+err:
+ __seterrno ();
+ termios_printf ("dup %d failed in DuplicateHandle, %E", errind);
+ return -1;
+}
+
+int
+fhandler_tty_slave::tcgetattr (struct termios *t)
+{
+ *t = get_ttyp ()->ti;
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcsetattr (int, const struct termios *t)
+{
+ acquire_output_mutex (INFINITE);
+ get_ttyp ()->ti = *t;
+ release_output_mutex ();
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ size_t len = UINT_MAX;
+ read (NULL, len);
+ ret = len >= 0;
+ }
+ if (queue == TCOFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+int
+fhandler_tty_slave::ioctl (unsigned int cmd, void *arg)
+{
+ termios_printf ("ioctl (%x)", cmd);
+
+ if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid
+ && myself->ctty == ttynum && (get_ttyp ()->ti.c_lflag & TOSTOP))
+ {
+ /* background process */
+ termios_printf ("bg ioctl pgid %d, tpgid %d, ctty %d",
+ myself->pgid, get_ttyp ()->getpgid (), myself->ctty);
+ raise (SIGTTOU);
+ }
+
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ goto out;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ acquire_output_mutex (INFINITE);
+
+ get_ttyp ()->cmd = cmd;
+ get_ttyp ()->ioctl_retval = 0;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ get_ttyp ()->arg.winsize = get_ttyp ()->winsize;
+ if (ioctl_request_event)
+ SetEvent (ioctl_request_event);
+ *(struct winsize *) arg = get_ttyp ()->arg.winsize;
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->arg.winsize = *(struct winsize *) arg;
+ if (ioctl_request_event)
+ {
+ get_ttyp ()->ioctl_retval = -1;
+ SetEvent (ioctl_request_event);
+ }
+ else
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ }
+ break;
+ }
+
+ release_output_mutex ();
+
+out:
+ termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd);
+ return get_ttyp ()->ioctl_retval;
+}
+
+/*******************************************************
+ fhandler_pty_master
+*/
+fhandler_pty_master::fhandler_pty_master (DWORD devtype, int unit)
+ : fhandler_tty_common (devtype, unit)
+{
+}
+
+int
+fhandler_pty_master::open (path_conv *, int flags, mode_t)
+{
+ ttynum = cygwin_shared->tty.allocate_tty (0);
+ if (ttynum < 0)
+ return 0;
+
+ cygwin_shared->tty[ttynum]->common_init (this);
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+
+ termios_printf ("opened pty master tty%d<%p>", ttynum, this);
+ return 1;
+}
+
+int
+fhandler_tty_common::close ()
+{
+ if (output_done_event && !CloseHandle (output_done_event))
+ termios_printf ("CloseHandle (output_done_event), %E");
+ if (ioctl_done_event && !CloseHandle (ioctl_done_event))
+ termios_printf ("CloseHandle (ioctl_done_event), %E");
+ if (ioctl_request_event && !CloseHandle (ioctl_request_event))
+ termios_printf ("CloseHandle (ioctl_request_event), %E");
+ if (inuse && !CloseHandle (inuse))
+ termios_printf ("CloseHandle (inuse), %E");
+ if (!ForceCloseHandle (input_mutex))
+ termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex);
+ if (!ForceCloseHandle (output_mutex))
+ termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+
+ /* Send EOF to slaves if master side is closed */
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("no more masters left. sending EOF" );
+ SetEvent (input_available_event);
+ }
+
+ if (!ForceCloseHandle (input_available_event))
+ termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event);
+ if (!ForceCloseHandle1 (get_handle (), from_pty))
+ termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ());
+ if (!ForceCloseHandle1 (get_output_handle (), to_pty))
+ termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ());
+
+ inuse = NULL;
+ termios_printf ("tty%d <%p,%p> closed", ttynum, get_handle (), get_output_handle ());
+ return 0;
+}
+
+int
+fhandler_pty_master::close ()
+{
+#if 0
+ while (accept_input () > 0)
+ continue;
+#endif
+ fhandler_tty_common::close ();
+
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("freeing tty%d (%d)", ttynum, get_ttyp ()->ntty);
+#if 0
+ if (get_ttyp ()->to_slave)
+ ForceCloseHandle1 (get_ttyp ()->to_slave, to_slave);
+ if (get_ttyp ()->from_slave)
+ ForceCloseHandle1 (get_ttyp ()->from_slave, from_slave);
+#endif
+ if (get_ttyp ()->from_master)
+ CloseHandle (get_ttyp ()->from_master);
+ if (get_ttyp ()->to_master)
+ CloseHandle (get_ttyp ()->to_master);
+ get_ttyp ()->init ();
+ }
+
+ return 0;
+}
+
+int
+fhandler_pty_master::write (const void *ptr, size_t len)
+{
+ int i;
+ char *p = (char *) ptr;
+ termios ti = tc->ti;
+
+ for (i = 0; i < (int) len; i++)
+ {
+ line_edit_status status = line_edit (p++, 1, ti);
+ if (status > line_edit_signalled)
+ {
+ if (status != line_edit_pipe_full)
+ i = -1;
+ break;
+ }
+ }
+ return i;
+}
+
+void __stdcall
+fhandler_pty_master::read (void *ptr, size_t& len)
+{
+ len = (size_t) process_slave_output ((char *) ptr, len, pktmode);
+ return;
+}
+
+int
+fhandler_pty_master::tcgetattr (struct termios *t)
+{
+ *t = cygwin_shared->tty[ttynum]->ti;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcsetattr (int, const struct termios *t)
+{
+ cygwin_shared->tty[ttynum]->ti = *t;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ ret = process_slave_output (NULL, OUT_BUFFER_SIZE, 0);
+ else if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+int
+fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
+{
+ switch (cmd)
+ {
+ case TIOCPKT:
+ pktmode = * (int *) arg;
+ break;
+ case TIOCGWINSZ:
+ *(struct winsize *) arg = get_ttyp ()->winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->winsize = * (struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ return 0;
+}
+
+char *
+fhandler_pty_master::ptsname (void)
+{
+ static char buf[32];
+
+ __small_sprintf (buf, "/dev/tty%d", ttynum);
+ return buf;
+}
+
+void
+fhandler_tty_common::set_close_on_exec (int val)
+{
+#ifndef DEBUGGING
+ fhandler_base::set_close_on_exec (val);
+#else
+ /* FIXME: This is a duplication from fhandler_base::set_close_on_exec.
+ It is here because we need to specify the "from_pty" stuff here or
+ we'll get warnings from ForceCloseHandle when debugging. */
+ set_inheritance (get_io_handle (), val);
+ set_close_on_exec_flag (val);
+#endif
+ if (output_done_event)
+ set_inheritance (output_done_event, val);
+ if (ioctl_request_event)
+ set_inheritance (ioctl_request_event, val);
+ if (ioctl_done_event)
+ set_inheritance (ioctl_done_event, val);
+ if (inuse)
+ set_inheritance (inuse, val);
+ set_inheritance (output_mutex, val);
+ set_inheritance (input_mutex, val);
+ set_inheritance (input_available_event, val);
+ set_inheritance (output_handle, val);
+}
+
+void
+fhandler_tty_slave::fixup_after_fork (HANDLE parent)
+{
+ fhandler_console::open_fhs++;
+ termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs);
+ fhandler_tty_common::fixup_after_fork (parent);
+}
+
+void
+fhandler_tty_common::fixup_after_fork (HANDLE parent)
+{
+ fhandler_termios::fixup_after_fork (parent);
+ if (output_done_event)
+ fork_fixup (parent, output_done_event, "output_done_event");
+ if (ioctl_request_event)
+ fork_fixup (parent, ioctl_request_event, "ioctl_request_event");
+ if (ioctl_done_event)
+ fork_fixup (parent, ioctl_done_event, "ioctl_done_event");
+ if (output_mutex)
+ fork_fixup (parent, output_mutex, "output_mutex");
+ if (input_mutex)
+ fork_fixup (parent, input_mutex, "input_mutex");
+ if (input_available_event)
+ fork_fixup (parent, input_available_event, "input_available_event");
+ fork_fixup (parent, inuse, "inuse");
+}
+
+void
+fhandler_pty_master::set_close_on_exec (int val)
+{
+ fhandler_tty_common::set_close_on_exec (val);
+
+ /* FIXME: There is a console handle leak here. */
+ if (get_ttyp ()->master_pid == GetCurrentProcessId ())
+ {
+ get_ttyp ()->from_slave = get_handle ();
+ get_ttyp ()->to_slave = get_output_handle ();
+ termios_printf ("from_slave %p, to_slave %p", get_handle (),
+ get_output_handle ());
+ }
+}
+
+void
+fhandler_tty_master::fixup_after_fork (HANDLE child)
+{
+ fhandler_pty_master::fixup_after_fork (child);
+ console->fixup_after_fork (child);
+}
+
+void
+fhandler_tty_master::fixup_after_exec (HANDLE)
+{
+ console->close ();
+ init_console ();
+}
+
+int
+fhandler_tty_master::init_console ()
+{
+ console = (fhandler_console *) cygheap->fdtab.build_fhandler (-1, FH_CONSOLE, "/dev/ttym");
+ if (console == NULL)
+ return -1;
+
+ console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY);
+ fhandler_console::open_fhs--; /* handled when individual fds are opened */
+ console->set_r_no_interrupt (1);
+ return 0;
+}
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
new file mode 100644
index 00000000000..702c7a5e2b9
--- /dev/null
+++ b/winsup/cygwin/syscalls.cc
@@ -0,0 +1,2985 @@
+/* syscalls.cc: syscalls
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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. */
+
+#define fstat __FOOfstat__
+#define lstat __FOOlstat__
+#define stat __FOOstat__
+#define _close __FOO_close__
+#define _lseek __FOO_lseek__
+#define _open __FOO_open__
+#define _read __FOO_read__
+#define _write __FOO_write__
+#define _open64 __FOO_open64__
+#define _lseek64 __FOO_lseek64__
+#define _fstat64 __FOO_fstat64__
+
+#include "winsup.h"
+#include <sys/stat.h>
+#include <sys/vfs.h> /* needed for statfs */
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h>
+#include <utmp.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <winnls.h>
+#include <wininet.h>
+#include <lmcons.h> /* for UNLEN */
+#include <rpc.h>
+
+#undef fstat
+#undef lstat
+#undef stat
+
+#include <cygwin/version.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "pwdgrp.h"
+#include "cpuid.h"
+#include "registry.h"
+
+#undef _close
+#undef _lseek
+#undef _open
+#undef _read
+#undef _write
+#undef _open64
+#undef _lseek64
+#undef _fstat64
+
+SYSTEM_INFO system_info;
+
+/* Close all files and process any queued deletions.
+ Lots of unix style applications will open a tmp file, unlink it,
+ but never call close. This function is called by _exit to
+ ensure we don't leave any such files lying around. */
+
+void __stdcall
+close_all_files (void)
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files");
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL)
+ {
+ fh->close ();
+ cygheap->fdtab.release (i);
+ }
+
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files");
+ cygwin_shared->delqueue.process_queue ();
+}
+
+int
+dup (int fd)
+{
+ return cygheap->fdtab.dup2 (fd, cygheap_fdnew ());
+}
+
+int
+dup2 (int oldfd, int newfd)
+{
+ return cygheap->fdtab.dup2 (oldfd, newfd);
+}
+
+extern "C" int
+unlink (const char *ourname)
+{
+ int res = -1;
+ DWORD devn;
+ sigframe thisframe (mainthread);
+
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ goto done;
+ }
+
+ if ((devn = win32_name.get_devn ()) == FH_PROC || devn == FH_REGISTRY
+ || devn == FH_PROCESS)
+ {
+ set_errno (EROFS);
+ goto done;
+ }
+
+ syscall_printf ("_unlink (%s)", win32_name.get_win32 ());
+
+ if (!win32_name.exists ())
+ {
+ syscall_printf ("unlinking a nonexistent file");
+ set_errno (ENOENT);
+ goto done;
+ }
+ else if (win32_name.isdir ())
+ {
+ syscall_printf ("unlinking a directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ /* Windows won't check the directory mode, so we do that ourselves. */
+ if (!writable_directory (win32_name))
+ {
+ syscall_printf ("non-writable directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ bool setattrs;
+ if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
+ setattrs = false;
+ else
+ {
+ /* Allow us to delete even if read-only */
+ setattrs = SetFileAttributes (win32_name,
+ (DWORD) win32_name
+ & ~(FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_SYSTEM));
+ }
+ /* Attempt to use "delete on close" semantics to handle removing
+ a file which may be open. */
+ HANDLE h;
+ h = CreateFile (win32_name, 0, FILE_SHARE_READ, &sec_none_nih,
+ OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (wincap.has_hard_links () && setattrs)
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+ BOOL res = CloseHandle (h);
+ syscall_printf ("%d = CloseHandle (%p)", res, h);
+ if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES
+ || !win32_name.isremote ())
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) succeeded");
+ goto ok;
+ }
+ else
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) failed");
+ if (setattrs)
+ SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM));
+ }
+ }
+
+ /* Try a delete with attributes reset */
+ if (DeleteFile (win32_name))
+ {
+ syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded");
+ goto ok;
+ }
+
+ DWORD lasterr;
+ lasterr = GetLastError ();
+
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+
+ /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
+ violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
+ to simplify tests. */
+ if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
+ && !win32_name.isremote ())
+ lasterr = ERROR_SHARING_VIOLATION;
+
+ /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
+ violation, then queue the file for deletion when the process
+ exits. Otherwise, punt. */
+ if (lasterr != ERROR_SHARING_VIOLATION)
+ goto err;
+
+ syscall_printf ("couldn't delete file, err %d", lasterr);
+
+ /* Add file to the "to be deleted" queue. */
+ cygwin_shared->delqueue.queue_file (win32_name);
+
+ /* Success condition. */
+ ok:
+ res = 0;
+ goto done;
+
+ /* Error condition. */
+ err:
+ __seterrno ();
+ res = -1;
+
+ done:
+ syscall_printf ("%d = unlink (%s)", res, ourname);
+ return res;
+}
+
+extern "C" int
+_remove_r (struct _reent *, const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" int
+remove (const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" pid_t
+getpid ()
+{
+ return myself->pid;
+}
+
+extern "C" pid_t
+_getpid_r (struct _reent *)
+{
+ return getpid ();
+}
+
+/* getppid: POSIX 4.1.1.1 */
+extern "C" pid_t
+getppid ()
+{
+ return myself->ppid;
+}
+
+/* setsid: POSIX 4.3.2.1 */
+extern "C" pid_t
+setsid (void)
+{
+ vfork_save *vf = vfork_storage.val ();
+ /* This is a horrible, horrible kludge */
+ if (vf && vf->pid < 0)
+ {
+ pid_t pid = fork ();
+ if (pid > 0)
+ {
+ syscall_printf ("longjmping due to vfork");
+ vf->restore_pid (pid);
+ }
+ /* assuming that fork was successful */
+ }
+
+ if (myself->pgid != myself->pid)
+ {
+ if (myself->ctty >= 0 && fhandler_console::open_fhs <= 0)
+ {
+ syscall_printf ("freeing console");
+ FreeConsole ();
+ }
+ myself->ctty = -1;
+ myself->sid = getpid ();
+ myself->pgid = getpid ();
+ syscall_printf ("sid %d, pgid %d, ctty %d, open_fhs %d", myself->sid,
+ myself->pgid, myself->ctty, fhandler_console::open_fhs);
+ return myself->sid;
+ }
+
+ set_errno (EPERM);
+ return -1;
+}
+
+extern "C" pid_t
+getsid (pid_t pid)
+{
+ pid_t res;
+ if (!pid)
+ res = myself->sid;
+ else
+ {
+ pinfo p (pid);
+ if (p)
+ res = p->sid;
+ else
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ }
+ return res;
+}
+
+extern "C" ssize_t
+read (int fd, void *ptr, size_t len)
+{
+ const iovec iov =
+ {
+ iov_base: ptr,
+ iov_len: len
+ };
+
+ return readv (fd, &iov, 1);
+}
+
+extern "C" ssize_t _read (int, void *, size_t)
+ __attribute__ ((alias ("read")));
+
+extern "C" ssize_t
+write (int fd, const void *ptr, size_t len)
+{
+ const struct iovec iov =
+ {
+ iov_base: (void *) ptr, // const_cast
+ iov_len: len
+ };
+
+ return writev (fd, &iov, 1);
+}
+
+extern "C" ssize_t _write (int fd, const void *ptr, size_t len)
+ __attribute__ ((alias ("write")));
+
+extern "C" ssize_t
+readv (int fd, const struct iovec *const iov, const int iovcnt)
+{
+ extern int sigcatchers;
+ const int e = get_errno ();
+
+ int res = -1;
+
+ const ssize_t tot = check_iovec_for_read (iov, iovcnt);
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ while (1)
+ {
+ sig_dispatch_pending ();
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ break;
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY)
+ {
+ set_errno (EBADF);
+ break;
+ }
+
+ DWORD wait = cfd->is_nonblocking () ? 0 : INFINITE;
+
+ /* Could block, so let user know we at least got here. */
+ syscall_printf ("readv (%d, %p, %d) %sblocking, sigcatchers %d",
+ fd, iov, iovcnt, wait ? "" : "non", sigcatchers);
+
+ if (wait && (!cfd->is_slow () || cfd->get_r_no_interrupt ()))
+ debug_printf ("no need to call ready_for_read");
+ else if (!cfd->ready_for_read (fd, wait))
+ {
+ res = -1;
+ goto out;
+ }
+
+ /* FIXME: This is not thread safe. We need some method to
+ ensure that an fd, closed in another thread, aborts I/O
+ operations. */
+ if (!cfd.isopen ())
+ break;
+
+ /* Check to see if this is a background read from a "tty",
+ sending a SIGTTIN, if appropriate */
+ res = cfd->bg_check (SIGTTIN);
+
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYIN;
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+ res = cfd->readv (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYIN;
+ }
+
+ out:
+ if (res >= 0 || get_errno () != EINTR || !thisframe.call_signal_handler ())
+ break;
+ set_errno (e);
+ }
+
+done:
+ syscall_printf ("%d = readv (%d, %p, %d), errno %d", res, fd, iov, iovcnt,
+ get_errno ());
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" ssize_t
+writev (const int fd, const struct iovec *const iov, const int iovcnt)
+{
+ int res = -1;
+ sig_dispatch_pending ();
+ const ssize_t tot = check_iovec_for_write (iov, iovcnt);
+
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto done;
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY)
+ {
+ set_errno (EBADF);
+ goto done;
+ }
+
+ /* Could block, so let user know we at least got here. */
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+ else
+ syscall_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+
+ res = cfd->bg_check (SIGTTOU);
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYOU;
+ res = cfd->writev (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYOU;
+ }
+
+done:
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+ else
+ syscall_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+
+ MALLOC_CHECK;
+ return res;
+}
+
+/* _open */
+/* newlib's fcntl.h defines _open as taking variable args so we must
+ correspond. The third arg if it exists is: mode_t mode. */
+extern "C" int
+open (const char *unix_path, int flags, ...)
+{
+ int res = -1;
+ va_list ap;
+ mode_t mode = 0;
+ sig_dispatch_pending ();
+ sigframe thisframe (mainthread);
+
+ syscall_printf ("open (%s, %p)", unix_path, flags);
+ if (!check_null_empty_str_errno (unix_path))
+ {
+ /* check for optional mode argument */
+ va_start (ap, flags);
+ mode = va_arg (ap, mode_t);
+ va_end (ap);
+
+ fhandler_base *fh;
+ cygheap_fdnew fd;
+
+ if (fd >= 0)
+ {
+ path_conv pc;
+ if (!(fh = cygheap->fdtab.build_fhandler_from_name (fd, unix_path,
+ NULL, pc)))
+ res = -1; // errno already set
+ else if (!fh->open (&pc, flags, (mode & 07777) & ~cygheap->umask))
+ {
+ fd.release ();
+ res = -1;
+ }
+ else if ((res = fd) <= 2)
+ set_std_handle (res);
+ }
+ }
+
+ syscall_printf ("%d = open (%s, %p)", res, unix_path, flags);
+ return res;
+}
+
+extern "C" int _open (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" int _open64 (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" _off64_t
+lseek64 (int fd, _off64_t pos, int dir)
+{
+ _off64_t res;
+ sigframe thisframe (mainthread);
+
+ if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END)
+ {
+ set_errno (EINVAL);
+ res = -1;
+ }
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->lseek (pos, dir);
+ else
+ res = -1;
+ }
+ syscall_printf ("%d = lseek (%d, %D, %d)", res, fd, pos, dir);
+
+ return res;
+}
+
+extern "C" int _lseek64 (int fd, _off64_t pos, int dir)
+ __attribute__ ((alias ("lseek64")));
+
+extern "C" _off_t
+lseek (int fd, _off_t pos, int dir)
+{
+ return lseek64 (fd, (_off64_t) pos, dir);
+}
+
+extern "C" _off_t _lseek (int, _off_t, int)
+ __attribute__ ((alias ("lseek")));
+
+extern "C" int
+close (int fd)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ syscall_printf ("close (%d)", fd);
+
+ MALLOC_CHECK;
+ cygheap_fdget cfd (fd, true);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ res = cfd->close ();
+ cfd.release ();
+ }
+
+ syscall_printf ("%d = close (%d)", res, fd);
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" int _close (int) __attribute__ ((alias ("close")));
+
+extern "C" int
+isatty (int fd)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = 0;
+ else
+ res = cfd->is_tty ();
+ syscall_printf ("%d = isatty (%d)", res, fd);
+ return res;
+}
+
+/* Under NT, try to make a hard link using backup API. If that
+ fails or we are Win 95, just copy the file.
+ FIXME: We should actually be checking partition type, not OS.
+ Under NTFS, we should support hard links. On FAT partitions,
+ we should just copy the file.
+*/
+
+extern "C" int
+link (const char *a, const char *b)
+{
+ int res = -1;
+ sigframe thisframe (mainthread);
+ path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL);
+ path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL);
+ extern BOOL allow_winsymlinks;
+
+ if (real_a.error)
+ {
+ set_errno (real_a.error);
+ goto done;
+ }
+
+ if (real_b.error)
+ {
+ set_errno (real_b.case_clash ? ECASECLASH : real_b.error);
+ goto done;
+ }
+
+ if (real_b.exists ())
+ {
+ syscall_printf ("file '%s' exists?", (char *) real_b);
+ set_errno (EEXIST);
+ goto done;
+ }
+
+ if (real_b[strlen (real_b) - 1] == '.')
+ {
+ syscall_printf ("trailing dot, bailing out");
+ set_errno (EINVAL);
+ goto done;
+ }
+
+ /* Shortcut hack. */
+ char new_lnk_buf[MAX_PATH + 5];
+ if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash)
+ {
+ strcpy (new_lnk_buf, b);
+ strcat (new_lnk_buf, ".lnk");
+ b = new_lnk_buf;
+ real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL);
+ }
+ /* Try to make hard link first on Windows NT */
+ if (wincap.has_hard_links ())
+ {
+ if (CreateHardLinkA (real_b, real_a, NULL))
+ goto success;
+
+ HANDLE hFileSource;
+
+ WIN32_STREAM_ID StreamId;
+ DWORD dwBytesWritten;
+ LPVOID lpContext;
+ DWORD cbPathLen;
+ DWORD StreamSize;
+ WCHAR wbuf[MAX_PATH];
+
+ BOOL bSuccess;
+
+ hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/,
+ &sec_none_nih, // sa
+ OPEN_EXISTING, 0, NULL);
+
+ if (hFileSource == INVALID_HANDLE_VALUE)
+ {
+ syscall_printf ("cannot open source, %E");
+ goto docopy;
+ }
+
+ cbPathLen = sys_mbstowcs (wbuf, real_b, MAX_PATH) * sizeof (WCHAR);
+
+ StreamId.dwStreamId = BACKUP_LINK;
+ StreamId.dwStreamAttributes = 0;
+ StreamId.dwStreamNameSize = 0;
+ StreamId.Size.HighPart = 0;
+ StreamId.Size.LowPart = cbPathLen;
+
+ StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) +
+ StreamId.dwStreamNameSize;
+
+ lpContext = NULL;
+ /* Write the WIN32_STREAM_ID */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) &StreamId, // buffer to write
+ StreamSize, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext);
+
+ if (bSuccess)
+ {
+ /* write the buffer containing the path */
+ /* FIXME: BackupWrite sometimes traps if linkname is invalid.
+ Need to handle. */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) wbuf, // buffer to write
+ cbPathLen, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext
+ );
+
+ if (!bSuccess)
+ syscall_printf ("cannot write linkname, %E");
+
+ /* Free context */
+ BackupWrite (
+ hFileSource,
+ NULL, // buffer to write
+ 0, // number of bytes to write
+ &dwBytesWritten,
+ TRUE, // abort
+ FALSE, // don't process security
+ &lpContext);
+ }
+ else
+ syscall_printf ("cannot write streamId, %E");
+
+ CloseHandle (hFileSource);
+
+ if (!bSuccess)
+ goto docopy;
+
+ success:
+ res = 0;
+ if (!allow_winsymlinks && real_a.is_lnk_symlink ())
+ SetFileAttributes (real_b, (DWORD) real_a
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_READONLY);
+
+ goto done;
+ }
+docopy:
+ /* do this with a copy */
+ if (CopyFileA (real_a, real_b, 1))
+ res = 0;
+ else
+ __seterrno ();
+
+done:
+ syscall_printf ("%d = link (%s, %s)", res, a, b);
+ return res;
+}
+
+/* chown: POSIX 5.6.5.1 */
+/*
+ * chown () is only implemented for Windows NT. Under other operating
+ * systems, it is only a stub that always returns zero.
+ */
+static int
+chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid)
+{
+ int res;
+
+ if (check_null_empty_str_errno (name))
+ return -1;
+
+ if (!wincap.has_security ()) // real chown only works on NT
+ res = 0; // return zero (and do nothing) under Windows 9x
+ else
+ {
+ /* we need Win32 path names because of usage of Win32 API functions */
+ path_conv win32_path (PC_NONULLEMPTY, name, fmode);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ res = -1;
+ goto done;
+ }
+
+ /* FIXME: This makes chown on a device succeed always. Someday we'll want
+ to actually allow chown to work properly on devices. */
+ if (win32_path.is_device () && !win32_path.issocket ())
+ {
+ res = 0;
+ goto done;
+ }
+
+ mode_t attrib = 0;
+ if (win32_path.isdir ())
+ attrib |= S_IFDIR;
+ res = get_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ &attrib);
+ if (!res)
+ res = set_file_attribute (win32_path.has_acls (), win32_path, uid,
+ gid, attrib);
+ if (res != 0 && (!win32_path.has_acls () || !allow_ntsec))
+ {
+ /* fake - if not supported, pretend we're like win95
+ where it just works */
+ res = 0;
+ }
+ }
+
+done:
+ syscall_printf ("%d = %schown (%s,...)",
+ res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
+ return res;
+}
+
+extern "C" int
+chown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+chown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_FOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+lchown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
+}
+
+extern "C" int
+lchown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_NOFOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+fchown32 (int fd, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchown (%d,...)", fd);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchown (%d,...) (no name)", fd);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchown (%d,...): calling chown_worker (%s,FOLLOW,...)",
+ fd, path);
+ return chown_worker (path, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+fchown (int fd, __uid16_t uid, __gid16_t gid)
+{
+ return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid));
+}
+
+/* umask: POSIX 5.3.3.1 */
+extern "C" mode_t
+umask (mode_t mask)
+{
+ mode_t oldmask;
+
+ oldmask = cygheap->umask;
+ cygheap->umask = mask & 0777;
+ return oldmask;
+}
+
+/* chmod: POSIX 5.6.4.1 */
+extern "C" int
+chmod (const char *path, mode_t mode)
+{
+ int res = -1;
+ sigframe thisframe (mainthread);
+
+ path_conv win32_path (path);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ goto done;
+ }
+
+ /* FIXME: This makes chmod on a device succeed always. Someday we'll want
+ to actually allow chmod to work properly on devices. */
+ if (win32_path.is_device () && !win32_path.issocket ())
+ {
+ res = 0;
+ goto done;
+ }
+
+ if (!win32_path.exists ())
+ __seterrno ();
+ else
+ {
+ /* temporary erase read only bit, to be able to set file security */
+ SetFileAttributes (win32_path, (DWORD) win32_path & ~FILE_ATTRIBUTE_READONLY);
+
+ if (win32_path.isdir ())
+ mode |= S_IFDIR;
+ if (!set_file_attribute (win32_path.has_acls (), win32_path,
+ ILLEGAL_UID, ILLEGAL_GID, mode)
+ && allow_ntsec)
+ res = 0;
+
+ /* if the mode we want has any write bits set, we can't
+ be read only. */
+ if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
+ (DWORD) win32_path &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY;
+
+ if (!win32_path.is_lnk_symlink () && S_ISLNK (mode) || S_ISSOCK (mode))
+ (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM;
+
+ if (!SetFileAttributes (win32_path, win32_path))
+ __seterrno ();
+ else if (!allow_ntsec)
+ /* Correct NTFS security attributes have higher priority */
+ res = 0;
+ }
+
+done:
+ syscall_printf ("%d = chmod (%s, %p)", res, path, mode);
+ return res;
+}
+
+/* fchmod: P96 5.6.4.1 */
+
+extern "C" int
+fchmod (int fd, mode_t mode)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o) (no name)", fd, mode);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchmod (%d, 0%o): calling chmod (%s, 0%o)",
+ fd, mode, path, mode);
+ return chmod (path, mode);
+}
+
+static void
+stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst)
+{
+ dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff);
+ dst->st_ino = ((unsigned) (src->st_ino >> 32)) | (unsigned) src->st_ino;
+ dst->st_mode = src->st_mode;
+ dst->st_nlink = src->st_nlink;
+ dst->st_uid = src->st_uid;
+ dst->st_gid = src->st_gid;
+ dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff);
+ dst->st_size = src->st_size;
+ dst->st_atim = src->st_atim;
+ dst->st_mtim = src->st_mtim;
+ dst->st_ctim = src->st_ctim;
+ dst->st_blksize = src->st_blksize;
+ dst->st_blocks = src->st_blocks;
+}
+
+extern "C" int
+fstat64 (int fd, struct __stat64 *buf)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ path_conv pc (cfd->get_name (), PC_SYM_NOFOLLOW);
+ memset (buf, 0, sizeof (struct __stat64));
+ res = cfd->fstat (buf, &pc);
+ if (!res && cfd->get_device () != FH_SOCKET)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, cfd->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = (cfd->get_device () << 16) | cfd->get_unit ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ syscall_printf ("%d = fstat (%d, %p)", res, fd, buf);
+ return res;
+}
+
+extern "C" int
+_fstat64_r (struct _reent *ptr, int fd, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat64 (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+fstat (int fd, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = fstat64 (fd, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_fstat_r (struct _reent *ptr, int fd, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* fsync: P96 6.6.1.1 */
+extern "C" int
+fsync (int fd)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fsync (%d)", fd);
+ return -1;
+ }
+
+ if (FlushFileBuffers (cfd->get_handle ()) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+/* sync: standards? */
+extern "C" int
+sync ()
+{
+ return 0;
+}
+
+suffix_info stat_suffixes[] =
+{
+ suffix_info ("", 1),
+ suffix_info (".exe", 1),
+ suffix_info (NULL)
+};
+
+/* Cygwin internal */
+int __stdcall
+stat_worker (const char *name, struct __stat64 *buf, int nofollow,
+ path_conv *pc)
+{
+ int res = -1;
+ path_conv real_path;
+ fhandler_base *fh = NULL;
+
+ if (check_null_invalid_struct_errno (buf))
+ goto done;
+
+ if (!pc)
+ pc = &real_path;
+
+ fh = cygheap->fdtab.build_fhandler_from_name (-1, name, NULL, *pc,
+ (nofollow ? PC_SYM_NOFOLLOW
+ : PC_SYM_FOLLOW)
+ | PC_FULL, stat_suffixes);
+
+ if (pc->error)
+ {
+ debug_printf ("got %d error from build_fhandler_from_name", pc->error);
+ set_errno (pc->error);
+ }
+ else
+ {
+ debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow,
+ pc, (DWORD) real_path);
+ memset (buf, 0, sizeof (*buf));
+ res = fh->fstat (buf, pc);
+ if (!res && fh->get_device () != FH_SOCKET)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, fh->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = (fh->get_device () << 16) | fh->get_unit ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ done:
+ if (fh)
+ delete fh;
+ MALLOC_CHECK;
+ syscall_printf ("%d = (%s, %p)", res, name, buf);
+ return res;
+}
+
+extern "C" int
+stat64 (const char *name, struct __stat64 *buf)
+{
+ sigframe thisframe (mainthread);
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 0);
+}
+
+extern "C" int
+_stat64_r (struct _reent *ptr, const char *name, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat64 (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+stat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = stat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_stat_r (struct _reent *ptr, const char *name, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat64 (const char *name, struct __stat64 *buf)
+{
+ sigframe thisframe (mainthread);
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 1);
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = lstat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+access (const char *fn, int flags)
+{
+ sigframe thisframe (mainthread);
+ // flags were incorrectly specified
+ if (flags & ~(F_OK|R_OK|W_OK|X_OK))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ path_conv real_path (fn, PC_SYM_FOLLOW | PC_FULL, stat_suffixes);
+ if (real_path.error)
+ {
+ set_errno (real_path.error);
+ return -1;
+ }
+
+ if (!real_path.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ if (!(flags & (R_OK | W_OK | X_OK)))
+ return 0;
+
+ if (real_path.has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK))
+ {
+ set_errno (EACCES);
+ return -1;
+ }
+
+ if (real_path.has_acls () && allow_ntsec)
+ return check_file_access (real_path, flags);
+
+ struct __stat64 st;
+ int r = stat_worker (fn, &st, 0);
+ if (r)
+ return -1;
+ r = -1;
+ if (flags & R_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IRUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IRGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IROTH))
+ goto done;
+ }
+ if (flags & W_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IWUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IWGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IWOTH))
+ goto done;
+ }
+ if (flags & X_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IXUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IXGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IXOTH))
+ goto done;
+ }
+ r = 0;
+done:
+ if (r)
+ set_errno (EACCES);
+ return r;
+}
+
+extern "C" int
+rename (const char *oldpath, const char *newpath)
+{
+ sigframe thisframe (mainthread);
+ int res = 0;
+ char *lnk_suffix = NULL;
+
+ path_conv real_old (oldpath, PC_SYM_NOFOLLOW);
+
+ if (real_old.error)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_old.error);
+ return -1;
+ }
+
+ path_conv real_new (newpath, PC_SYM_NOFOLLOW);
+
+ /* Shortcut hack. */
+ char new_lnk_buf[MAX_PATH + 5];
+ if (real_old.is_lnk_symlink () && !real_new.error && !real_new.case_clash)
+ {
+ strcpy (new_lnk_buf, newpath);
+ strcat (new_lnk_buf, ".lnk");
+ newpath = new_lnk_buf;
+ real_new.check (newpath, PC_SYM_NOFOLLOW);
+ }
+
+ if (real_new.error || real_new.case_clash)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_new.case_clash ? ECASECLASH : real_new.error);
+ return -1;
+ }
+
+ if (!writable_directory (real_old) || !writable_directory (real_new))
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (EACCES);
+ return -1;
+ }
+
+ if (!real_old.exists ()) /* file to move doesn't exist */
+ {
+ syscall_printf ("file to move doesn't exist");
+ set_errno (ENOENT);
+ return (-1);
+ }
+
+ /* Destination file exists and is read only, change that or else
+ the rename won't work. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY);
+
+ /* Shortcut hack No. 2, part 1 */
+ if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_symlink ()
+ && (lnk_suffix = strrchr (real_new.get_win32 (), '.')))
+ *lnk_suffix = '\0';
+
+ if (!MoveFile (real_old, real_new))
+ res = -1;
+
+ if (res == 0 || (GetLastError () != ERROR_ALREADY_EXISTS
+ && GetLastError () != ERROR_FILE_EXISTS))
+ goto done;
+
+ if (wincap.has_move_file_ex ())
+ {
+ if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
+ MOVEFILE_REPLACE_EXISTING))
+ res = 0;
+ }
+ else
+ {
+ syscall_printf ("try win95 hack");
+ for (int i = 0; i < 2; i++)
+ {
+ if (!DeleteFileA (real_new.get_win32 ()) &&
+ GetLastError () != ERROR_FILE_NOT_FOUND)
+ {
+ syscall_printf ("deleting %s to be paranoid",
+ real_new.get_win32 ());
+ break;
+ }
+ else if (MoveFile (real_old.get_win32 (), real_new.get_win32 ()))
+ {
+ res = 0;
+ break;
+ }
+ }
+ }
+
+done:
+ if (res)
+ {
+ __seterrno ();
+ /* Reset R/O attributes if neccessary. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, real_new);
+ }
+ else
+ {
+ /* make the new file have the permissions of the old one */
+ DWORD attr = real_old;
+#ifdef HIDDEN_DOT_FILES
+ char *c = strrchr (real_old.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_old.get_win32 () == '.')
+ attr &= ~FILE_ATTRIBUTE_HIDDEN;
+ c = strrchr (real_new.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_new.get_win32 () == '.')
+ attr |= FILE_ATTRIBUTE_HIDDEN;
+#endif
+ SetFileAttributes (real_new, attr);
+
+ /* Shortcut hack, No. 2, part 2 */
+ /* if the new filename was an existing shortcut, remove it now if the
+ new filename is equal to the shortcut name without .lnk suffix. */
+ if (lnk_suffix)
+ {
+ *lnk_suffix = '.';
+ DeleteFile (real_new);
+ }
+ }
+
+ syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old,
+ (char *) real_new);
+
+ return res;
+}
+
+struct system_cleanup_args
+{
+ _sig_func_ptr oldint, oldquit;
+ sigset_t old_mask;
+};
+
+static void system_cleanup (void *args)
+{
+ struct system_cleanup_args *cleanup_args = (struct system_cleanup_args *) args;
+
+ signal (SIGINT, cleanup_args->oldint);
+ signal (SIGQUIT, cleanup_args->oldquit);
+ (void) sigprocmask (SIG_SETMASK, &cleanup_args->old_mask, 0);
+}
+
+extern "C" int
+system (const char *cmdstring)
+{
+ pthread_testcancel ();
+
+ if (check_null_empty_str_errno (cmdstring))
+ return -1;
+
+ sigframe thisframe (mainthread);
+ int res;
+ const char* command[4];
+ struct system_cleanup_args cleanup_args;
+ sigset_t child_block;
+
+ if (cmdstring == (const char *) NULL)
+ return 1;
+
+ cleanup_args.oldint = signal (SIGINT, SIG_IGN);
+ cleanup_args.oldquit = signal (SIGQUIT, SIG_IGN);
+ sigemptyset (&child_block);
+ sigaddset (&child_block, SIGCHLD);
+ (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup_args.old_mask);
+
+ command[0] = "sh";
+ command[1] = "-c";
+ command[2] = cmdstring;
+ command[3] = (const char *) NULL;
+
+ pthread_cleanup_push (system_cleanup, (void *) &cleanup_args);
+
+ if ((res = spawnvp (_P_WAIT, "sh", command)) == -1)
+ {
+ // when exec fails, return value should be as if shell
+ // executed exit (127)
+ res = 127;
+ }
+
+ pthread_cleanup_pop (1);
+
+ return res;
+}
+
+extern "C" int
+setdtablesize (int size)
+{
+ if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size))
+ return 0;
+
+ return -1;
+}
+
+extern "C" int
+getdtablesize ()
+{
+ return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX;
+}
+
+extern "C" size_t
+getpagesize ()
+{
+ if (!system_info.dwPageSize)
+ GetSystemInfo (&system_info);
+ return (int) system_info.dwPageSize;
+}
+
+static int
+check_posix_perm (const char *fname, int v)
+{
+ /* Windows 95/98/ME don't support file system security at all. */
+ if (!wincap.has_security ())
+ return 0;
+
+ /* ntea is ok for supporting permission bits but it doesn't support
+ full POSIX security settings. */
+ if (v == _PC_POSIX_PERMISSIONS && allow_ntea)
+ return 1;
+
+ if (!allow_ntsec)
+ return 0;
+
+ char *root = rootdir (strcpy ((char *)alloca (strlen (fname)), fname));
+
+ if (!allow_smbntsec
+ && ((root[0] == '\\' && root[1] == '\\')
+ || GetDriveType (root) == DRIVE_REMOTE))
+ return 0;
+
+ DWORD vsn, len, flags;
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &len, &flags, NULL, 16))
+ {
+ __seterrno ();
+ return 0;
+ }
+
+ return (flags & FS_PERSISTENT_ACLS) ? 1 : 0;
+}
+
+/* FIXME: not all values are correct... */
+extern "C" long int
+fpathconf (int fd, int v)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ switch (v)
+ {
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ if (isatty (fd))
+ return _POSIX_MAX_CANON;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ return PATH_MAX;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ if (cfd->is_tty ())
+ return -1;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ if (cfd->get_device () == FH_DISK)
+ return check_posix_perm (cfd->get_win32_name (), v);
+ set_errno (EINVAL);
+ return -1;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" long int
+pathconf (const char *file, int v)
+{
+ switch (v)
+ {
+ case _PC_PATH_MAX:
+ if (check_null_empty_str_errno (file))
+ return -1;
+ return PATH_MAX - strlen (file);
+ case _PC_NAME_MAX:
+ return PATH_MAX;
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ return _POSIX_MAX_CANON;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ return -1;
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ {
+ path_conv full_path (file, PC_SYM_FOLLOW | PC_FULL);
+ if (full_path.error)
+ {
+ set_errno (full_path.error);
+ return -1;
+ }
+ if (full_path.is_device ())
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ return check_posix_perm (full_path, v);
+ }
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" char *
+ttyname (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0 || !cfd->is_tty ())
+ {
+ return 0;
+ }
+ return (char *) (cfd->ttyname ());
+}
+
+extern "C" char *
+ctermid (char *str)
+{
+ static NO_COPY char buf[16];
+ if (str == NULL)
+ str = buf;
+ if (!real_tty_attached (myself))
+ strcpy (str, "/dev/conin");
+ else
+ __small_sprintf (str, "/dev/tty%d", myself->ctty);
+ return str;
+}
+
+/* Tells stdio if it should do the cr/lf conversion for this file */
+extern "C" int
+_cygwin_istext_for_stdio (int fd)
+{
+ syscall_printf ("_cygwin_istext_for_stdio (%d)", fd);
+ if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING)
+ {
+ syscall_printf (" _cifs: old API");
+ return 0; /* we do it for old apps, due to getc/putc macros */
+ }
+
+ cygheap_fdget cfd (fd, false, false);
+ if (cfd < 0)
+ {
+ syscall_printf (" _cifs: fd not open");
+ return 0;
+ }
+
+ if (cfd->get_device () != FH_DISK)
+ {
+ syscall_printf (" _cifs: fd not disk file");
+ return 0;
+ }
+
+ if (cfd->get_w_binary () || cfd->get_r_binary ())
+ {
+ syscall_printf (" _cifs: get_*_binary");
+ return 0;
+ }
+
+ syscall_printf ("_cygwin_istext_for_stdio says yes");
+ return 1;
+}
+
+/* internal newlib function */
+extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *));
+
+static int setmode_mode;
+static int setmode_file;
+
+static int
+setmode_helper (FILE *f)
+{
+ if (fileno (f) != setmode_file)
+ return 0;
+ syscall_printf ("setmode: file was %s now %s",
+ f->_flags & __SCLE ? "text" : "raw",
+ setmode_mode & O_TEXT ? "text" : "raw");
+ if (setmode_mode & O_TEXT)
+ f->_flags |= __SCLE;
+ else
+ f->_flags &= ~__SCLE;
+ return 0;
+}
+
+extern "C" int
+getmode (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+
+ return cfd->get_flags () & (O_BINARY | O_TEXT);
+}
+
+/* Set a file descriptor into text or binary mode, returning the
+ previous mode. */
+
+extern "C" int
+setmode (int fd, int mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ if (mode != O_BINARY && mode != O_TEXT && mode != 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Note that we have no way to indicate the case that writes are
+ binary but not reads, or vice-versa. These cases can arise when
+ using the tty or console interface. People using those
+ interfaces should not use setmode. */
+
+ int res;
+ if (cfd->get_w_binary () && cfd->get_r_binary ())
+ res = O_BINARY;
+ else if (cfd->get_w_binset () && cfd->get_r_binset ())
+ res = O_TEXT; /* Specifically set O_TEXT */
+ else
+ res = 0;
+
+ if (!mode)
+ cfd->reset_to_open_binmode ();
+ else
+ cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
+
+ if (_cygwin_istext_for_stdio (fd))
+ setmode_mode = O_TEXT;
+ else
+ setmode_mode = O_BINARY;
+ setmode_file = fd;
+ _fwalk (_REENT, setmode_helper);
+
+ syscall_printf ("setmode (%d<%s>, %p) returns %s", fd, cfd->get_name (),
+ mode, res & O_TEXT ? "text" : "binary");
+ return res;
+}
+
+extern "C" int
+ftruncate64 (int fd, _off64_t length)
+{
+ sigframe thisframe (mainthread);
+ int res = -1;
+
+ if (length < 0)
+ set_errno (EINVAL);
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ {
+ HANDLE h = cygheap->fdtab[fd]->get_handle ();
+
+ if (cfd->get_handle ())
+ {
+ /* remember curr file pointer location */
+ _off64_t prev_loc = cfd->lseek (0, SEEK_CUR);
+
+ cfd->lseek (length, SEEK_SET);
+ if (!SetEndOfFile (h))
+ __seterrno ();
+ else
+ res = 0;
+
+ /* restore original file pointer location */
+ cfd->lseek (prev_loc, SEEK_SET);
+ }
+ }
+ }
+
+ syscall_printf ("%d = ftruncate (%d, %d)", res, fd, length);
+ return res;
+}
+
+/* ftruncate: P96 5.6.7.1 */
+extern "C" int
+ftruncate (int fd, _off_t length)
+{
+ return ftruncate64 (fd, (_off64_t)length);
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate64 (const char *pathname, _off64_t length)
+{
+ sigframe thisframe (mainthread);
+ int fd;
+ int res = -1;
+
+ fd = open (pathname, O_RDWR);
+
+ if (fd == -1)
+ set_errno (EBADF);
+ else
+ {
+ res = ftruncate64 (fd, length);
+ close (fd);
+ }
+ syscall_printf ("%d = truncate (%s, %d)", res, pathname, length);
+
+ return res;
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate (const char *pathname, _off_t length)
+{
+ return truncate64 (pathname, (_off64_t)length);
+}
+
+extern "C" long
+get_osfhandle (int fd)
+{
+ long res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = (long) cfd->get_handle ();
+ else
+ res = -1;
+
+ syscall_printf ("%d = get_osfhandle (%d)", res, fd);
+ return res;
+}
+
+extern "C" int
+statfs (const char *fname, struct statfs *sfs)
+{
+ sigframe thisframe (mainthread);
+ if (!sfs)
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL);
+
+ const char *root = full_path.root_dir();
+
+ syscall_printf ("statfs %s", root);
+
+ /* GetDiskFreeSpaceEx must be called before GetDiskFreeSpace on
+ WinME, to avoid the MS KB 314417 bug */
+ ULARGE_INTEGER availb, freeb, totalb;
+ BOOL status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb);
+
+ DWORD spc, bps, availc, freec, totalc;
+
+ if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ if (status)
+ {
+ availc = availb.QuadPart / (spc*bps);
+ totalc = totalb.QuadPart / (spc*bps);
+ freec = freeb.QuadPart / (spc*bps);
+ }
+ else
+ availc = freec;
+
+ DWORD vsn, maxlen, flags;
+
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0))
+ {
+ __seterrno ();
+ return -1;
+ }
+ sfs->f_type = flags;
+ sfs->f_bsize = spc*bps;
+ sfs->f_blocks = totalc;
+ sfs->f_bavail = availc;
+ sfs->f_bfree = freec;
+ sfs->f_files = -1;
+ sfs->f_ffree = -1;
+ sfs->f_fsid = vsn;
+ sfs->f_namelen = maxlen;
+ return 0;
+}
+
+extern "C" int
+fstatfs (int fd, struct statfs *sfs)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ return statfs (cfd->get_name (), sfs);
+}
+
+/* setpgid: POSIX 4.3.3.1 */
+extern "C" int
+setpgid (pid_t pid, pid_t pgid)
+{
+ sigframe thisframe (mainthread);
+ int res = -1;
+ if (pid == 0)
+ pid = getpid ();
+ if (pgid == 0)
+ pgid = pid;
+
+ if (pgid < 0)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ else
+ {
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ goto out;
+ }
+ /* A process may only change the process group of itself and its children */
+ if (p == myself || p->ppid == myself->pid)
+ {
+ p->pgid = pgid;
+ if (p->pid != p->pgid)
+ p->set_has_pgid_children (0);
+ res = 0;
+ }
+ else
+ {
+ set_errno (EPERM);
+ goto out;
+ }
+ }
+out:
+ syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
+ return res;
+}
+
+extern "C" pid_t
+getpgid (pid_t pid)
+{
+ sigframe thisframe (mainthread);
+ if (pid == 0)
+ pid = getpid ();
+
+ pinfo p (pid);
+ if (p == 0)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ return p->pgid;
+}
+
+extern "C" int
+setpgrp (void)
+{
+ sigframe thisframe (mainthread);
+ return setpgid (0, 0);
+}
+
+extern "C" pid_t
+getpgrp (void)
+{
+ sigframe thisframe (mainthread);
+ return getpgid (0);
+}
+
+extern "C" char *
+ptsname (int fd)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return 0;
+ return (char *) (cfd->ptsname ());
+}
+
+/* FIXME: what is this? */
+extern "C" int __declspec(dllexport)
+regfree ()
+{
+ return 0;
+}
+
+/* mknod was the call to create directories before the introduction
+ of mkdir in 4.2BSD and SVR3. Use of mknod required superuser privs
+ so the mkdir command had to be setuid root.
+ Although mknod hasn't been implemented yet, some GNU tools (e.g. the
+ fileutils) assume its existence so we must provide a stub that always
+ fails. */
+extern "C" int
+mknod32 (const char *_path, mode_t mode, __dev32_t dev)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+mknod (const char *_path, mode_t mode, __dev16_t dev)
+{
+ return mknod32 (_path, mode, (__dev32_t) dev);
+}
+
+extern "C" int
+mkfifo (const char *_path, mode_t mode)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+/* seteuid: standards? */
+extern "C" int
+seteuid32 (__uid32_t uid)
+{
+ debug_printf ("uid: %u myself->gid: %u", uid, myself->gid);
+
+ if (uid == myself->uid && !cygheap->user.groups.ischanged)
+ {
+ debug_printf ("Nothing happens");
+ return 0;
+ }
+
+ sigframe thisframe (mainthread);
+ cygsid usersid;
+ user_groups &groups = cygheap->user.groups;
+ HANDLE ptok, new_token = INVALID_HANDLE_VALUE;
+ struct passwd * pw_new;
+ cygpsid origpsid, psid2 (NO_SID);
+ BOOL token_is_internal, issamesid;
+
+ pw_new = internal_getpwuid (uid);
+ if (!wincap.has_security () && pw_new)
+ goto success_9x;
+ if (!usersid.getfrompw (pw_new))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ RevertToSelf ();
+ if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok))
+ {
+ __seterrno ();
+ goto failed_ptok;;
+ }
+
+ /* Verify if the process token is suitable. */
+ if (verify_token (ptok, usersid, groups))
+ new_token = ptok;
+ /* Verify if the external token is suitable */
+ else if (cygheap->user.external_token != INVALID_HANDLE_VALUE
+ && verify_token (cygheap->user.external_token, usersid, groups))
+ new_token = cygheap->user.external_token;
+ /* Verify if the current token (internal or former external) is suitable */
+ else if (cygheap->user.current_token != INVALID_HANDLE_VALUE
+ && cygheap->user.current_token != cygheap->user.external_token
+ && verify_token (cygheap->user.current_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.current_token;
+ /* Verify if the internal token is suitable */
+ else if (cygheap->user.internal_token != INVALID_HANDLE_VALUE
+ && cygheap->user.internal_token != cygheap->user.current_token
+ && verify_token (cygheap->user.internal_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.internal_token;
+
+ debug_printf ("Found token %d", new_token);
+
+ /* Set process def dacl to allow access to impersonated token */
+ if (cygheap->user.current_token != new_token)
+ {
+ char dacl_buf[MAX_DACL_LEN (5)];
+ if (usersid != (origpsid = cygheap->user.orig_sid ()))
+ psid2 = usersid;
+ if (sec_acl ((PACL) dacl_buf, FALSE, origpsid, psid2))
+ {
+ TOKEN_DEFAULT_DACL tdacl;
+ tdacl.DefaultDacl = (PACL) dacl_buf;
+ if (!SetTokenInformation (ptok, TokenDefaultDacl,
+ &tdacl, sizeof dacl_buf))
+ debug_printf ("SetTokenInformation"
+ "(TokenDefaultDacl): %E");
+ }
+ }
+
+ /* If no impersonation token is available, try to
+ authenticate using NtCreateToken () or subauthentication. */
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ new_token = create_token (usersid, groups, pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ /* create_token failed. Try subauthentication. */
+ debug_printf ("create token failed, try subauthentication.");
+ new_token = subauth (pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ goto failed;
+ }
+ /* Keep at most one internal token */
+ if (cygheap->user.internal_token != INVALID_HANDLE_VALUE)
+ CloseHandle (cygheap->user.internal_token);
+ cygheap->user.internal_token = new_token;
+ }
+ else if (new_token != ptok)
+ {
+ /* Avoid having HKCU use default user */
+ load_registry_hive (usersid);
+
+ /* Try setting owner to same value as user. */
+ if (!SetTokenInformation (new_token, TokenOwner,
+ &usersid, sizeof usersid))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenOwner): %E");
+ /* Try setting primary group in token to current group */
+ if (!SetTokenInformation (new_token,
+ TokenPrimaryGroup,
+ &groups.pgsid, sizeof (cygsid)))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenPrimaryGroup): %E");
+ }
+
+ CloseHandle (ptok);
+ issamesid = (usersid == (psid2 = cygheap->user.sid ()));
+ cygheap->user.set_sid (usersid);
+ cygheap->user.current_token = new_token == ptok ? INVALID_HANDLE_VALUE
+ : new_token;
+ if (!issamesid) /* MS KB 199190 */
+ RegCloseKey(HKEY_CURRENT_USER);
+ cygheap->user.reimpersonate ();
+ if (!issamesid)
+ user_shared_initialize ();
+
+success_9x:
+ cygheap->user.set_name (pw_new->pw_name);
+ myself->uid = uid;
+ groups.ischanged = FALSE;
+ return 0;
+
+failed:
+ CloseHandle (ptok);
+failed_ptok:
+ cygheap->user.reimpersonate ();
+ return -1;
+}
+
+extern "C" int
+seteuid (__uid16_t uid)
+{
+ return seteuid32 (uid16touid32 (uid));
+}
+
+/* setuid: POSIX 4.2.2.1 */
+extern "C" int
+setuid32 (__uid32_t uid)
+{
+ int ret = seteuid32 (uid);
+ if (!ret)
+ cygheap->user.real_uid = myself->uid;
+ debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setuid (__uid16_t uid)
+{
+ return setuid32 (uid16touid32 (uid));
+}
+
+extern "C" int
+setreuid32 (__uid32_t ruid, __uid32_t euid)
+{
+ int ret = 0;
+ bool tried = false;
+ __uid32_t old_euid = myself->uid;
+
+ if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid)
+ tried = !(ret = seteuid32 (ruid));
+ if (!ret && euid != ILLEGAL_UID)
+ ret = seteuid32 (euid);
+ if (tried && (ret || euid == ILLEGAL_UID) && seteuid32 (old_euid))
+ system_printf ("Cannot restore original euid %u", old_euid);
+ if (!ret && ruid != ILLEGAL_UID)
+ cygheap->user.real_uid = ruid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setreuid (__uid16_t ruid, __uid16_t euid)
+{
+ return setreuid32 (uid16touid32 (ruid), uid16touid32 (euid));
+}
+
+/* setegid: from System V. */
+extern "C" int
+setegid32 (__gid32_t gid)
+{
+ debug_printf ("new egid: %u current: %u", gid, myself->gid);
+
+ if (gid == myself->gid || !wincap.has_security ())
+ {
+ myself->gid = gid;
+ return 0;
+ }
+
+ sigframe thisframe (mainthread);
+ user_groups * groups = &cygheap->user.groups;
+ cygsid gsid;
+ HANDLE ptok;
+ struct __group32 * gr = internal_getgrgid (gid);
+
+ if (!gsid.getfromgr (gr))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ myself->gid = gid;
+
+ groups->update_pgrp (gsid);
+ /* If impersonated, update primary group and revert */
+ if (cygheap->user.issetuid ())
+ {
+ if (!SetTokenInformation (cygheap->user.token (),
+ TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(thread, "
+ "TokenPrimaryGroup): %E");
+ RevertToSelf ();
+ }
+ if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok))
+ debug_printf ("OpenProcessToken(): %E");
+ else
+ {
+ if (!SetTokenInformation (ptok, TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(process, "
+ "TokenPrimaryGroup): %E");
+ CloseHandle (ptok);
+ }
+ if (cygheap->user.issetuid ()
+ && !ImpersonateLoggedOnUser (cygheap->user.token ()))
+ system_printf ("Impersonating in setegid failed: %E");
+ return 0;
+}
+
+extern "C" int
+setegid (__gid16_t gid)
+{
+ return setegid32 (gid16togid32 (gid));
+}
+
+/* setgid: POSIX 4.2.2.1 */
+extern "C" int
+setgid32 (__gid32_t gid)
+{
+ int ret = setegid32 (gid);
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setgid (__gid16_t gid)
+{
+ int ret = setegid32 (gid16togid32 (gid));
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setregid32 (__gid32_t rgid, __gid32_t egid)
+{
+ int ret = 0;
+ bool tried = false;
+ __gid32_t old_egid = myself->gid;
+
+ if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid)
+ tried = !(ret = setegid32 (rgid));
+ if (!ret && egid != ILLEGAL_GID)
+ ret = setegid32 (egid);
+ if (tried && (ret || egid == ILLEGAL_GID) && setegid32 (old_egid))
+ system_printf ("Cannot restore original egid %u", old_egid);
+ if (!ret && rgid != ILLEGAL_GID)
+ cygheap->user.real_gid = rgid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid);
+ return ret;
+}
+
+extern "C" int
+setregid (__gid16_t rgid, __gid16_t egid)
+{
+ return setregid32 (gid16togid32 (rgid), gid16togid32 (egid));
+}
+
+/* chroot: privileged Unix system call. */
+/* FIXME: Not privileged here. How should this be done? */
+extern "C" int
+chroot (const char *newroot)
+{
+ sigframe thisframe (mainthread);
+ path_conv path (newroot, PC_SYM_FOLLOW | PC_FULL | PC_POSIX);
+
+ int ret;
+ if (path.error)
+ ret = -1;
+ else if (!path.exists ())
+ {
+ set_errno (ENOENT);
+ ret = -1;
+ }
+ else if (!path.isdir ())
+ {
+ set_errno (ENOTDIR);
+ ret = -1;
+ }
+ else
+ {
+ cygheap->root.set (path.normalized_path, path);
+ ret = 0;
+ }
+
+ syscall_printf ("%d = chroot (%s)", ret ? get_errno () : 0,
+ newroot ? newroot : "NULL");
+ if (path.normalized_path)
+ cfree (path.normalized_path);
+ return ret;
+}
+
+extern "C" int
+creat (const char *path, mode_t mode)
+{
+ sigframe thisframe (mainthread);
+ return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+extern "C" void
+__assertfail ()
+{
+ exit (99);
+}
+
+extern "C" int
+getw (FILE *fp)
+{
+ sigframe thisframe (mainthread);
+ int w, ret;
+ ret = fread (&w, sizeof (int), 1, fp);
+ return ret != 1 ? EOF : w;
+}
+
+extern "C" int
+putw (int w, FILE *fp)
+{
+ sigframe thisframe (mainthread);
+ int ret;
+ ret = fwrite (&w, sizeof (int), 1, fp);
+ if (feof (fp) || ferror (fp))
+ return -1;
+ return 0;
+}
+
+extern "C" int
+wcscmp (const wchar_t *s1, const wchar_t *s2)
+{
+ while (*s1 && *s1 == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ return (* (unsigned short *) s1) - (* (unsigned short *) s2);
+}
+
+extern "C" size_t
+wcslen (const wchar_t *s1)
+{
+ int l = 0;
+ while (s1[l])
+ l++;
+ return l;
+}
+
+/* FIXME: to do this right, maybe work out the usoft va_list machine
+ and use wsvprintfW instead?
+*/
+extern "C" int
+wprintf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = vprintf (fmt, ap);
+ va_end (ap);
+ return ret;
+}
+
+extern "C" int
+vhangup ()
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" _PTR
+memccpy (_PTR out, const _PTR in, int c, size_t len)
+{
+ const char *inc = (char *) in;
+ char *outc = (char *) out;
+
+ while (len)
+ {
+ char x = *inc++;
+ *outc++ = x;
+ if (x == c)
+ return outc;
+ len --;
+ }
+ return 0;
+}
+
+extern "C" int
+nice (int incr)
+{
+ sigframe thisframe (mainthread);
+ DWORD priority[] =
+ {
+ IDLE_PRIORITY_CLASS,
+ IDLE_PRIORITY_CLASS,
+ NORMAL_PRIORITY_CLASS,
+ HIGH_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS
+ };
+ int curr = 2;
+
+ switch (GetPriorityClass (hMainProc))
+ {
+ case IDLE_PRIORITY_CLASS:
+ curr = 1;
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ curr = 2;
+ break;
+ case HIGH_PRIORITY_CLASS:
+ curr = 3;
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ curr = 4;
+ break;
+ }
+ if (incr > 0)
+ incr = -1;
+ else if (incr < 0)
+ incr = 1;
+
+ if (SetPriorityClass (hMainProc, priority[curr + incr]) == FALSE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first bit set in I.
+ */
+
+extern "C" int
+ffs (int i)
+{
+ static const unsigned char table[] =
+ {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ unsigned long int a;
+ unsigned long int x = i & -i;
+
+ a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24);
+
+ return table[x >> a] + a;
+}
+
+extern "C" void
+updwtmp (const char *wtmp_file, const struct utmp *ut)
+{
+ /* Writing to wtmp must be atomic to prevent mixed up data. */
+ char mutex_name[MAX_PATH];
+ HANDLE mutex;
+ int fd;
+
+ mutex = CreateMutex (NULL, FALSE, shared_name (mutex_name, "wtmp_mutex", 0));
+ if (mutex)
+ while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED)
+ ;
+ if ((fd = open (wtmp_file, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0)
+ {
+ write (fd, ut, sizeof *ut);
+ close (fd);
+ }
+ if (mutex)
+ {
+ ReleaseMutex (mutex);
+ CloseHandle (mutex);
+ }
+}
+
+extern "C" void
+logwtmp (const char *line, const char *user, const char *host)
+{
+ sigframe thisframe (mainthread);
+ struct utmp ut;
+ memset (&ut, 0, sizeof ut);
+ ut.ut_type = USER_PROCESS;
+ ut.ut_pid = getpid ();
+ if (line)
+ strncpy (ut.ut_line, line, sizeof ut.ut_line);
+ time (&ut.ut_time);
+ if (user)
+ strncpy (ut.ut_user, user, sizeof ut.ut_user);
+ if (host)
+ strncpy (ut.ut_host, host, sizeof ut.ut_host);
+ updwtmp (_PATH_WTMP, &ut);
+}
+
+extern "C" void
+login (struct utmp *ut)
+{
+ sigframe thisframe (mainthread);
+ pututline (ut);
+ endutent ();
+ updwtmp (_PATH_WTMP, ut);
+}
+
+extern "C" int
+logout (char *line)
+{
+ sigframe thisframe (mainthread);
+ struct utmp ut_buf, *ut;
+
+ memset (&ut_buf, 0, sizeof ut_buf);
+ strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line);
+ setutent ();
+ ut = getutline (&ut_buf);
+
+ if (ut)
+ {
+ ut->ut_type = DEAD_PROCESS;
+ memset (ut->ut_user, 0, sizeof ut->ut_user);
+ time (&ut->ut_time);
+ updwtmp (_PATH_WTMP, &ut_buf);
+ debug_printf ("set logout time for %s", line);
+ memset (ut->ut_line, 0, sizeof ut_buf.ut_line);
+ ut->ut_time = 0;
+ pututline (ut);
+ endutent ();
+ }
+ return 1;
+}
+
+static int utmp_fd = -1;
+static bool utmp_readonly = false;
+static char *utmp_file = (char *) _PATH_UTMP;
+
+static void
+internal_setutent (bool force_readwrite)
+{
+ sigframe thisframe (mainthread);
+ if (force_readwrite && utmp_readonly)
+ endutent ();
+ if (utmp_fd < 0)
+ {
+ utmp_fd = open (utmp_file, O_RDWR | O_BINARY);
+ /* If open fails, we assume an unprivileged process (who?). In this
+ case we try again for reading only unless the process calls
+ pututline() (==force_readwrite) in which case opening just fails. */
+ if (utmp_fd < 0 && !force_readwrite)
+ {
+ utmp_fd = open (utmp_file, O_RDONLY | O_BINARY);
+ if (utmp_fd >= 0)
+ utmp_readonly = true;
+ }
+ }
+ else
+ lseek (utmp_fd, 0, SEEK_SET);
+}
+
+extern "C" void
+setutent ()
+{
+ internal_setutent (false);
+}
+
+extern "C" void
+endutent ()
+{
+ sigframe thisframe (mainthread);
+ if (utmp_fd >= 0)
+ {
+ close (utmp_fd);
+ utmp_fd = -1;
+ utmp_readonly = false;
+ }
+}
+
+extern "C" void
+utmpname (_CONST char *file)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_empty_str (file))
+ {
+ debug_printf ("Invalid file");
+ return;
+ }
+ endutent ();
+ utmp_file = strdup (file);
+ debug_printf ("New UTMP file: %s", utmp_file);
+}
+
+/* Note: do not make NO_COPY */
+static struct utmp utmp_data_buf[16];
+static unsigned utix = 0;
+#define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0]))
+#define utmp_data ({ \
+ if (utix > nutdbuf) \
+ utix = 0; \
+ utmp_data_buf + utix++; \
+})
+
+extern "C" struct utmp *
+getutent ()
+{
+ sigframe thisframe (mainthread);
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut)
+ return NULL;
+ return ut;
+}
+
+extern "C" struct utmp *
+getutid (struct utmp *id)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct_errno (id))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ {
+ switch (id->ut_type)
+ {
+ case RUN_LVL:
+ case BOOT_TIME:
+ case OLD_TIME:
+ case NEW_TIME:
+ if (id->ut_type == ut->ut_type)
+ return ut;
+ break;
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0)
+ return ut;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+extern "C" struct utmp *
+getutline (struct utmp *line)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct_errno (line))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ if ((ut->ut_type == LOGIN_PROCESS ||
+ ut->ut_type == USER_PROCESS) &&
+ !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line)))
+ return ut;
+
+ return NULL;
+}
+
+extern "C" void
+pututline (struct utmp *ut)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct (ut))
+ return;
+ internal_setutent (true);
+ if (utmp_fd < 0)
+ {
+ debug_printf ("error: utmp_fd %d", utmp_fd);
+ return;
+ }
+ debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n",
+ ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id);
+ debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n",
+ ut->ut_user, ut->ut_host);
+ /* Read/write to utmp must be atomic to prevent overriding data
+ by concurrent processes. */
+ char mutex_name[MAX_PATH];
+ HANDLE mutex = CreateMutex (NULL, FALSE,
+ shared_name (mutex_name, "utmp_mutex", 0));
+ if (mutex)
+ while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED)
+ ;
+ struct utmp *u;
+ if ((u = getutid (ut)))
+ lseek (utmp_fd, -sizeof *ut, SEEK_CUR);
+ else
+ lseek (utmp_fd, 0, SEEK_END);
+ write (utmp_fd, ut, sizeof *ut);
+ if (mutex)
+ {
+ ReleaseMutex (mutex);
+ CloseHandle (mutex);
+ }
+}
+
+extern "C"
+long gethostid(void)
+{
+ unsigned data[13] = {0x92895012,
+ 0x10293412,
+ 0x29602018,
+ 0x81928167,
+ 0x34601329,
+ 0x75630198,
+ 0x89860395,
+ 0x62897564,
+ 0x00194362,
+ 0x20548593,
+ 0x96839102,
+ 0x12219854,
+ 0x00290012};
+
+ bool has_cpuid = false;
+ sigframe thisframe (mainthread);
+
+ DWORD opmask = SetThreadAffinityMask (GetCurrentThread (), 1);
+ if (!opmask)
+ debug_printf ("SetThreadAffinityMask to 1 failed, %E");
+
+ if (!can_set_flag (0x00040000))
+ debug_printf ("386 processor - no cpuid");
+ else
+ {
+ debug_printf ("486 processor");
+ if (can_set_flag (0x00200000))
+ {
+ debug_printf ("processor supports CPUID instruction");
+ has_cpuid = true;
+ }
+ else
+ debug_printf ("processor does not support CPUID instruction");
+ }
+ if (has_cpuid)
+ {
+ unsigned maxf, unused[3];
+ cpuid (&maxf, &unused[0], &unused[1], &unused[2], 0);
+ maxf &= 0xffff;
+ if (maxf >= 1)
+ {
+ unsigned features;
+ cpuid (&data[0], &unused[0], &unused[1], &features, 1);
+ if (features & (1 << 18))
+ {
+ debug_printf ("processor has psn");
+ if (maxf >= 3)
+ {
+ cpuid (&unused[0], &unused[1], &data[1], &data[2], 3);
+ debug_printf ("Processor PSN: %04x-%04x-%04x-%04x-%04x-%04x",
+ data[0] >> 16, data[0] & 0xffff, data[2] >> 16, data[2] & 0xffff, data[1] >> 16, data[1] & 0xffff);
+ }
+ }
+ else
+ debug_printf ("processor does not have psn");
+ }
+ }
+
+ UUID Uuid;
+ RPC_STATUS status = UuidCreateSequential (&Uuid);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ status = UuidCreate (&Uuid);
+ if (status == RPC_S_OK)
+ {
+ data[4] = *(unsigned *)&Uuid.Data4[2];
+ data[5] = *(unsigned short *)&Uuid.Data4[6];
+ // Unfortunately Windows will sometimes pick a virtual Ethernet card
+ // e.g. VMWare Virtual Ethernet Adaptor
+ debug_printf ("MAC address of first Ethernet card: %02x:%02x:%02x:%02x:%02x:%02x",
+ Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4],
+ Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]);
+ }
+ else
+ {
+ debug_printf ("no Ethernet card installed");
+ }
+
+ reg_key key (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", "Microsoft", "Windows", "CurrentVersion", NULL);
+ key.get_string ("ProductId", (char *)&data[6], 24, "00000-000-0000000-00000");
+ debug_printf ("Windows Product ID: %s", (char *)&data[6]);
+
+ /* Contrary to MSDN, NT4 requires the second argument
+ or a STATUS_ACCESS_VIOLATION is generated */
+ ULARGE_INTEGER availb;
+ GetDiskFreeSpaceEx ("C:\\", &availb, (PULARGE_INTEGER) &data[11], NULL);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ GetDiskFreeSpace ("C:\\", NULL, NULL, NULL, (DWORD *)&data[11]);
+
+ debug_printf ("hostid entropy: %08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x",
+ data[0], data[1],
+ data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[8], data[9],
+ data[10], data[11],
+ data[12]);
+
+ long hostid = 0x40291372;
+ // a random hashing algorithm
+ // dependancy on md5 is probably too costly
+ for (int i=0;i<13;i++)
+ hostid ^= ((data[i] << (i << 2)) | (data[i] >> (32 - (i << 2))));
+
+ if (opmask && !SetThreadAffinityMask (GetCurrentThread (), opmask))
+ debug_printf ("SetThreadAffinityMask to %p failed, %E", opmask);
+
+ debug_printf ("hostid: %08x", hostid);
+
+ return hostid;
+}
+
+#define ETC_SHELLS "/etc/shells"
+static int shell_index;
+static struct __sFILE64 *shell_fp;
+
+extern "C" char *
+getusershell ()
+{
+ /* List of default shells if no /etc/shells exists, defined as on Linux.
+ FIXME: SunOS has a far longer list, containing all shells which
+ might be shipped with the OS. Should we do the same for the Cygwin
+ distro, adding bash, tcsh, ksh, pdksh and zsh? */
+ static NO_COPY const char *def_shells[] = {
+ "/bin/sh",
+ "/bin/csh",
+ "/usr/bin/sh",
+ "/usr/bin/csh",
+ NULL
+ };
+ static char buf[MAX_PATH];
+ int ch, buf_idx;
+
+ if (!shell_fp && !(shell_fp = fopen64 (ETC_SHELLS, "rt")))
+ {
+ if (def_shells[shell_index])
+ return strcpy (buf, def_shells[shell_index++]);
+ return NULL;
+ }
+ /* Skip white space characters. */
+ while ((ch = getc (shell_fp)) != EOF && isspace (ch))
+ ;
+ /* Get each non-whitespace character as part of the shell path as long as
+ it fits in buf. */
+ for (buf_idx = 0;
+ ch != EOF && !isspace (ch) && buf_idx < MAX_PATH;
+ buf_idx++, ch = getc (shell_fp))
+ buf[buf_idx] = ch;
+ /* Skip any trailing non-whitespace character not fitting in buf. If the
+ path is longer than MAX_PATH, it's invalid anyway. */
+ while (ch != EOF && !isspace (ch))
+ ch = getc (shell_fp);
+ if (buf_idx)
+ {
+ buf[buf_idx] = '\0';
+ return buf;
+ }
+ return NULL;
+}
+
+extern "C" void
+setusershell ()
+{
+ if (shell_fp)
+ fseek (shell_fp, 0L, SEEK_SET);
+ shell_index = 0;
+}
+
+extern "C" void
+endusershell ()
+{
+ if (shell_fp)
+ fclose (shell_fp);
+ shell_index = 0;
+}
diff --git a/winsup/cygwin/termios.cc b/winsup/cygwin/termios.cc
new file mode 100644
index 00000000000..c82f771b7dd
--- /dev/null
+++ b/winsup/cygwin/termios.cc
@@ -0,0 +1,307 @@
+/* termios.cc: termios for WIN32.
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Doug Evans and Steve Chamberlain of Cygnus Support
+ dje@cygnus.com, sac@cygnus.com
+
+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 <signal.h>
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "cygwin/version.h"
+#include "perprocess.h"
+#include "sigproc.h"
+#include <sys/termios.h>
+
+/* tcsendbreak: POSIX 7.2.2.1 */
+extern "C" int
+tcsendbreak (int fd, int duration)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcsendbreak (duration);
+
+out:
+ syscall_printf ("%d = tcsendbreak (%d, %d)", res, fd, duration);
+ return res;
+}
+
+/* tcdrain: POSIX 7.2.2.1 */
+extern "C" int
+tcdrain (int fd)
+{
+ int res = -1;
+
+ termios_printf ("tcdrain");
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcdrain ();
+
+out:
+ syscall_printf ("%d = tcdrain (%d)", res, fd);
+ return res;
+}
+
+/* tcflush: POSIX 7.2.2.1 */
+extern "C" int
+tcflush (int fd, int queue)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if (queue != TCIFLUSH && queue != TCOFLUSH && queue != TCIOFLUSH)
+ set_errno (EINVAL);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcflush (queue);
+
+out:
+ termios_printf ("%d = tcflush (%d, %d)", res, fd, queue);
+ return res;
+}
+
+/* tcflow: POSIX 7.2.2.1 */
+extern "C" int
+tcflow (int fd, int action)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcflow (action);
+
+out:
+ syscall_printf ("%d = tcflow (%d, %d)", res, fd, action);
+ return res;
+}
+
+/* tcsetattr: POSIX96 7.2.1.1 */
+extern "C" int
+tcsetattr (int fd, int a, const struct termios *t)
+{
+ int res;
+ t = __tonew_termios (t);
+ int e = get_errno ();
+
+ while (1)
+ {
+ sigframe thisframe (mainthread);
+
+ res = -1;
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ e = get_errno ();
+ break;
+ }
+
+ if (!cfd->is_tty ())
+ {
+ e = ENOTTY;
+ break;
+ }
+
+ res = cfd->bg_check (-SIGTTOU);
+
+ switch (res)
+ {
+ case bg_eof:
+ e = get_errno ();
+ break;
+ case bg_ok:
+ if (cfd.isopen ())
+ res = cfd->tcsetattr (a, t);
+ e = get_errno ();
+ break;
+ case bg_signalled:
+ if (thisframe.call_signal_handler ())
+ continue;
+ res = -1;
+ /* fall through intentionally */
+ default:
+ e = get_errno ();
+ break;
+ }
+ break;
+ }
+
+ set_errno (e);
+ termios_printf ("iflag %p, oflag %p, cflag %p, lflag %p, VMIN %d, VTIME %d",
+ t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN],
+ t->c_cc[VTIME]);
+ termios_printf ("%d = tcsetattr (%d, %d, %x)", res, fd, a, t);
+ return res;
+}
+
+/* tcgetattr: POSIX 7.2.1.1 */
+extern "C" int
+tcgetattr (int fd, struct termios *in_t)
+{
+ int res = -1;
+ struct termios *t = __makenew_termios (in_t);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->tcgetattr (t)) == 0)
+ (void) __toapp_termios (in_t, t);
+
+ if (res)
+ termios_printf ("%d = tcgetattr (%d, %p)", res, fd, in_t);
+ else
+ termios_printf ("iflag %x, oflag %x, cflag %x, lflag %x, VMIN %d, VTIME %d",
+ t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN],
+ t->c_cc[VTIME]);
+
+ return res;
+}
+
+/* tcgetpgrp: POSIX 7.2.3.1 */
+extern "C" int
+tcgetpgrp (int fd)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else
+ res = cfd->tcgetpgrp ();
+
+ termios_printf ("%d = tcgetpgrp (%d)", res, fd);
+ return res;
+}
+
+/* tcsetpgrp: POSIX 7.2.4.1 */
+extern "C" int
+tcsetpgrp (int fd, pid_t pgid)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else
+ res = cfd->tcsetpgrp (pgid);
+
+ termios_printf ("%d = tcsetpgrp (%d, %x)", res, fd, pgid);
+ return res;
+}
+
+/* NIST PCTS requires not macro-only implementation */
+#undef cfgetospeed
+#undef cfgetispeed
+#undef cfsetospeed
+#undef cfsetispeed
+
+/* cfgetospeed: POSIX96 7.1.3.1 */
+extern "C" speed_t
+cfgetospeed (struct termios *tp)
+{
+ return __tonew_termios (tp)->c_ospeed;
+}
+
+/* cfgetispeed: POSIX96 7.1.3.1 */
+extern "C" speed_t
+cfgetispeed (struct termios *tp)
+{
+ return __tonew_termios (tp)->c_ispeed;
+}
+
+static inline int
+setspeed (speed_t &set_speed, speed_t from_speed)
+{
+ int res;
+ switch (from_speed)
+ {
+ case B0:
+ case B50:
+ case B75:
+ case B110:
+ case B134:
+ case B150:
+ case B200:
+ case B300:
+ case B600:
+ case B1200:
+ case B1800:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B128000:
+ case B230400:
+ case B256000:
+ set_speed = from_speed;
+ res = 0;
+ break;
+ default:
+ set_errno (EINVAL);
+ res = -1;
+ break;
+ }
+ return res;
+}
+
+/* cfsetospeed: POSIX96 7.1.3.1 */
+extern "C" int
+cfsetospeed (struct termios *in_tp, speed_t speed)
+{
+ struct termios *tp = __tonew_termios (in_tp);
+ int res = setspeed (tp->c_ospeed, speed);
+ (void) __toapp_termios (in_tp, tp);
+ return res;
+}
+
+/* cfsetispeed: POSIX96 7.1.3.1 */
+extern "C" int
+cfsetispeed (struct termios *in_tp, speed_t speed)
+{
+ struct termios *tp = __tonew_termios (in_tp);
+ int res = setspeed (tp->c_ispeed, speed);
+ (void) __toapp_termios (in_tp, tp);
+ return res;
+}