diff options
Diffstat (limited to 'winsup/cygwin/dtable.cc')
-rw-r--r-- | winsup/cygwin/dtable.cc | 485 |
1 files changed, 320 insertions, 165 deletions
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 96ee5a503..9fc6f4e21 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -1,6 +1,6 @@ /* dtable.cc: file descriptor support. - Copyright 1996, 1997, 1998, 1999, 2000, 2001 Red Hat, Inc. + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. This file is part of Cygwin. @@ -16,13 +16,13 @@ details. */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <fcntl.h> #include <sys/cygwin.h> +#include <assert.h> +#include <ntdef.h> +#include <winnls.h> #define USE_SYS_TYPES_FD_SET #include <winsock.h> -#include "sync.h" -#include "sigproc.h" #include "pinfo.h" #include "cygerrno.h" #include "perprocess.h" @@ -31,9 +31,12 @@ details. */ #include "path.h" #include "dtable.h" #include "cygheap.h" +#include "ntdll.h" static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, - STD_ERROR_HANDLE}; + STD_ERROR_HANDLE}; + +static char *handle_to_fn (HANDLE, char *); /* Set aside space for the table of fds */ void @@ -55,7 +58,8 @@ set_std_handle (int fd) void dtable::dec_console_fds () { - if (console_fds > 0 && !--console_fds && myself->ctty != TTY_CONSOLE) + if (console_fds > 0 && !--console_fds && + myself->ctty != TTY_CONSOLE && !check_pty_fds()) FreeConsole (); } @@ -88,59 +92,84 @@ dtable::extend (int howmuch) return 1; } +void +dtable::get_debugger_info () +{ + if (being_debugged ()) + { + char std[3][sizeof ("/dev/ttyNNNN")]; + std[0][0] = std[1][0] = std [2][0] = '\0'; + char buf[sizeof ("cYgstd %x") + 32]; + sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3); + OutputDebugString (buf); + for (int i = 0; i < 3; i++) + if (std[i][0]) + { + path_conv pc; + HANDLE h = GetStdHandle (std_consts[i]); + fhandler_base *fh = build_fhandler_from_name (i, std[i], NULL, pc); + if (!fh) + continue; + if (!fh->open (&pc, (i ? O_WRONLY : O_RDONLY) | O_BINARY, 0777)) + release (i); + else + CloseHandle (h); + } + } +} + /* Initialize the file descriptor/handle mapping table. - We only initialize the parent table here. The child table is - initialized at each fork () call. */ + This function should only be called when a cygwin function is invoked + by a non-cygwin function, i.e., it should only happen very rarely. */ void -stdio_init (void) +dtable::stdio_init () { extern void set_console_ctty (); /* Set these before trying to output anything from strace. Also, always set them even if we're to pick up our parent's fds in case they're missed. */ - if (!myself->ppid_handle && NOTSTATE (myself, PID_CYGPARENT)) - { - HANDLE in = GetStdHandle (STD_INPUT_HANDLE); - HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); - HANDLE err = GetStdHandle (STD_ERROR_HANDLE); + if (myself->ppid_handle || ISSTATE (myself, PID_CYGPARENT)) + return; - cygheap->fdtab.init_std_file_from_handle (0, in, GENERIC_READ); + HANDLE in = GetStdHandle (STD_INPUT_HANDLE); + HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); + HANDLE err = GetStdHandle (STD_ERROR_HANDLE); - /* STD_ERROR_HANDLE has been observed to be the same as - STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes - to pass data from child to parent). */ - if (out == err) + init_std_file_from_handle (0, in, GENERIC_READ); + + /* STD_ERROR_HANDLE has been observed to be the same as + STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes + to pass data from child to parent). */ + if (out == err) + { + /* Since this code is not invoked for forked tasks, we don't have + to worry about the close-on-exec flag here. */ + if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0, + 1, DUPLICATE_SAME_ACCESS)) { - /* Since this code is not invoked for forked tasks, we don't have - to worry about the close-on-exec flag here. */ - if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0, - 1, DUPLICATE_SAME_ACCESS)) - { - /* If that fails, do this as a fall back. */ - err = out; - system_printf ("couldn't make stderr distinct from stdout"); - } + /* If that fails, do this as a fall back. */ + err = out; + system_printf ("couldn't make stderr distinct from stdout"); } - - cygheap->fdtab.init_std_file_from_handle (1, out, GENERIC_WRITE); - cygheap->fdtab.init_std_file_from_handle (2, err, GENERIC_WRITE); - /* Assign the console as the controlling tty for this process if we actually - have a console and no other controlling tty has been assigned. */ - if (myself->ctty < 0 && GetConsoleCP () > 0) - set_console_ctty (); } + + init_std_file_from_handle (1, out, GENERIC_WRITE); + init_std_file_from_handle (2, err, GENERIC_WRITE); + /* Assign the console as the controlling tty for this process if we actually + have a console and no other controlling tty has been assigned. */ + if (myself->ctty < 0 && GetConsoleCP () > 0) + set_console_ctty (); } int dtable::find_unused_handle (int start) { AssertResourceOwner (LOCK_FD_LIST, READ_LOCK); - do { - for (int i = start; i < (int) size; i++) + for (size_t i = start; i < size; i++) /* See if open -- no need for overhead of not_open */ if (fds[i] == NULL) return i; @@ -168,189 +197,217 @@ dtable::release (int fd) } } -void -dtable::init_std_file_from_handle (int fd, HANDLE handle, - DWORD myaccess) -{ - int bin; - const char *name = NULL; - - if (__fmode) - bin = __fmode; - else - bin = binmode ?: 0; - - first_fd_for_open = 0; - /* See if we can consoleify it - if it is a console, - don't open it in binary. That will screw up our crlfs*/ - CONSOLE_SCREEN_BUFFER_INFO buf; - if (GetConsoleScreenBufferInfo (handle, &buf)) - { - if (ISSTATE (myself, PID_USETTY)) - name = "/dev/tty"; - else - name = "/dev/conout"; - bin = 0; - } - else if (FlushConsoleInputBuffer (handle)) - { - if (ISSTATE (myself, PID_USETTY)) - name = "/dev/tty"; - else - name = "/dev/conin"; - bin = 0; - } - else if (GetFileType (handle) == FILE_TYPE_PIPE) - { - if (fd == 0) - name = "/dev/piper"; - else if (fd == 1 || fd == 2) - name = "/dev/pipew"; - if (bin == 0) - bin = O_BINARY; - } - - path_conv pc; - build_fhandler (fd, name, handle, pc)->init (handle, myaccess, bin); - set_std_handle (fd); - paranoid_printf ("fd %d, handle %p", fd, handle); -} - -extern "C" -int +extern "C" int cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin, DWORD myaccess) { if (fd == -1) fd = cygheap->fdtab.find_unused_handle (); path_conv pc; - fhandler_base *res = cygheap->fdtab.build_fhandler (fd, name, handle, pc); - res->init (handle, myaccess, bin); + fhandler_base *res = cygheap->fdtab.build_fhandler_from_name (fd, name, handle, + pc); + res->init (handle, myaccess, bin ?: pc.binmode ()); return fd; } -fhandler_base * -dtable::build_fhandler (int fd, const char *name, HANDLE handle, path_conv& pc, - unsigned opt, suffix_info *si) +void +dtable::init_std_file_from_handle (int fd, HANDLE handle, DWORD myaccess) { - if (!name && handle) + const char *name; + CONSOLE_SCREEN_BUFFER_INFO buf; + struct sockaddr sa; + int sal = sizeof (sa); + DCB dcb; + + first_fd_for_open = 0; + + if (!not_open (fd)) + return; + + SetLastError (0); + DWORD ft = GetFileType (handle); + if (ft == FILE_TYPE_UNKNOWN && GetLastError () == ERROR_INVALID_HANDLE) + name = NULL; + else { - struct sockaddr sa; - int sal = sizeof (sa); - CONSOLE_SCREEN_BUFFER_INFO cinfo; - DCB dcb; - - if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &cinfo)) - name = "/dev/conin"; - else if (GetConsoleScreenBufferInfo (handle, &cinfo)) - name = "/dev/conout"; + /* See if we can consoleify it */ + if (GetConsoleScreenBufferInfo (handle, &buf)) + { + if (ISSTATE (myself, PID_USETTY)) + name = "/dev/tty"; + else + name = "/dev/conout"; + } + else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf)) + { + if (ISSTATE (myself, PID_USETTY)) + name = "/dev/tty"; + else + name = "/dev/conin"; + } + else if (ft == FILE_TYPE_PIPE) + { + if (fd == 0) + name = "/dev/piper"; + else + name = "/dev/pipew"; + } else if (wsock_started && getpeername ((SOCKET) handle, &sa, &sal) == 0) name = "/dev/socket"; - else if (GetFileType (handle) == FILE_TYPE_PIPE) - name = "/dev/pipe"; else if (GetCommState (handle, &dcb)) name = "/dev/ttyS0"; // FIXME - determine correct device else - name = "some disk file"; + name = handle_to_fn (handle, (char *) alloca (MAX_PATH + 100)); + } + + if (!name) + fds[fd] = NULL; + else + { + path_conv pc; + build_fhandler_from_name (fd, name, handle, pc)->init (handle, myaccess, + pc.binmode ()); + set_std_handle (fd); + paranoid_printf ("fd %d, handle %p", fd, handle); } +} - pc.check (name, opt | PC_NULLEMPTY, si); +fhandler_base * +dtable::build_fhandler_from_name (int fd, const char *name, HANDLE handle, + path_conv& pc, unsigned opt, suffix_info *si) +{ + pc.check (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si); if (pc.error) { set_errno (pc.error); return NULL; } - fhandler_base *fh = build_fhandler (fd, pc.get_devn (), name, pc.get_unitn ()); - fh->set_name (name, pc, pc.get_unitn ()); + if (!pc.exists () && handle) + pc.fillin (handle); + + fhandler_base *fh = build_fhandler (fd, pc.get_devn (), + pc.return_and_clear_normalized_path (), + pc, pc.get_unitn ()); return fh; } fhandler_base * -dtable::build_fhandler (int fd, DWORD dev, const char *name, int unit) +dtable::build_fhandler (int fd, DWORD dev, const char *unix_name, + const char *win32_name, int unit) +{ + return build_fhandler (fd, dev, cstrdup (unix_name), win32_name, unit); +} + +#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name +fhandler_base * +dtable::build_fhandler (int fd, DWORD dev, char *unix_name, + const char *win32_name, int unit) { fhandler_base *fh; - void *buf = ccalloc (HEAP_FHANDLER, 1, sizeof (fhandler_union) + 100); dev &= FH_DEVMASK; switch (dev) { case FH_TTYM: - fh = new (buf) fhandler_tty_master (name, unit); + fh = cnew (fhandler_tty_master) (unit); break; case FH_CONSOLE: case FH_CONIN: case FH_CONOUT: - fh = new (buf) fhandler_console (name); - inc_console_fds (); + if ((fh = cnew (fhandler_console) ())) + inc_console_fds (); break; case FH_PTYM: - fh = new (buf) fhandler_pty_master (name); + fh = cnew (fhandler_pty_master) (); break; case FH_TTYS: if (unit < 0) - fh = new (buf) fhandler_tty_slave (name); + fh = cnew (fhandler_tty_slave) (); else - fh = new (buf) fhandler_tty_slave (unit, name); + fh = cnew (fhandler_tty_slave) (unit); break; case FH_WINDOWS: - fh = new (buf) fhandler_windows (name); + fh = cnew (fhandler_windows) (); break; case FH_SERIAL: - fh = new (buf) fhandler_serial (name, dev, unit); + fh = cnew (fhandler_serial) (unit); break; case FH_PIPE: case FH_PIPER: case FH_PIPEW: - fh = new (buf) fhandler_pipe (name, dev); + fh = cnew (fhandler_pipe) (dev); break; case FH_SOCKET: - fh = new (buf) fhandler_socket (name); + if ((fh = cnew (fhandler_socket) ())) + inc_need_fixup_before (); break; case FH_DISK: - fh = new (buf) fhandler_disk_file (NULL); + fh = cnew (fhandler_disk_file) (); + break; + case FH_CYGDRIVE: + fh = cnew (fhandler_cygdrive) (unit); break; case FH_FLOPPY: - fh = new (buf) fhandler_dev_floppy (name, unit); + fh = cnew (fhandler_dev_floppy) (unit); break; case FH_TAPE: - fh = new (buf) fhandler_dev_tape (name, unit); + fh = cnew (fhandler_dev_tape) (unit); break; case FH_NULL: - fh = new (buf) fhandler_dev_null (name); + fh = cnew (fhandler_dev_null) (); break; case FH_ZERO: - fh = new (buf) fhandler_dev_zero (name); + fh = cnew (fhandler_dev_zero) (); break; case FH_RANDOM: - fh = new (buf) fhandler_dev_random (name, unit); + fh = cnew (fhandler_dev_random) (unit); break; case FH_MEM: - fh = new (buf) fhandler_dev_mem (name, unit); + fh = cnew (fhandler_dev_mem) (unit); break; case FH_CLIPBOARD: - fh = new (buf) fhandler_dev_clipboard (name); + fh = cnew (fhandler_dev_clipboard) (); break; case FH_OSS_DSP: - fh = new (buf) fhandler_dev_dsp (name); + fh = cnew (fhandler_dev_dsp) (); + break; + case FH_PROC: + fh = cnew (fhandler_proc) (); + break; + case FH_REGISTRY: + fh = cnew (fhandler_registry) (); + break; + case FH_PROCESS: + fh = cnew (fhandler_process) (); break; default: + system_printf ("internal error -- unknown device - %p", dev); + fh = NULL; + } + + if (unix_name) + { + char new_win32_name[strlen (unix_name) + 1]; + if (!win32_name) { - /* FIXME - this could recurse forever */ - path_conv pc; - return build_fhandler (fd, name, NULL, pc); + char *p; + /* FIXME: ? Should we call win32_device_name here? + It seems like overkill, but... */ + win32_name = strcpy (new_win32_name, unix_name); + for (p = (char *) win32_name; (p = strchr (p, '/')); p++) + *p = '\\'; } + fh->set_name (unix_name, win32_name, fh->get_unit ()); } - - debug_printf ("%s - cb %d, fd %d, fh %p", fh->get_name () ?: "", fh->cb, - fd, fh); + debug_printf ("fd %d, fh %p", fd, fh); return fd >= 0 ? (fds[fd] = fh) : fh; } fhandler_base * dtable::dup_worker (fhandler_base *oldfh) { - fhandler_base *newfh = build_fhandler (-1, oldfh->get_device (), NULL); + fhandler_base *newfh = build_fhandler (-1, oldfh->get_device ()); *newfh = *oldfh; newfh->set_io_handle (NULL); if (oldfh->dup (newfh)) @@ -374,6 +431,7 @@ dtable::dup2 (int oldfd, int newfd) MALLOC_CHECK; debug_printf ("dup2 (%d, %d)", oldfd, newfd); + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); if (not_open (oldfd)) { @@ -382,6 +440,13 @@ dtable::dup2 (int oldfd, int newfd) goto done; } + if (newfd < 0) + { + syscall_printf ("new fd out of bounds: %d", newfd); + set_errno (EBADF); + goto done; + } + if (newfd == oldfd) { res = 0; @@ -394,51 +459,33 @@ dtable::dup2 (int oldfd, int newfd) goto done; } - SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); + debug_printf ("newfh->io_handle %p, oldfh->io_handle %p", + newfh->get_io_handle (), fds[oldfd]->get_io_handle ()); - if (newfd < 0) + if (!not_open (newfd)) + _close (newfd); + else if ((size_t) newfd < size) + /* nothing to do */; + else if (find_unused_handle (newfd) < 0) { - syscall_printf ("new fd out of bounds: %d", newfd); - set_errno (EBADF); + newfh->close (); + res = -1; goto done; } - if ((size_t) newfd >= cygheap->fdtab.size) - { - int inc_size = NOFILE_INCR * ((newfd + NOFILE_INCR - 1) / NOFILE_INCR) - - cygheap->fdtab.size; - cygheap->fdtab.extend (inc_size); - } - - if (!not_open (newfd)) - _close (newfd); fds[newfd] = newfh; - /* Count sockets. */ - if ((fds[newfd]->get_device () & FH_DEVMASK) == FH_SOCKET) - inc_need_fixup_before (); - - ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); - MALLOC_CHECK; - if ((res = newfd) <= 2) set_std_handle (res); - MALLOC_CHECK; done: + MALLOC_CHECK; + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd); return res; } -void -dtable::reset_unix_path_name (int fd, const char *name) -{ - SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "reset_unix_name"); - fds[fd]->reset_unix_path_name (name); - ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "reset_unix_name"); -} - select_record * dtable::select_read (int fd, select_record *s) { @@ -569,6 +616,10 @@ dtable::vfork_child_dup () newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0])); int res = 1; + /* Remove impersonation */ + if (cygheap->user.issetuid ()) + RevertToSelf (); + for (size_t i = 0; i < size; i++) if (not_open (i)) continue; @@ -585,6 +636,10 @@ dtable::vfork_child_dup () fds = newtable; out: + /* Restore impersonation */ + if (cygheap->user.issetuid ()) + ImpersonateLoggedOnUser (cygheap->user.token); + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); return 1; } @@ -596,6 +651,7 @@ dtable::vfork_parent_restore () close_all_files (); fhandler_base **deleteme = fds; + assert (fds_on_hold != NULL); fds = fds_on_hold; fds_on_hold = NULL; cfree (deleteme); @@ -633,3 +689,102 @@ dtable::vfork_child_fixup () return; } + +#define DEVICE_PREFIX "\\device\\" +#define DEVICE_PREFIX_LEN sizeof(DEVICE_PREFIX) - 1 +#define REMOTE "\\Device\\LanmanRedirector\\" +#define REMOTE_LEN sizeof (REMOTE) - 1 + +static char * +handle_to_fn (HANDLE h, char *posix_fn) +{ + OBJECT_NAME_INFORMATION *ntfn; + char fnbuf[32768]; + + memset (fnbuf, 0, sizeof (fnbuf)); + ntfn = (OBJECT_NAME_INFORMATION *) fnbuf; + ntfn->Name.MaximumLength = sizeof (fnbuf) - sizeof (*ntfn); + ntfn->Name.Buffer = (WCHAR *) (ntfn + 1); + + DWORD res = NtQueryObject (h, ObjectNameInformation, ntfn, sizeof (fnbuf), NULL); + + if (res) + { + strcpy (posix_fn, "some disk file"); + debug_printf ("NtQueryObject failed"); + return posix_fn; + } + + // NT seems to do this on an unopened file + if (!ntfn->Name.Buffer) + { + debug_printf ("nt->Name.Buffer == NULL"); + return NULL; + } + + ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = 0; + + char win32_fn[MAX_PATH + 100]; + sys_wcstombs (win32_fn, ntfn->Name.Buffer, ntfn->Name.Length); + debug_printf ("nt name '%s'", win32_fn); + if (!strncasematch (win32_fn, DEVICE_PREFIX, DEVICE_PREFIX_LEN) + || !QueryDosDevice (NULL, fnbuf, sizeof (fnbuf))) + return strcpy (posix_fn, win32_fn); + + char *p = strchr (win32_fn + DEVICE_PREFIX_LEN, '\\'); + if (!p) + p = strchr (win32_fn + DEVICE_PREFIX_LEN, '\0'); + + int n = p - win32_fn; + int maxmatchlen = 0; + char *maxmatchdos = NULL; + for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1) + { + char device[MAX_PATH + 10]; + device[MAX_PATH + 9] = '\0'; + if (strchr (s, ':') == NULL) + continue; + if (!QueryDosDevice (s, device, sizeof (device) - 1)) + continue; + char *q = strrchr (device, ';'); + if (q) + { + char *r = strchr (q, '\\'); + if (r) + strcpy (q, r + 1); + } + int devlen = strlen (device); + if (device[devlen - 1] == '\\') + device[--devlen] = '\0'; + if (devlen < maxmatchlen) + continue; + if (!strncasematch (device, win32_fn, devlen) || + (win32_fn[devlen] != '\0' && win32_fn[devlen] != '\\')) + continue; + maxmatchlen = devlen; + maxmatchdos = s; + debug_printf ("current match '%s'", device); + } + + char *w32 = win32_fn; + if (maxmatchlen) + { + n = strlen (maxmatchdos); + if (maxmatchdos[n - 1] == '\\') + n--; + w32 += maxmatchlen - n; + memcpy (w32, maxmatchdos, n); + w32[n] = '\\'; + } + else if (strncasematch (w32, REMOTE, REMOTE_LEN)) + { + w32 += REMOTE_LEN - 2; + *w32 = '\\'; + debug_printf ("remote drive"); + } + + + debug_printf ("derived path '%s'", w32); + cygwin_conv_to_full_posix_path (w32, posix_fn); + return posix_fn; +} |