From b4dc8397e9b84c28f375daee6cbe268d48c92a97 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 13 Nov 2014 12:53:12 +0000 Subject: [PATCH] 2014-11-07 Corinna Vinschen * dcrt0.cc (cygwin__cxa_atexit): Fetch correct DSO handle value by searching dll list. Explain why. 2014-11-06 Corinna Vinschen * dcrt0.cc (cygwin_atexit): Change preceeding comment to reflect API version numbers. * external.cc (cygwin_internal): disable setting cxx_malloc on 64 bit. Add CW_FIXED_ATEXIT case. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. * include/sys/cygwin.h (cygwin_getinfo_types): Add CW_FIXED_ATEXIT. * lib/atexit.c (atexit): Test running Cygwin version by checking return value of cygwin_internal (CW_FIXED_ATEXIT). 2014-11-05 Corinna Vinschen * lib/atexit.c (atexit): Check for being linked into the executable. If so, call __cxa_atexit with NULL DSO handle. Explain why. * lib/dso_handle.c: New file providing fallback __dso_handle. 2014-11-05 Corinna Vinschen * Makefile.in (NEW_FUNCTIONS): Add atexit to be not exported. * lib/atexit.c (atexit): New, statically linkable version of atexit. * dcrt0.cc (cygwin_atexit): Add comment to mark this function as old entry point. Indiscriminately check for DSO of function pointer for all functions, if checking for DSO of return address fails on x86_64. Change comment accordingly. 2014-11-05 Corinna Vinschen * Makefile.in (NEW_FUNCTIONS): Define target-independent. Add target dependent stuff afterwards. Globally define timezone and all xdr symbols as non-exported from libcygwin.a. 2014-11-03 Corinna Vinschen * cygheap.cc: Fix formatting. 2014-10-29 Corinna Vinschen * cygheap.cc (init_cygheap::init_installation_root): Create content of installation_dir as non-prefixed path, if possible. 2014-10-29 Corinna Vinschen * common.din (__cxa_atexit): Define as cygwin__cxa_atexit. * dcrt0.cc (cygwin__cxa_atexit): New function. Explain what we do. 2014-10-28 Corinna Vinschen * globals.cc (dos_file_warning): Set to false by default. * path.cc (warn_msdos): Make static. Drop test for dos_file_warning. (path_conv::check): Check for dos_file_warning here to avoid a function call in the default case. 2014-10-27 Corinna Vinschen * dcrt0.cc (cygwin_atexit): Add workaround for broken atexit calls in __gcc_register_frame of DLLs built with gcc-4.8.3-3. 2014-10-27 Corinna Vinschen * cygheap.cc (init_cygheap::init_installation_root): Set installation_dir_len. * cygheap.h (struct init_cygheap): Add installation_dir_len member. * environ.cc (win_env::add_cache): Use stpcpy for speed. (posify_maybe): Use tmp_pathbuf buffer instead of stack. (raise_envblock): New function to resize Windows environment block. (build_env): Fix indentation. Call raise_envblock function. Check if $PATH exists and is non-empty. If not, add PATH variable with Cygwin installation directory as content to Windows environment. Explain why. 2014-10-24 Corinna Vinschen * fhandler_proc.cc (format_proc_cygdrive): Fix symlink path if cygdrive is "/". 2014-10-24 Corinna Vinschen * gendef (sigdelayed): 64 bit only: Fix seh_pushreg statements in prologue. 2014-10-22 Yaakov Selkowitz * common.din (stime): Export. * times.cc (stime): New function. * include/cygwin/time.h (stime): Declare. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. 2014-10-20 Corinna Vinschen * fhandler_serial.cc: Revert debug code accidentally checked in on 2014-08-18. 2014-10-20 Corinna Vinschen * path.cc (symlink_info::check): Set error from status code if opening the parent directory failed, but keep special case for root dirs. 2014-10-17 Corinna Vinschen * cygheap.cc (init_cygheap::init_installation_root): Just memmove contents of installation_root instead of calling GetModuleFileNameW again. Copy installation_root to installation_dir before stripping of "bin" dir. Explain what we do. * cygheap.h (struct init_cygheap): Add installation_dir member. 2014-10-17 Corinna Vinschen * cygtls.h (__try): Define __l_endtry as block-local label in 32 bit case as well. 2014-10-17 Corinna Vinschen * Makefile.in (DLL_OFILES): Add quotactl.o. * common.din (quotactl): Export. * ntdll.h: Define FILE_FS_CONTROL_INFORMATION::FileSystemControlFlags flag values. (struct _FILE_FS_CONTROL_INFORMATION): Define. (struct _FILE_GET_QUOTA_INFORMATION): Define. (typedef struct _FILE_QUOTA_INFORMATION): Define. (NtQueryObject): Use PVOID rather than VOID*. (NtQueryVolumeInformationFile): Ditto. (NtQueryQuotaInformationFile): Declare. (NtSetQuotaInformationFile): Declare. (NtSetVolumeInformationFile): Declare. * quotactl.cc: New file implementing quotactl(). * include/sys/mount.h (BLOCK_SIZE): Define. (BLOCK_SIZE_BITS): Define. * include/sys/quota.h: New header. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. 2014-10-16 Corinna Vinschen * Makefile.in (DLL_OFILES): Rearrange with one file per line. 2014-10-16 Corinna Vinschen * fhandler_proc.cc (format_proc_partitions): Extend output to print the windows mount points the device is mounted on. 2014-10-13 Corinna Vinschen * net.cc (cygwin_setsockopt): Drop redundant test for AF_LOCAL and SOCK_STREAM in SO_PEERCRED case, as in the original patch. 2014-10-12 Corinna Vinschen * dlfcn.cc (gfpod_helper): Only check for POSIX dir separator, same as in get_full_path_of_dll. 2014-10-12 Corinna Vinschen * dlfcn.cc (set_dl_error): Drop useless __stdcall. (check_path_access): Ditto. Drop FE_CWD from call to find_exec. (gfpod_helper): Call path_conv::check for all paths containing a dir separator to more closely follow the Linux search algorithm. (get_full_path_of_dll): Drop useless __stdcall. (dlopen): Simplify RTLD_NOLOAD case by calling GetModuleHandleEx instead of GetModuleHandle/LoadLibrary. 2014-10-11 Christian Franke Add setsockopt(sd, SOL_SOCKET, SO_PEERCRED, NULL, 0) to disable initial handshake on AF_LOCAL sockets. * fhandler.h (class fhandler_socket): Add no_getpeereid status flag. (fhandler_socket::af_local_set_no_getpeereid): New prototype. * fhandler_socket.cc (fhandler_socket::af_local_connect): Skip handshake if no_getpeereid is set. Add debug output. (fhandler_socket::af_local_accept): Likewise. (fhandler_socket::af_local_set_no_getpeereid): New function. (fhandler_socket::af_local_copy): Copy no_getpeereid. (fhandler_socket::getpeereid): Fail if no_getpeereid is set. * net.cc (cygwin_setsockopt): Add SO_PEERCRED for AF_LOCAL/SOCK_STREAM sockets. Add comment to explain why we need it. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. 2014-10-10 Corinna Vinschen * dlfcn.cc (dlopen): Disable old 32 bit code on 64 bit. * dcrt0.cc (check_sanity_and_sync): Ditto. * dll_init.cc (dll_dllcrt0_1): Fix typo in comment. 2014-10-08 Corinna Vinschen * common.din (ffsl): Export. (ffsll): Export. * syscalls.cc (ffs): Implement using GCC intrinsic. (ffsl): Ditto. (ffsll): Ditto. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. 2014-10-08 Christian Franke * syscalls.cc (ffs): Fix crash of ffs (0x80000000) on 64 bit. 2014-10-08 Corinna Vinschen * fhandler_process.cc (format_process_statm): Fix output of dirty pages. Add linefeed. 2014-09-05 Corinna Vinschen * fhandler_proc.cc (proc_tab): Add entry for cygdrive symlink. (format_proc_cygdrive): New function to implement /proc/cygdrive. 2014-09-05 Corinna Vinschen * exception.h (class exception): Remove unnecessary #ifdef. 2014-09-05 Corinna Vinschen * winlean.h (DNLEN): Raise to 31. Explain why. 2014-08-28 Corinna Vinschen * fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Try the FileFsSizeInformation information class on filesystems choking on FileFsFullSizeInformation (I see you Netapp!) 2014-08-27 Corinna Vinschen * fhandler.h (fhandler_pty_slave::fch_open_handles): Add bool parameter to declaration. * fhandler_tty.cc (fhandler_pty_slave::fch_open_handles): Add bool parameter "chown". Only request WRITE_OWNER access when opening pty synchronization objects if "chown" is set. (fhandler_pty_slave::fchmod): Call fch_open_handles with new bool parameter set to false. (fhandler_pty_slave::fchown): Call fch_open_handles with new bool parameter set to true. * kernel32.cc (CreateFileMappingW): Fix default standard rights for file mappings from READ_CONTROL to STANDARD_RIGHTS_REQUIRED to allow changing the DACL (fixes "access denied" error in pinfo::set_acl). * fhandler_disk_file.cc (fhandler_base::fstat_helper): Change debug output to print mode bits in octal. * security.cc (alloc_sd): Ditto. (set_file_attribute): Ditto. 2014-08-27 Corinna Vinschen * ntea.cc (read_ea): Change left-over return to __leave. Fix condition to close handle. Call NtClose rather than CloseHandle. (write_ea): Fix condition to close handle. Call NtClose rather than CloseHandle. * security.cc (get_file_sd): Call pc.init_reopen_attr if a valid incoming handle was given, pc.get_object_attr otherwise. (set_file_sd): Ditto. 2014-08-26 Corinna Vinschen * path.h (path_conv::init_reopen_attr): Change from void to returning POBJECT_ATTRIBUTES. Take OBJECT_ATTRIBUTES reference as argument, not pointer. * fhandler_disk_file.cc: Throughout accommodate above change. * syscalls.cc: Ditto. * ntea.cc (read_ea): Don't set hdl to NULL if it's already NULL. Set attr with pc.init_reopen_attr before trying to reopen file. (write_ea): Ditto. * security.cc (get_file_sd): Use pc.init_reopen_attr rather than pc.get_object_attr when trying to reopen file. (set_file_sd): Ditto. 2014-08-25 Corinna Vinschen * cygtls.cc (san::leave/x86_64): Implement. * cygtls.h (class tls_pathbuf): Move counter variables into a union. Add 64 bit element _counters covering both counter variables to optimize save and restore operations. (class san/x86_64): Only store single 64 bit value. (san::san/x86_64): Implement. (san::leave/x86_64): Only declare here, as returns_twice function. Explain why. (class san/i686): Change type of _c_cnt and _w_cnt to uint32_t. (__try/x86_64): Move definition of __sebastian after the first memory barrier. Drop __sebastian.setup call. 2014-08-25 Corinna Vinschen * cygtls.cc (_cygtls::remove): Revert previous patch. * cygtls.h (struct _local_storage): Move pathbufs back here. (class san/x86_64): Revert class. Save and restore pathbufs counters only. (class san/i686): Revert saving and restoring pathbufs counters. (__try/x86_64): Add a san variable and call it's setup method. (__except/x86_64): Call san::leave to restore pathbufs counters. * gendef (_sigbe): Revert previous change. * thread.cc (verifyable_object_state): Remove gcc 4.7 workaround in forward declaration as well. * tls_pbuf.cc (tls_pbuf): Revert previous change. * tls_pbuf.h (class tmp_pathbuf): Accommodate reverting pathbufs to locals structure. * tlsoffsets.h: Regenerate. * tlsoffsets64.h: Regenerate. 2014-08-21 Corinna Vinschen * Throughout, use __try/__except/__endtry blocks, rather than myfault handler. * cygtls.cc (_cygtls::remove): Accommodate the fact that pathbufs has been moved from _local_storage to _cygtls. * cygtls.h (class tls_pathbuf): Add comment to hint to gendef usage of counters. Change type of counters to uint32_t for clarity. Remove _cygtls as friend class. (struct _local_storage): Move pathbufs from here... (struct _cygtls): ...to here, allowing to access it from _sigbe. (class san): Only define on 32 bit. Remove errno, _c_cnt and _w_cnt members. (san::setup): Drop parameter. Don't initialize removed members. (san::leave): Don't set removed members. (class myfault): Only define on 32 bit. (myfault::faulted): Only keep implementation not taking any parameter. Drop argument in call to sebastian.setup. (__try/__leave/__except/__endtry): Implement to support real SEH. For now stick to SJLJ on 32 bit. * dcrt0.cc (dll_crt0_0): Drop 64 bit call to exception::install_myfault_handler. * exception.h (exception_handler): Define with EXCEPTION_DISPOSITION as return type. (PDISPATCHER_CONTEXT): Define as void * on 32 bit. Define as pointer to _DISPATCHER_CONTEXT on 64 bit. (class exception): Define separately for 32 and 64 bit. (exception::myfault): Add handler for myfault SEH handling on 64 bit. (exception::exception): Fix mangled method name to account for change in type of last parameter. (exception::install_myfault_handler): Remove. * exceptions.cc (exception::myfault_handle): Remove. (exception::myfault): New SEH handler for 64 bit. * gendef (_sigbe): Set tls_pathbuf counters to 0 explicitely when returning to the caller. * ntdll.h: Move a comment to a better place. (struct _SCOPE_TABLE): Define on 64 bit. * thread.cc (verifyable_object_isvalid): Remove gcc 4.7 workaround. * tls_pbuf.cc (tls_pbuf): Fix to accommodate new place of pathbufs. (tls_pathbuf::destroy): Change type of loop variables to uint32_t. * tls_pbuf.h (class tmp_pathbuf): Change type of buffer counters to uint32_t. Accommodate new place of pathbufs. * tlsoffsets.h: Regenerate. * tlsoffsets64.h: Regenerate. 2014-08-21 Corinna Vinschen * miscfuncs.cc (__import_address): Cover the first dereference to imp under the fault handler. 2014-08-21 Corinna Vinschen * net.cc (if_freenameindex): Don't catch a SEGV from free to fail loudly on double free. 2014-08-21 Corinna Vinschen * dir.cc (rmdir): Don't skip deleting fh in the ENOTEMPTY case. 2014-08-20 Corinna Vinschen * tls_pbuf.h (tmp_pathbuf::tmp_pathbuf): Convert to inline method. (tmp_pathbuf::~tmp_pathbuf): Ditto. * tls_pbuf.cc (tmp_pathbuf::tmp_pathbuf): Remove here. (tmp_pathbuf::~tmp_pathbuf): Ditto. 2014-08-19 Corinna Vinschen * dir.cc (dirfd): Per POSIX, return EINVAL on invalid directory stream. (telldir): Per POSIX, return -1 and set errno to EBADF, rather than just returning 0, on invalid directory stream. * signal.cc (sigwaitinfo): Return -1, not EFAULT, when SEGV was catched. 2014-08-19 Corinna Vinschen * autoload.cc: Replace WNet[...]A with WNet[...]W imports. * dcrt0.cc (initial_env): Drop strlwr calls. Call strcasestr instead. * fhandler_netdrive.cc: Throughout, convert to calling WNet UNICODE functions. Use tmp_pathbuf rather than alloca. Replace call to strlwr with call to RtlDowncaseUnicodeString. 2014-08-19 Corinna Vinschen * fhandler.h (fhandler_serial::is_tty): Reinstantiate. 2014-08-18 Corinna Vinschen * miscfuncs.cc (strlwr): Rename from cygwin_strlwr. Drop __stdcall decoration. (strupr): Rename from cygwin_strupr. Drop __stdcall decoration. * string.h (strlwr): Remove override macro. Simply declare. (strupr): Ditto. 2014-08-18 Corinna Vinschen * dtable.cc (dtable::init_std_file_from_handle): Mention that console handles are kernel objects since Windows 8. * fhandler.h (enum conn_state): Add "listener" state. (class fhandler_socket): Drop listener status flag. (fhandler_socket::lseek): Return -1 and errno ESPIPE. (fhandler_serial::lseek): Ditto. (fhandler_serial::is_tty): Remove. * fhandler_socket.cc (fhandler_socket::listen): Set connect_state to listener. Add comment. (fhandler_socket::accept4): Explicitely check if the socket is listening and fail with EINVAL, if not. Explain why we have to do that. (fhandler_socket::getpeereid): Drop now redundant test. 2014-08-15 Corinna Vinschen * winsup.h (_GNU_SOURCE): Define. Explain why. 2014-08-14 Corinna Vinschen * dlmalloc.c: Remove unused file. * dlmalloc.h: Ditto. * malloc.cc: Update to Doug Lea's malloc version 2.8.6. 2014-08-13 Corinna Vinschen * include/cygwin/version.h (CYGWIN_VERSION_DLL_MINOR): Bump to 33. (CYGWIN_VERSION_API_MINOR): Bump to reflect intermediate 1.7.32 release. 2014-07-21 Corinna Vinschen * include/cygwin/version.h (CYGWIN_VERSION_DLL_MINOR): Bump to 32. 2014-05-06 Corinna Vinschen * winlean.h (PIPE_REJECT_REMOTE_CLIENTS): Drop temporary definition since Mingw64 catched up. (DNLEN): Redefine as 16. Explain why. 2014-03-06 Corinna Vinschen * setlsapwd.cc (setlsapwd): Use RtlSecureZeroMemory to delete password from memory. 2014-11-05 Corinna Vinschen * new-features.xml (ov-new1.7.33): Document atexit. 2014-10-28 Corinna Vinschen * cygwinenv.xml: Change default setting of dosfilewarning. * new-features.xml (ov-new1.7.33): Document aforementioned change. 2014-10-27 Corinna Vinschen * new-features.xml (ov-new1.7.33): Document empty $PATH handling. 2014-10-22 Corinna Vinschen * posix.xml (std-gnu): Add ffsl, ffsll, quotactl. (std-notes): Add restrictions of quotactl. 2014-10-22 Yaakov Selkowitz * new-features.xml (ov-new1.7.33): Document stime. * posix.xml (std-deprec): Add stime. 2014-10-22 Corinna Vinschen * new-features.xml (ov-new1.7.33): s/Linux/glibc. 2014-10-22 Corinna Vinschen * new-features.xml (ov-new1.7.33): Update to current state. 2014-08-13 Corinna Vinschen * new-features.xml (ov-new1.7.33): Add new section. (ov-new1.7.32): Reflect intermediate 1.7.32 release. 2014-10-21 Corinna Vinschen * cygcheck.cc (CYGLSA64_DLL): Remove unused macro. (dump_sysinfo): If COMSPEC isn't set in the MSVCRT environment, set it. Explain why. --- winsup/cygwin/ChangeLog | 483 ++- winsup/cygwin/Makefile.in | 238 +- winsup/cygwin/autoload.cc | 8 +- winsup/cygwin/common.din | 6 +- winsup/cygwin/cygheap.cc | 41 +- winsup/cygwin/cygheap.h | 4 +- winsup/cygwin/cygtls.cc | 8 + winsup/cygwin/cygtls.h | 120 +- winsup/cygwin/dcrt0.cc | 67 +- winsup/cygwin/dir.cc | 351 +-- winsup/cygwin/dlfcn.cc | 31 +- winsup/cygwin/dll_init.cc | 2 +- winsup/cygwin/dtable.cc | 7 +- winsup/cygwin/environ.cc | 327 +- winsup/cygwin/exception.h | 84 +- winsup/cygwin/exceptions.cc | 105 +- winsup/cygwin/external.cc | 6 + winsup/cygwin/fcntl.cc | 141 +- winsup/cygwin/fhandler.h | 26 +- winsup/cygwin/fhandler_disk_file.cc | 42 +- winsup/cygwin/fhandler_netdrive.cc | 108 +- winsup/cygwin/fhandler_proc.cc | 46 +- winsup/cygwin/fhandler_process.cc | 4 +- winsup/cygwin/fhandler_socket.cc | 79 +- winsup/cygwin/fhandler_tape.cc | 290 +- winsup/cygwin/fhandler_tty.cc | 15 +- winsup/cygwin/flock.cc | 142 +- winsup/cygwin/gendef | 100 +- winsup/cygwin/globals.cc | 4 +- winsup/cygwin/include/cygwin/time.h | 2 + winsup/cygwin/include/cygwin/version.h | 11 +- winsup/cygwin/include/sys/cygwin.h | 4 +- winsup/cygwin/include/sys/mount.h | 7 +- winsup/cygwin/include/sys/quota.h | 239 ++ winsup/cygwin/kernel32.cc | 5 +- winsup/cygwin/lib/atexit.c | 49 + winsup/cygwin/lib/dso_handle.c | 12 + winsup/cygwin/libc/bsdlib.cc | 5 +- winsup/cygwin/libc/rexec.cc | 183 +- winsup/cygwin/malloc.cc | 3815 ++++++++++++++++-------- winsup/cygwin/miscfuncs.cc | 80 +- winsup/cygwin/mount.cc | 101 +- winsup/cygwin/msg.cc | 114 +- winsup/cygwin/net.cc | 1825 ++++++------ winsup/cygwin/ntdll.h | 77 +- winsup/cygwin/ntea.cc | 499 ++-- winsup/cygwin/path.cc | 2170 +++++++------- winsup/cygwin/path.h | 7 +- winsup/cygwin/poll.cc | 29 +- winsup/cygwin/posix.sgml | 8 + winsup/cygwin/posix_ipc.cc | 1046 +++---- winsup/cygwin/quotactl.cc | 341 +++ winsup/cygwin/release/1.7.33 | 80 + winsup/cygwin/resource.cc | 126 +- winsup/cygwin/security.cc | 27 +- winsup/cygwin/select.cc | 34 +- winsup/cygwin/sem.cc | 52 +- winsup/cygwin/setlsapwd.cc | 2 +- winsup/cygwin/shm.cc | 66 +- winsup/cygwin/signal.cc | 132 +- winsup/cygwin/spawn.cc | 1113 +++---- winsup/cygwin/string.h | 9 +- winsup/cygwin/syscalls.cc | 2110 ++++++------- winsup/cygwin/thread.cc | 264 +- winsup/cygwin/timer.cc | 207 +- winsup/cygwin/times.cc | 111 +- winsup/cygwin/tls_pbuf.cc | 16 +- winsup/cygwin/tls_pbuf.h | 19 +- winsup/cygwin/uinfo.cc | 13 +- winsup/cygwin/uname.cc | 144 +- winsup/cygwin/winlean.h | 16 +- winsup/cygwin/winsup.h | 7 +- winsup/doc/ChangeLog | 36 + winsup/doc/cygwinenv.xml | 2 +- winsup/doc/new-features.xml | 154 +- winsup/utils/ChangeLog | 6 + winsup/utils/cygcheck.cc | 19 +- 77 files changed, 10873 insertions(+), 7406 deletions(-) create mode 100644 winsup/cygwin/include/sys/quota.h create mode 100644 winsup/cygwin/lib/atexit.c create mode 100644 winsup/cygwin/lib/dso_handle.c create mode 100644 winsup/cygwin/quotactl.cc create mode 100644 winsup/cygwin/release/1.7.33 diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 0c6b6d21b..51cc4e880 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,425 @@ +2014-11-07 Corinna Vinschen + + * dcrt0.cc (cygwin__cxa_atexit): Fetch correct DSO handle value + by searching dll list. Explain why. + +2014-11-06 Corinna Vinschen + + * dcrt0.cc (cygwin_atexit): Change preceeding comment to reflect + API version numbers. + * external.cc (cygwin_internal): disable setting cxx_malloc on 64 bit. + Add CW_FIXED_ATEXIT case. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + * include/sys/cygwin.h (cygwin_getinfo_types): Add CW_FIXED_ATEXIT. + * lib/atexit.c (atexit): Test running Cygwin version by checking + return value of cygwin_internal (CW_FIXED_ATEXIT). + +2014-11-05 Corinna Vinschen + + * lib/atexit.c (atexit): Check for being linked into the executable. + If so, call __cxa_atexit with NULL DSO handle. Explain why. + * lib/dso_handle.c: New file providing fallback __dso_handle. + +2014-11-05 Corinna Vinschen + + * Makefile.in (NEW_FUNCTIONS): Add atexit to be not exported. + * lib/atexit.c (atexit): New, statically linkable version of atexit. + * dcrt0.cc (cygwin_atexit): Add comment to mark this function as old + entry point. Indiscriminately check for DSO of function pointer for + all functions, if checking for DSO of return address fails on x86_64. + Change comment accordingly. + +2014-11-05 Corinna Vinschen + + * Makefile.in (NEW_FUNCTIONS): Define target-independent. Add target + dependent stuff afterwards. Globally define timezone and all xdr + symbols as non-exported from libcygwin.a. + +2014-11-03 Corinna Vinschen + + * cygheap.cc: Fix formatting. + +2014-10-29 Corinna Vinschen + + * cygheap.cc (init_cygheap::init_installation_root): Create content of + installation_dir as non-prefixed path, if possible. + +2014-10-29 Corinna Vinschen + + * common.din (__cxa_atexit): Define as cygwin__cxa_atexit. + * dcrt0.cc (cygwin__cxa_atexit): New function. Explain what we do. + +2014-10-28 Corinna Vinschen + + * globals.cc (dos_file_warning): Set to false by default. + * path.cc (warn_msdos): Make static. Drop test for dos_file_warning. + (path_conv::check): Check for dos_file_warning here to avoid a function + call in the default case. + +2014-10-27 Corinna Vinschen + + * dcrt0.cc (cygwin_atexit): Add workaround for broken atexit calls + in __gcc_register_frame of DLLs built with gcc-4.8.3-3. + +2014-10-27 Corinna Vinschen + + * cygheap.cc (init_cygheap::init_installation_root): Set + installation_dir_len. + * cygheap.h (struct init_cygheap): Add installation_dir_len member. + * environ.cc (win_env::add_cache): Use stpcpy for speed. + (posify_maybe): Use tmp_pathbuf buffer instead of stack. + (raise_envblock): New function to resize Windows environment block. + (build_env): Fix indentation. Call raise_envblock function. Check if + $PATH exists and is non-empty. If not, add PATH variable with Cygwin + installation directory as content to Windows environment. Explain why. + +2014-10-24 Corinna Vinschen + + * fhandler_proc.cc (format_proc_cygdrive): Fix symlink path if cygdrive + is "/". + +2014-10-24 Corinna Vinschen + + * gendef (sigdelayed): 64 bit only: Fix seh_pushreg statements in + prologue. + +2014-10-22 Yaakov Selkowitz + + * common.din (stime): Export. + * times.cc (stime): New function. + * include/cygwin/time.h (stime): Declare. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + +2014-10-20 Corinna Vinschen + + * fhandler_serial.cc: Revert debug code accidentally checked in on + 2014-08-18. + +2014-10-20 Corinna Vinschen + + * path.cc (symlink_info::check): Set error from status code if opening + the parent directory failed, but keep special case for root dirs. + +2014-10-17 Corinna Vinschen + + * cygheap.cc (init_cygheap::init_installation_root): Just memmove + contents of installation_root instead of calling GetModuleFileNameW + again. Copy installation_root to installation_dir before stripping of + "bin" dir. Explain what we do. + * cygheap.h (struct init_cygheap): Add installation_dir member. + +2014-10-17 Corinna Vinschen + + * cygtls.h (__try): Define __l_endtry as block-local label in 32 bit + case as well. + +2014-10-17 Corinna Vinschen + + * Makefile.in (DLL_OFILES): Add quotactl.o. + * common.din (quotactl): Export. + * ntdll.h: Define FILE_FS_CONTROL_INFORMATION::FileSystemControlFlags + flag values. + (struct _FILE_FS_CONTROL_INFORMATION): Define. + (struct _FILE_GET_QUOTA_INFORMATION): Define. + (typedef struct _FILE_QUOTA_INFORMATION): Define. + (NtQueryObject): Use PVOID rather than VOID*. + (NtQueryVolumeInformationFile): Ditto. + (NtQueryQuotaInformationFile): Declare. + (NtSetQuotaInformationFile): Declare. + (NtSetVolumeInformationFile): Declare. + * quotactl.cc: New file implementing quotactl(). + * include/sys/mount.h (BLOCK_SIZE): Define. + (BLOCK_SIZE_BITS): Define. + * include/sys/quota.h: New header. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + +2014-10-16 Corinna Vinschen + + * Makefile.in (DLL_OFILES): Rearrange with one file per line. + +2014-10-16 Corinna Vinschen + + * fhandler_proc.cc (format_proc_partitions): Extend output to print + the windows mount points the device is mounted on. + +2014-10-13 Corinna Vinschen + + * net.cc (cygwin_setsockopt): Drop redundant test for AF_LOCAL and + SOCK_STREAM in SO_PEERCRED case, as in the original patch. + +2014-10-12 Corinna Vinschen + + * dlfcn.cc (gfpod_helper): Only check for POSIX dir separator, same as + in get_full_path_of_dll. + +2014-10-12 Corinna Vinschen + + * dlfcn.cc (set_dl_error): Drop useless __stdcall. + (check_path_access): Ditto. Drop FE_CWD from call to find_exec. + (gfpod_helper): Call path_conv::check for all paths containing a dir + separator to more closely follow the Linux search algorithm. + (get_full_path_of_dll): Drop useless __stdcall. + (dlopen): Simplify RTLD_NOLOAD case by calling GetModuleHandleEx + instead of GetModuleHandle/LoadLibrary. + +2014-10-11 Christian Franke + + Add setsockopt(sd, SOL_SOCKET, SO_PEERCRED, NULL, 0) to disable + initial handshake on AF_LOCAL sockets. + * fhandler.h (class fhandler_socket): Add no_getpeereid status flag. + (fhandler_socket::af_local_set_no_getpeereid): New prototype. + * fhandler_socket.cc (fhandler_socket::af_local_connect): Skip handshake + if no_getpeereid is set. Add debug output. + (fhandler_socket::af_local_accept): Likewise. + (fhandler_socket::af_local_set_no_getpeereid): New function. + (fhandler_socket::af_local_copy): Copy no_getpeereid. + (fhandler_socket::getpeereid): Fail if no_getpeereid is set. + * net.cc (cygwin_setsockopt): Add SO_PEERCRED for AF_LOCAL/SOCK_STREAM + sockets. Add comment to explain why we need it. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + +2014-10-10 Corinna Vinschen + + * dlfcn.cc (dlopen): Disable old 32 bit code on 64 bit. + * dcrt0.cc (check_sanity_and_sync): Ditto. + * dll_init.cc (dll_dllcrt0_1): Fix typo in comment. + +2014-10-08 Corinna Vinschen + + * common.din (ffsl): Export. + (ffsll): Export. + * syscalls.cc (ffs): Implement using GCC intrinsic. + (ffsl): Ditto. + (ffsll): Ditto. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + +2014-10-08 Christian Franke + + * syscalls.cc (ffs): Fix crash of ffs (0x80000000) on 64 bit. + +2014-10-08 Corinna Vinschen + + * fhandler_process.cc (format_process_statm): Fix output of dirty + pages. Add linefeed. + +2014-09-05 Corinna Vinschen + + * fhandler_proc.cc (proc_tab): Add entry for cygdrive symlink. + (format_proc_cygdrive): New function to implement /proc/cygdrive. + +2014-09-05 Corinna Vinschen + + * exception.h (class exception): Remove unnecessary #ifdef. + +2014-09-05 Corinna Vinschen + + * winlean.h (DNLEN): Raise to 31. Explain why. + +2014-08-28 Corinna Vinschen + + * fhandler_disk_file.cc (fhandler_disk_file::fstatvfs): Try the + FileFsSizeInformation information class on filesystems choking on + FileFsFullSizeInformation (I see you Netapp!) + +2014-08-27 Corinna Vinschen + + * fhandler.h (fhandler_pty_slave::fch_open_handles): Add bool parameter + to declaration. + * fhandler_tty.cc (fhandler_pty_slave::fch_open_handles): Add bool + parameter "chown". Only request WRITE_OWNER access when opening pty + synchronization objects if "chown" is set. + (fhandler_pty_slave::fchmod): Call fch_open_handles with new bool + parameter set to false. + (fhandler_pty_slave::fchown): Call fch_open_handles with new bool + parameter set to true. + * kernel32.cc (CreateFileMappingW): Fix default standard rights for + file mappings from READ_CONTROL to STANDARD_RIGHTS_REQUIRED to allow + changing the DACL (fixes "access denied" error in pinfo::set_acl). + + * fhandler_disk_file.cc (fhandler_base::fstat_helper): Change debug + output to print mode bits in octal. + * security.cc (alloc_sd): Ditto. + (set_file_attribute): Ditto. + +2014-08-27 Corinna Vinschen + + * ntea.cc (read_ea): Change left-over return to __leave. Fix + condition to close handle. Call NtClose rather than CloseHandle. + (write_ea): Fix condition to close handle. Call NtClose rather than + CloseHandle. + * security.cc (get_file_sd): Call pc.init_reopen_attr if a valid + incoming handle was given, pc.get_object_attr otherwise. + (set_file_sd): Ditto. + +2014-08-26 Corinna Vinschen + + * path.h (path_conv::init_reopen_attr): Change from void to returning + POBJECT_ATTRIBUTES. Take OBJECT_ATTRIBUTES reference as argument, not + pointer. + * fhandler_disk_file.cc: Throughout accommodate above change. + * syscalls.cc: Ditto. + * ntea.cc (read_ea): Don't set hdl to NULL if it's already NULL. Set + attr with pc.init_reopen_attr before trying to reopen file. + (write_ea): Ditto. + * security.cc (get_file_sd): Use pc.init_reopen_attr rather than + pc.get_object_attr when trying to reopen file. + (set_file_sd): Ditto. + +2014-08-25 Corinna Vinschen + + * cygtls.cc (san::leave/x86_64): Implement. + * cygtls.h (class tls_pathbuf): Move counter variables into a union. + Add 64 bit element _counters covering both counter variables to + optimize save and restore operations. + (class san/x86_64): Only store single 64 bit value. + (san::san/x86_64): Implement. + (san::leave/x86_64): Only declare here, as returns_twice function. + Explain why. + (class san/i686): Change type of _c_cnt and _w_cnt to uint32_t. + (__try/x86_64): Move definition of __sebastian after the first memory + barrier. Drop __sebastian.setup call. + +2014-08-25 Corinna Vinschen + + * cygtls.cc (_cygtls::remove): Revert previous patch. + * cygtls.h (struct _local_storage): Move pathbufs back here. + (class san/x86_64): Revert class. Save and restore pathbufs counters + only. + (class san/i686): Revert saving and restoring pathbufs counters. + (__try/x86_64): Add a san variable and call it's setup method. + (__except/x86_64): Call san::leave to restore pathbufs counters. + * gendef (_sigbe): Revert previous change. + * thread.cc (verifyable_object_state): Remove gcc 4.7 workaround in + forward declaration as well. + * tls_pbuf.cc (tls_pbuf): Revert previous change. + * tls_pbuf.h (class tmp_pathbuf): Accommodate reverting pathbufs to + locals structure. + * tlsoffsets.h: Regenerate. + * tlsoffsets64.h: Regenerate. + +2014-08-21 Corinna Vinschen + + * Throughout, use __try/__except/__endtry blocks, rather than myfault + handler. + * cygtls.cc (_cygtls::remove): Accommodate the fact that pathbufs + has been moved from _local_storage to _cygtls. + * cygtls.h (class tls_pathbuf): Add comment to hint to gendef usage + of counters. Change type of counters to uint32_t for clarity. + Remove _cygtls as friend class. + (struct _local_storage): Move pathbufs from here... + (struct _cygtls): ...to here, allowing to access it from _sigbe. + (class san): Only define on 32 bit. Remove errno, _c_cnt and _w_cnt + members. + (san::setup): Drop parameter. Don't initialize removed members. + (san::leave): Don't set removed members. + (class myfault): Only define on 32 bit. + (myfault::faulted): Only keep implementation not taking any parameter. + Drop argument in call to sebastian.setup. + (__try/__leave/__except/__endtry): Implement to support real SEH. For + now stick to SJLJ on 32 bit. + * dcrt0.cc (dll_crt0_0): Drop 64 bit call to + exception::install_myfault_handler. + * exception.h (exception_handler): Define with EXCEPTION_DISPOSITION + as return type. + (PDISPATCHER_CONTEXT): Define as void * on 32 bit. Define as pointer + to _DISPATCHER_CONTEXT on 64 bit. + (class exception): Define separately for 32 and 64 bit. + (exception::myfault): Add handler for myfault SEH handling on 64 bit. + (exception::exception): Fix mangled method name to account for change + in type of last parameter. + (exception::install_myfault_handler): Remove. + * exceptions.cc (exception::myfault_handle): Remove. + (exception::myfault): New SEH handler for 64 bit. + * gendef (_sigbe): Set tls_pathbuf counters to 0 explicitely when + returning to the caller. + * ntdll.h: Move a comment to a better place. + (struct _SCOPE_TABLE): Define on 64 bit. + * thread.cc (verifyable_object_isvalid): Remove gcc 4.7 workaround. + * tls_pbuf.cc (tls_pbuf): Fix to accommodate new place of pathbufs. + (tls_pathbuf::destroy): Change type of loop variables to uint32_t. + * tls_pbuf.h (class tmp_pathbuf): Change type of buffer counters to + uint32_t. Accommodate new place of pathbufs. + * tlsoffsets.h: Regenerate. + * tlsoffsets64.h: Regenerate. + +2014-08-21 Corinna Vinschen + + * miscfuncs.cc (__import_address): Cover the first dereference to imp + under the fault handler. + +2014-08-21 Corinna Vinschen + + * net.cc (if_freenameindex): Don't catch a SEGV from free to fail + loudly on double free. + +2014-08-21 Corinna Vinschen + + * dir.cc (rmdir): Don't skip deleting fh in the ENOTEMPTY case. + +2014-08-20 Corinna Vinschen + + * tls_pbuf.h (tmp_pathbuf::tmp_pathbuf): Convert to inline method. + (tmp_pathbuf::~tmp_pathbuf): Ditto. + * tls_pbuf.cc (tmp_pathbuf::tmp_pathbuf): Remove here. + (tmp_pathbuf::~tmp_pathbuf): Ditto. + +2014-08-19 Corinna Vinschen + + * dir.cc (dirfd): Per POSIX, return EINVAL on invalid directory stream. + (telldir): Per POSIX, return -1 and set errno to EBADF, rather than + just returning 0, on invalid directory stream. + * signal.cc (sigwaitinfo): Return -1, not EFAULT, when SEGV was catched. + +2014-08-19 Corinna Vinschen + + * autoload.cc: Replace WNet[...]A with WNet[...]W imports. + * dcrt0.cc (initial_env): Drop strlwr calls. Call strcasestr instead. + * fhandler_netdrive.cc: Throughout, convert to calling WNet UNICODE + functions. Use tmp_pathbuf rather than alloca. Replace call to + strlwr with call to RtlDowncaseUnicodeString. + +2014-08-19 Corinna Vinschen + + * fhandler.h (fhandler_serial::is_tty): Reinstantiate. + +2014-08-18 Corinna Vinschen + + * miscfuncs.cc (strlwr): Rename from cygwin_strlwr. Drop __stdcall + decoration. + (strupr): Rename from cygwin_strupr. Drop __stdcall decoration. + * string.h (strlwr): Remove override macro. Simply declare. + (strupr): Ditto. + +2014-08-18 Corinna Vinschen + + * dtable.cc (dtable::init_std_file_from_handle): Mention that console + handles are kernel objects since Windows 8. + * fhandler.h (enum conn_state): Add "listener" state. + (class fhandler_socket): Drop listener status flag. + (fhandler_socket::lseek): Return -1 and errno ESPIPE. + (fhandler_serial::lseek): Ditto. + (fhandler_serial::is_tty): Remove. + * fhandler_socket.cc (fhandler_socket::listen): Set connect_state to + listener. Add comment. + (fhandler_socket::accept4): Explicitely check if the socket is listening + and fail with EINVAL, if not. Explain why we have to do that. + (fhandler_socket::getpeereid): Drop now redundant test. + +2014-08-15 Corinna Vinschen + + * winsup.h (_GNU_SOURCE): Define. Explain why. + +2014-08-14 Corinna Vinschen + + * dlmalloc.c: Remove unused file. + * dlmalloc.h: Ditto. + * malloc.cc: Update to Doug Lea's malloc version 2.8.6. + +2014-08-13 Corinna Vinschen + + * include/cygwin/version.h (CYGWIN_VERSION_DLL_MINOR): Bump to 33. + (CYGWIN_VERSION_API_MINOR): Bump to reflect intermediate 1.7.32 release. + 2014-08-11 Corinna Vinschen * cpuid.h: Add missing copyright header. Fix formatting. Use uint32_t @@ -49,6 +471,10 @@ * thread.cc (pthread::init_mainthread): Initialize thread mutex to type PTHREAD_MUTEX_RECURSIVE, just as for any other thread. +2014-07-21 Corinna Vinschen + + * include/cygwin/version.h (CYGWIN_VERSION_DLL_MINOR): Bump to 32. + 2014-07-16 Corinna Vinschen * thread.cc (pthread::create): Handle stackaddr as upper bound address. @@ -127,7 +553,8 @@ writing small buffers. Rename variables and add comments to help understanding the code in years to come. -2014-07-07 Corinna Vinschen +2014-07-07 Pierre Humblet + Corinna Vinschen * libc/minires.c (minires_dprintf): Change "Minires" to "Resolv" to differ from external minres lib. @@ -257,7 +684,7 @@ 2014-05-20 Corinna Vinschen * fhandler_floppy.cc (fhandler_dev_floppy::get_drive_info): Fix floppy - drive handling broken with 1.7.19. + drive handling broken with 1.7.19. 2014-05-20 Corinna Vinschen @@ -318,6 +745,12 @@ * fhandler_console.cc (dev_console::save_restore): Only save current dwEnd line rather than the one after that. +2014-05-06 Corinna Vinschen + + * winlean.h (PIPE_REJECT_REMOTE_CLIENTS): Drop temporary definition + since Mingw64 catched up. + (DNLEN): Redefine as 16. Explain why. + 2014-05-05 Corinna Vinschen * net.cc (cygwin_getsockopt): Rearrange code slightly and handle @@ -381,7 +814,7 @@ (tmp_pathbuf::w_get): Ditto. * tls_pbuf.h (class tmp_pathbuf): Change type of c_buf_old and w_buf_old to unsigned. - (tmp_pathbuf::check_usage): New inline method to check if we have + (tmp_pathbuf::check_usage): New inline method to check if we have enough tmp_pathbuf buffers left to call a function using tmp_pathbuf buffers. * tlsoffsets.h: Regenerate. @@ -395,21 +828,21 @@ * include/cygwin/version.h (CYGWIN_VERSION_DLL_MINOR): Bump to 30. +2014-04-09 Corinna Vinschen + + * exceptions.cc (exception::myfault_handle): Only handle the minimum + amount of exceptions the myfault handler was designed for. + 2014-04-08 Corinna Vinschen * cygwin.sc.in: (Temporarily?) workaround serious ld bug which truncates symbols in certain computations to 32 bit. See https://sourceware.org/bugzilla/show_bug.cgi?id=16821 -2014-04-09 Corinna Vinschen - - * exceptions.cc (exception::myfault_handle): Only handle the minimum - amount of exceptions the myfault handler was designed for. - 2014-04-07 Corinna Vinschen * cygserver_ipc.h (ipc_set_proc_info): Add bool parameter to specify - whether or not to send signal_arrived. + whether or not to send signal_arrived. * shm.cc (client_request_shm::client_request_shm): Call ipc_set_proc_info with bool parameter set to true to not send signal_arrived. @@ -606,6 +1039,11 @@ * dir.cc (opendir): Propagate any errno from build_fh_name. +2014-03-06 Corinna Vinschen + + * setlsapwd.cc (setlsapwd): Use RtlSecureZeroMemory to delete password + from memory. + 2014-03-05 Corinna Vinschen * include/cygwin/config.h (__TM_GMTOFF): Define. @@ -641,13 +1079,6 @@ (fhandler_console::char_command): Use scroll_buffer_screen as appropriate. (dev_console::scroll_buffer): Remove if 0'ed block. -2014-02-22 Corinna Vinschen - - * external.cc (cygwin_internal): Add cases for CW_GETPWSID and - CW_GETGRSID. - * include/sys/cygwin.h (cygwin_getinfo_types): Add CW_SETENT, CW_GETENT, - CW_ENDENT, CW_GETNSSSEP, CW_GETPWSID and CW_GETGRSID. - 2014-02-22 Christopher Faylor * dev_console::scroll_buffer): Reinstate clipping region. @@ -677,6 +1108,13 @@ (fhandler_console::cursor_get): Ditto. (fhandler_console::write): Fix "reverse index". +2014-02-22 Corinna Vinschen + + * external.cc (cygwin_internal): Add cases for CW_GETPWSID and + CW_GETGRSID. + * include/sys/cygwin.h (cygwin_getinfo_types): Add CW_SETENT, CW_GETENT, + CW_ENDENT, CW_GETNSSSEP, CW_GETPWSID and CW_GETGRSID. + 2014-02-20 Corinna Vinschen * grp.cc (getgrouplist): Fix previous fix so ret is only set to ngroups @@ -687,6 +1125,16 @@ * grp.cc (get_groups): Don't add gid to list if it's ILLEGAL_GID. (getgrouplist): Return number of groups, just like glibc. +2014-02-18 Corinna Vinschen + + * setlsapwd.cc (setlsapwd): Fix conditional expression after breaking + it on 2014-01-23. + +2014-02-16 Corinna Vinschen + + * dcrt0.cc (dll_crt0_1): Call initial_setlocale before fetching + current user information. + 2014-02-15 Christopher Faylor * DevNotes: Add entry cgf-000024. @@ -765,6 +1213,9 @@ * net.cc (cygwin_gethostname): Call GetComputerNameExA rather than GetComputerNameA if gethostname failed. * shared.cc (user_info::initialize): Fix formatting. + +2014-02-06 Corinna Vinschen + * include/sys/file.h: Define flock and accompanying macros if not already defined in sys/_default_fcntl.h. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index b47330379..7daad7b54 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -1,6 +1,6 @@ # Makefile.in for Cygwin. # Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. +# 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. # # This file is part of Cygwin. # @@ -153,31 +153,157 @@ DLL_IMPORTS:=${shell $(CC) -print-file-name=w32api/libkernel32.a} ${shell $(CC) MT_SAFE_OBJECTS:= # -DLL_OFILES:=advapi32.o arc4random.o assert.o autoload.o base64.o bsdlib.o ctype.o \ - cxx.o cygheap.o cygthread.o cygtls.o cygwait.o cygxdr.o dcrt0.o debug.o \ - devices.o dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o exceptions.o \ - exec.o external.o fcntl.o fenv.o fhandler.o fhandler_clipboard.o \ - fhandler_console.o fhandler_dev.o fhandler_disk_file.o fhandler_dsp.o \ - fhandler_fifo.o fhandler_floppy.o fhandler_mailslot.o \ - fhandler_netdrive.o fhandler_nodevice.o fhandler_proc.o \ - fhandler_process.o fhandler_procnet.o fhandler_procsys.o \ - fhandler_procsysvipc.o fhandler_random.o fhandler_raw.o \ - fhandler_registry.o fhandler_serial.o fhandler_socket.o fhandler_tape.o \ - fhandler_termios.o fhandler_tty.o fhandler_virtual.o fhandler_windows.o \ - fhandler_zero.o flock.o fnmatch.o fork.o fts.o ftw.o getopt.o glob.o \ - glob_pattern_p.o globals.o grp.o heap.o hookapi.o inet_addr.o \ - inet_network.o init.o ioctl.o ipc.o kernel32.o libstdcxx_wrapper.o \ - localtime.o lsearch.o malloc_wrapper.o minires-os-if.o minires.o \ - miscfuncs.o mktemp.o mmap.o msg.o mount.o net.o netdb.o nfs.o nftw.o \ - nlsfuncs.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o posix_ipc.o \ - pseudo-reloc.o pthread.o random.o regcomp.o regerror.o regexec.o regfree.o \ - registry.o resource.o rexec.o rcmd.o scandir.o sched.o sec_acl.o \ - sec_auth.o sec_helper.o security.o select.o sem.o setlsapwd.o shared.o \ - shm.o sigfe.o signal.o sigproc.o smallprint.o spawn.o strace.o strfmon.o \ - strfuncs.o strptime.o strsep.o strsig.o sync.o syscalls.o sysconf.o \ - syslog.o termios.o thread.o timer.o times.o tls_pbuf.o tty.o uinfo.o \ - uname.o wait.o wincap.o window.o winf.o wow64.o xsique.o \ - $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS) +DLL_OFILES:= \ + advapi32.o \ + arc4random.o \ + assert.o \ + autoload.o \ + base64.o \ + bsdlib.o \ + ctype.o \ + cxx.o \ + cygheap.o \ + cygthread.o \ + cygtls.o \ + cygwait.o \ + cygxdr.o \ + dcrt0.o \ + debug.o \ + devices.o \ + dir.o \ + dlfcn.o \ + dll_init.o \ + dtable.o \ + environ.o \ + errno.o \ + exceptions.o \ + exec.o \ + external.o \ + fcntl.o \ + fenv.o \ + fhandler.o \ + fhandler_clipboard.o \ + fhandler_console.o \ + fhandler_dev.o \ + fhandler_disk_file.o \ + fhandler_dsp.o \ + fhandler_fifo.o \ + fhandler_floppy.o \ + fhandler_mailslot.o \ + fhandler_netdrive.o \ + fhandler_nodevice.o \ + fhandler_proc.o \ + fhandler_process.o \ + fhandler_procnet.o \ + fhandler_procsys.o \ + fhandler_procsysvipc.o \ + fhandler_random.o \ + fhandler_raw.o \ + fhandler_registry.o \ + fhandler_serial.o \ + fhandler_socket.o \ + fhandler_tape.o \ + fhandler_termios.o \ + fhandler_tty.o \ + fhandler_virtual.o \ + fhandler_windows.o \ + fhandler_zero.o \ + flock.o \ + fnmatch.o \ + fork.o \ + fts.o \ + ftw.o \ + getopt.o \ + glob.o \ + glob_pattern_p.o \ + globals.o \ + grp.o \ + heap.o \ + hookapi.o \ + inet_addr.o \ + inet_network.o \ + init.o \ + ioctl.o \ + ipc.o \ + kernel32.o \ + libstdcxx_wrapper.o \ + localtime.o \ + lsearch.o \ + malloc_wrapper.o \ + minires-os-if.o \ + minires.o \ + miscfuncs.o \ + mktemp.o \ + mmap.o \ + msg.o \ + mount.o \ + net.o \ + netdb.o \ + nfs.o \ + nftw.o \ + nlsfuncs.o \ + ntea.o \ + passwd.o \ + path.o \ + pinfo.o \ + pipe.o \ + poll.o \ + posix_ipc.o \ + pseudo-reloc.o \ + pthread.o \ + quotactl.o \ + random.o \ + regcomp.o \ + regerror.o \ + regexec.o \ + regfree.o \ + registry.o \ + resource.o \ + rexec.o \ + rcmd.o \ + scandir.o \ + sched.o \ + sec_acl.o \ + sec_auth.o \ + sec_helper.o \ + security.o \ + select.o \ + sem.o \ + setlsapwd.o \ + shared.o \ + shm.o \ + sigfe.o \ + signal.o \ + sigproc.o \ + smallprint.o \ + spawn.o \ + strace.o \ + strfmon.o \ + strfuncs.o \ + strptime.o \ + strsep.o \ + strsig.o \ + sync.o \ + syscalls.o \ + sysconf.o \ + syslog.o \ + termios.o \ + thread.o \ + timer.o \ + times.o \ + tls_pbuf.o \ + tty.o \ + uinfo.o \ + uname.o \ + wait.o \ + wincap.o \ + window.o \ + winf.o \ + wow64.o \ + xsique.o \ + $(EXTRA_OFILES) \ + $(MALLOC_OFILES) \ + $(MT_SAFE_OBJECTS) EXCLUDE_STATIC_OFILES:=$(addprefix --exclude=,\ cygtls.o \ @@ -202,10 +328,63 @@ endif GMON_OFILES:=gmon.o mcount.o profil.o mcountFunc.o -ifeq ($(target_cpu),x86_64) -NEW_FUNCTIONS:= -else NEW_FUNCTIONS:=$(addprefix --replace=,\ + atexit= \ + timezone= \ + __xdrrec_getrec= \ + __xdrrec_setnonblock= \ + xdr_array= \ + xdr_bool= \ + xdr_bytes= \ + xdr_char= \ + xdr_double= \ + xdr_enum= \ + xdr_float= \ + xdr_free= \ + xdr_hyper= \ + xdr_int= \ + xdr_int16_t= \ + xdr_int32_t= \ + xdr_int64_t= \ + xdr_int8_t= \ + xdr_long= \ + xdr_longlong_t= \ + xdr_netobj= \ + xdr_opaque= \ + xdr_pointer= \ + xdr_reference= \ + xdr_short= \ + xdr_sizeof= \ + xdr_string= \ + xdr_u_char= \ + xdr_u_hyper= \ + xdr_u_int= \ + xdr_u_int16_t= \ + xdr_u_int32_t= \ + xdr_u_int64_t= \ + xdr_u_int8_t= \ + xdr_u_long= \ + xdr_u_longlong_t= \ + xdr_u_short= \ + xdr_uint16_t= \ + xdr_uint32_t= \ + xdr_uint64_t= \ + xdr_uint8_t= \ + xdr_union= \ + xdr_vector= \ + xdr_void= \ + xdr_wrapstring= \ + xdrmem_create= \ + xdrrec_create= \ + xdrrec_endofrecord= \ + xdrrec_eof= \ + xdrrec_skiprecord= \ + xdrstdio_create= \ +) +ifeq ($(target_cpu),x86_64) +NEW_FUNCTIONS+= +else +NEW_FUNCTIONS+=$(addprefix --replace=,\ acl=_acl32 \ aclcheck=_aclcheck32 \ aclfrommode=_aclfrommode32 \ @@ -253,7 +432,6 @@ NEW_FUNCTIONS:=$(addprefix --replace=,\ setreuid=_setreuid32 \ setuid=_setuid32 \ stat=_stat64 \ - timezone= \ tmpfile=_tmpfile64 \ truncate=_truncate64 \ ) diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 0199cc553..4351173be 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -581,10 +581,10 @@ LoadDLLfuncEx2 (IdnToUnicode, 20, kernel32, 1, 0) LoadDLLfunc (LocaleNameToLCID, 8, kernel32) LoadDLLfunc (WNetCloseEnum, 4, mpr) -LoadDLLfunc (WNetEnumResourceA, 16, mpr) -LoadDLLfunc (WNetGetProviderNameA, 12, mpr) -LoadDLLfunc (WNetGetResourceInformationA, 16, mpr) -LoadDLLfunc (WNetOpenEnumA, 20, mpr) +LoadDLLfunc (WNetEnumResourceW, 16, mpr) +LoadDLLfunc (WNetGetProviderNameW, 12, mpr) +LoadDLLfunc (WNetGetResourceInformationW, 16, mpr) +LoadDLLfunc (WNetOpenEnumW, 20, mpr) LoadDLLfunc (DsGetDcNameW, 24, netapi32) LoadDLLfunc (NetApiBufferFree, 4, netapi32) diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index bf20fde7a..3fcde65cc 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -41,7 +41,7 @@ __assert_func NOSIGFE __assertfail NOSIGFE __b64_ntop NOSIGFE __b64_pton NOSIGFE -__cxa_atexit SIGFE +__cxa_atexit = cygwin__cxa_atexit SIGFE __cxa_finalize SIGFE __dn_comp SIGFE __dn_expand SIGFE @@ -374,6 +374,8 @@ feupdateenv SIGFE fexecve SIGFE fflush SIGFE ffs NOSIGFE +ffsl NOSIGFE +ffsll NOSIGFE fgetc SIGFE fgetpos SIGFE fgets SIGFE @@ -918,6 +920,7 @@ putwc SIGFE putwchar SIGFE pwrite SIGFE qsort NOSIGFE +quotactl SIGFE raise SIGFE rand NOSIGFE rand_r NOSIGFE @@ -1106,6 +1109,7 @@ sscanf SIGFE stat SIGFE statfs SIGFE statvfs SIGFE +stime SIGFE stpcpy NOSIGFE stpncpy NOSIGFE strcasecmp NOSIGFE diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index 1ccbbaf5f..26564ad9c 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -140,6 +140,8 @@ init_cygheap::close_ctty () void init_cygheap::init_installation_root () { + ptrdiff_t len = 0; + if (!GetModuleFileNameW (cygwin_hmodule, installation_root, PATH_MAX)) api_fatal ("Can't initialize Cygwin installation root dir.\n" "GetModuleFileNameW(%p, %p, %u), %E", @@ -147,16 +149,18 @@ init_cygheap::init_installation_root () PWCHAR p = installation_root; if (wcsncasecmp (p, L"\\\\", 2)) /* Normal drive letter path */ { - p = wcpcpy (p, L"\\??\\"); - GetModuleFileNameW (cygwin_hmodule, p, PATH_MAX - 4); + len = 4; + memmove (p + 4, p, PATH_MAX - 4); + p = wcpncpy (p, L"\\\\?\\", 4); } else { bool unc = false; if (wcsncmp (p + 2, L"?\\", 2)) /* No long path prefix, so UNC path. */ { - p = wcpcpy (p, L"\\??\\UN"); - GetModuleFileNameW (cygwin_hmodule, p, PATH_MAX - 6); + len = 6; + memmove (p + 6, p, PATH_MAX - 6); + p = wcpncpy (p, L"\\??\\UN", 6); *p = L'C'; unc = true; } @@ -170,12 +174,12 @@ init_cygheap::init_installation_root () } } installation_root[1] = L'?'; - RtlInitEmptyUnicodeString (&installation_key, installation_key_buf, sizeof installation_key_buf); RtlInt64ToHexUnicodeString (hash_path_name (0, installation_root), &installation_key, FALSE); + /* Strip off last path component ("\\cygwin1.dll") */ PWCHAR w = wcsrchr (installation_root, L'\\'); if (w) { @@ -185,6 +189,20 @@ init_cygheap::init_installation_root () if (!w) api_fatal ("Can't initialize Cygwin installation root dir.\n" "Invalid DLL path"); + + /* Copy result into installation_dir before stripping off "bin" dir and + revert to Win32 path. This path is added to the Windows environment + in buildenv. See there for a description. */ + installation_dir_len = wcpncpy (installation_dir, installation_root + len, + PATH_MAX) + - installation_dir; + if (len == 4) /* Local path */ + ; + else if (len == 6) /* UNC path */ + installation_dir[0] = L'\\'; + else /* Long, prefixed path */ + installation_dir[1] = L'\\'; + /* If w < p, the Cygwin DLL resides in the root dir of a drive or network path. In that case, if we strip off yet another backslash, the path becomes invalid. We avoid that here so that the DLL also works in this @@ -638,10 +656,7 @@ init_cygheap::find_tls (int sig, bool& issig_wait) _cygtls *t = NULL; issig_wait = false; - myfault efault; - if (efault.faulted ()) - threadlist[ix]->remove (INFINITE); - else + __try { ix = -1; /* Scan thread list looking for valid signal-delivery candidates */ @@ -652,11 +667,15 @@ init_cygheap::find_tls (int sig, bool& issig_wait) { t = cygheap->threadlist[ix]; issig_wait = true; - goto out; + __leave; } else if (!t && !sigismember (&(threadlist[ix]->sigmask), sig)) t = cygheap->threadlist[ix]; } -out: + __except (NO_ERROR) + { + threadlist[ix]->remove (INFINITE); + } + __endtry return t; } diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 164a87090..a83b3d7c7 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -1,7 +1,7 @@ /* cygheap.h: Cygwin heap manager. Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012, 2013 Red Hat, Inc. + 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -375,6 +375,8 @@ struct init_cygheap: public mini_cygheap unsigned bucket_val[NBUCKETS]; char *buckets[NBUCKETS]; WCHAR installation_root[PATH_MAX]; + WCHAR installation_dir[PATH_MAX]; + size_t installation_dir_len; UNICODE_STRING installation_key; WCHAR installation_key_buf[18]; cygheap_root root; diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc index 5cc9405eb..2eaae9437 100644 --- a/winsup/cygwin/cygtls.cc +++ b/winsup/cygwin/cygtls.cc @@ -200,3 +200,11 @@ _cygtls::remove (DWORD wait) cygheap->remove_tls (this, wait); remove_wq (wait); } + +#ifdef __x86_64__ +void san::leave () +{ + /* Restore tls_pathbuf counters in case of error. */ + _my_tls.locals.pathbufs._counters = _cnt; +} +#endif diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h index 0d6aa713b..366d7da4d 100644 --- a/winsup/cygwin/cygtls.h +++ b/winsup/cygwin/cygtls.h @@ -44,18 +44,28 @@ details. */ #else #pragma pack(push,4) #endif + /* Defined here to support auto rebuild of tlsoffsets.h. */ class tls_pathbuf { - int c_cnt; - int w_cnt; + /* Make sure that c_cnt and w_cnt are always the first two members of this + class, and never change the size (32 bit), unless you also change the + mov statements in sigbe! */ + union + { + struct + { + uint32_t c_cnt; + uint32_t w_cnt; + }; + uint64_t _counters; + }; char *c_buf[TP_NUM_C_BUFS]; WCHAR *w_buf[TP_NUM_W_BUFS]; public: void destroy (); friend class tmp_pathbuf; - friend class _cygtls; friend class san; }; @@ -136,7 +146,6 @@ struct _local_storage /* thread.cc */ HANDLE cw_timer; - /* All functions requiring temporary path buffers. */ tls_pathbuf pathbufs; char ttybuf[32]; }; @@ -288,31 +297,43 @@ const int CYGTLS_PADSIZE = 12700; extern PVOID _tlsbase __asm__ ("%fs:4"); extern PVOID _tlstop __asm__ ("%fs:8"); #endif + #define _my_tls (*((_cygtls *) ((char *)_tlsbase - CYGTLS_PADSIZE))) extern _cygtls *_main_tls; extern _cygtls *_sig_tls; +#ifdef __x86_64__ +class san +{ + uint64_t _cnt; +public: + san () __attribute__ ((always_inline)) + { + _cnt = _my_tls.locals.pathbufs._counters; + } + /* This is the first thing called in the __except handler. The attribute + "returns_twice" makes sure that GCC disregards any register value set + earlier in the function, so this call serves as a register barrier. */ + void leave () __attribute__ ((returns_twice)); +}; +#else class san { san *_clemente; jmp_buf _context; - int _errno; - unsigned _c_cnt; - unsigned _w_cnt; + uint32_t _c_cnt; + uint32_t _w_cnt; public: - int setup (int myerrno = 0) __attribute__ ((always_inline)) + int setup () __attribute__ ((always_inline)) { _clemente = _my_tls.andreas; _my_tls.andreas = this; - _errno = myerrno; _c_cnt = _my_tls.locals.pathbufs.c_cnt; _w_cnt = _my_tls.locals.pathbufs.w_cnt; return __sjfault (_context); } void leave () __attribute__ ((always_inline)) { - if (_errno) - set_errno (_errno); /* Restore tls_pathbuf counters in case of error. */ _my_tls.locals.pathbufs.c_cnt = _c_cnt; _my_tls.locals.pathbufs.w_cnt = _w_cnt; @@ -331,17 +352,76 @@ public: ~myfault () __attribute__ ((always_inline)) { sebastian.reset (); } inline int faulted () __attribute__ ((always_inline)) { - return sebastian.setup (0); - } - inline int faulted (void const *obj, int myerrno = 0) __attribute__ ((always_inline)) - { - return !obj || !(*(const char **) obj) || sebastian.setup (myerrno); - } - inline int faulted (int myerrno) __attribute__ ((always_inline)) - { - return sebastian.setup (myerrno); + return sebastian.setup (); } }; +#endif + +/* Exception handling macros. These are required because SEH differs a lot + between 32 and 64 bit. Essentially, on 64 bit, we have to create compile + time SEH tables which define the handler and try/except labels, while on + 32 bit we can simply set up an SJLJ handler within the myfault class. */ +#define __mem_barrier __asm__ __volatile__ ("" ::: "memory") +#ifdef __x86_64__ +#define __try \ + { \ + __label__ __l_try, __l_except, __l_endtry; \ + __mem_barrier; \ + san __sebastian; \ + __asm__ goto ("\n" \ + " .seh_handler _ZN9exception7myfaultEP17_EXCEPTION_RECORDPvP8_CONTEXTP19_DISPATCHER_CONTEXT, @except \n" \ + " .seh_handlerdata \n" \ + " .long 1 \n" \ + " .rva %l[__l_try],%l[__l_endtry],%l[__l_except],%l[__l_except] \n" \ + " .seh_code \n" \ + : : : : __l_try, __l_endtry, __l_except); \ + { \ + __l_try: \ + __mem_barrier; + +#define __leave \ + goto __l_endtry + +#define __except(__errno) \ + goto __l_endtry; \ + } \ + { \ + __l_except: \ + __mem_barrier; \ + __sebastian.leave (); \ + if (__errno) \ + set_errno (__errno); + +#define __endtry \ + } \ + __l_endtry: \ + __mem_barrier; \ + } + +#else /* !__x86_64__ */ +#define __try \ + { \ + __label__ __l_endtry; \ + myfault efault; \ + if (!efault.faulted ()) \ + { + +#define __leave \ + goto __l_endtry + +#define __except(__errno) \ + goto __l_endtry; \ + } \ + { \ + if (__errno) \ + set_errno (__errno); + +#define __endtry \ + } \ + __l_endtry: \ + __mem_barrier; \ + } +#endif /* __x86_64__ */ class set_signal_arrived { diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 8e1e6271b..8d059144d 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -393,11 +393,13 @@ check_sanity_and_sync (per_process *p) api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %u > %u", p->api_major, cygwin_version.api_major); +#ifndef __x86_64__ /* This is a kludge to work around a version of _cygwin_common_crt0 which overwrote the cxx_malloc field with the local DLL copy. Hilarity ensues if the DLL is not loaded while the process is forking. */ __cygwin_user_data.cxx_malloc = &default_cygwin_cxx_malloc; +#endif } child_info NO_COPY *child_proc_info; @@ -517,14 +519,12 @@ initial_env () { char buf1[NT_MAX_PATH]; GetModuleFileName (NULL, buf1, NT_MAX_PATH); - strlwr (buf1); - strlwr (buf); char *p = strpbrk (buf, ":="); if (!p) p = (char *) "gdb.exe -nw"; else *p++ = '\0'; - if (strstr (buf1, buf)) + if (strcasestr (buf1, buf)) { error_start_init (p); jit_debug = true; @@ -798,10 +798,6 @@ dll_crt0_0 () _main_tls = &_my_tls; -#ifdef __x86_64__ - exception::install_myfault_handler (); -#endif - /* Initialize signal processing here, early, in the hopes that the creation of a thread early in the process will cause more predictability in memory layout for the main thread. */ @@ -936,14 +932,14 @@ dll_crt0_1 (void *) /* Allocate cygheap->fdtab */ dtable_init (); + /* Set internal locale to the environment settings. */ + initial_setlocale (); + uinfo_init (); /* initialize user info */ /* Connect to tty. */ tty::init_session (); - /* Set internal locale to the environment settings. */ - initial_setlocale (); - if (!__argc) { PWCHAR wline = GetCommandLineW (); @@ -1235,11 +1231,62 @@ do_exit (int status) myself.exit (n); } +/* When introducing support for -fuse-cxa-atexit with Cygwin 1.7.32 and + GCC 4.8.3-3, we defined __dso_value as &ImageBase. This supposedly allowed + a reproducible value which could also be easily evaluated in cygwin_atexit. + However, when building C++ applications with -fuse-cxa-atexit, G++ creates + calls to __cxa_atexit using the *address* of __dso_handle as DSO handle. + + So what we do here is this: A call to __cxa_atexit from the application + actually calls cygwin__cxa_atexit. From dso_handle (which is either + &__dso_handle, or __dso_handle == ImageBase or NULL) we fetch the dll + structure of the DLL. Then use dll::handle == ImageBase as the actual DSO + handle value in calls to __cxa_atexit and __cxa_finalize. + Thus, __cxa_atexit becomes entirely independent of the incoming value of + dso_handle, as long as it's *some* pointer into the DSO's address space. */ +extern "C" int +cygwin__cxa_atexit (void (*fn)(void *), void *obj, void *dso_handle) +{ + dll *d = dso_handle ? dlls.find (dso_handle) : NULL; + return __cxa_atexit (fn, obj, d ? d->handle : NULL); +} + +/* This function is only called for applications built with Cygwin versions + up to API 0.279. Starting with API 0.280 (Cygwin 1.7.33/1.8.6-2), atexit + is a statically linked function inside of libcygwin.a. The reason is that + the old method to fetch the caller return address is unreliable given GCCs + ability to perform tail call elimination. For the details, see the below + comment. The atexit replacement is defined in libcygwin.a to allow reliable + access to the correct DSO handle. */ extern "C" int cygwin_atexit (void (*fn) (void)) { int res; + dll *d = dlls.find ((void *) _my_tls.retaddr ()); +#ifdef __x86_64__ + /* x86_64 DLLs created with GCC 4.8.3-3 register __gcc_deregister_frame + as atexit function using a call to atexit, rather than __cxa_atexit. + Due to GCC's tail call optimizing, cygwin_atexit doesn't get the correct + return address on the stack. As a result it fails to get the HMODULE of + the caller and thus calls atexit rather than __cxa_atexit. Then, if the + module gets dlclosed, __cxa_finalize (called from dll_list::detach) can't + remove __gcc_deregister_frame from the atexit function chain. So at + process exit, __call_exitprocs calls __gcc_deregister_frame while the + module is already unloaded and the __gcc_deregister_frame function not + available ==> SEGV. + + This also occurs for other functions. + + Workaround: If dlls.find fails, try to find the dll entry of the DLL + containing fn. If that works, proceed by calling __cxa_atexit, otherwise + call atexit. + + This *should* be sufficiently safe. Ultimately, new applications will + use the statically linked atexit function though, as outlined above. */ + if (!d) + d = dlls.find ((void *) fn); +#endif res = d ? __cxa_atexit ((void (*) (void *)) fn, NULL, d->handle) : atexit (fn); return res; } diff --git a/winsup/cygwin/dir.cc b/winsup/cygwin/dir.cc index 01b9ab882..a8eeb2327 100644 --- a/winsup/cygwin/dir.cc +++ b/winsup/cygwin/dir.cc @@ -28,16 +28,16 @@ details. */ extern "C" int dirfd (DIR *dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (dir->__d_cookie != __DIRENT_COOKIE) + __try { - set_errno (EBADF); + if (dir->__d_cookie == __DIRENT_COOKIE) + return dir->__d_fd; syscall_printf ("-1 = dirfd (%p)", dir); - return -1; + set_errno (EINVAL); } - return dir->__d_fd; + __except (EFAULT) {} + __endtry + return -1; } /* Symbol kept for backward compatibility. Don't remove. Don't declare @@ -93,79 +93,86 @@ fdopendir (int fd) static int readdir_worker (DIR *dir, dirent *de) { - myfault efault; - if (efault.faulted ()) - return EFAULT; + int res = 0; - if (dir->__d_cookie != __DIRENT_COOKIE) + __try { - syscall_printf ("%p = readdir (%p)", NULL, dir); - return EBADF; - } - - de->d_ino = 0; - de->d_type = DT_UNKNOWN; - memset (&de->__d_unused1, 0, sizeof (de->__d_unused1)); - - int res = ((fhandler_base *) dir->__fh)->readdir (dir, de); - - if (res == ENMFILE) - { - if (!(dir->__flags & dirent_saw_dot)) + if (dir->__d_cookie != __DIRENT_COOKIE) { - strcpy (de->d_name, "."); - dir->__flags |= dirent_saw_dot; - dir->__d_position++; - res = 0; - } - else if (!(dir->__flags & dirent_saw_dot_dot)) - { - strcpy (de->d_name, ".."); - dir->__flags |= dirent_saw_dot_dot; - dir->__d_position++; - res = 0; - } - } - - if (!res && !de->d_ino) - { - bool is_dot = false; - bool is_dot_dot = false; - - if (de->d_name[0] == '.') - { - if (de->d_name[1] == '\0') - is_dot = true; - else if (de->d_name[1] == '.' && de->d_name[2] == '\0') - is_dot_dot = true; + syscall_printf ("%p = readdir (%p)", NULL, dir); + res = EBADF; + __leave; } - if (is_dot_dot && !(dir->__flags & dirent_isroot)) - de->d_ino = readdir_get_ino (((fhandler_base *) dir->__fh)->get_name (), - true); - else + de->d_ino = 0; + de->d_type = DT_UNKNOWN; + memset (&de->__d_unused1, 0, sizeof (de->__d_unused1)); + + res = ((fhandler_base *) dir->__fh)->readdir (dir, de); + + if (res == ENMFILE) { - /* Compute d_ino by combining filename hash with directory hash. */ - de->d_ino = ((fhandler_base *) dir->__fh)->get_ino (); - if (!is_dot && !is_dot_dot) + if (!(dir->__flags & dirent_saw_dot)) { - PUNICODE_STRING w32name = - ((fhandler_base *) dir->__fh)->pc.get_nt_native_path (); - DWORD devn = ((fhandler_base *) dir->__fh)->get_device (); - /* Paths below /proc don't have a Win32 pendant. */ - if (isproc_dev (devn)) - de->d_ino = hash_path_name (de->d_ino, L"/"); - else if (w32name->Buffer[w32name->Length / sizeof (WCHAR) - 1] - != L'\\') - de->d_ino = hash_path_name (de->d_ino, L"\\"); - de->d_ino = hash_path_name (de->d_ino, de->d_name); + strcpy (de->d_name, "."); + dir->__flags |= dirent_saw_dot; + dir->__d_position++; + res = 0; + } + else if (!(dir->__flags & dirent_saw_dot_dot)) + { + strcpy (de->d_name, ".."); + dir->__flags |= dirent_saw_dot_dot; + dir->__d_position++; + res = 0; } } - } - /* This fills out the old 32 bit d_ino field for old applications, - build under Cygwin before 1.5.x. */ - de->__d_internal1 = de->d_ino; + if (!res && !de->d_ino) + { + bool is_dot = false; + bool is_dot_dot = false; + + if (de->d_name[0] == '.') + { + if (de->d_name[1] == '\0') + is_dot = true; + else if (de->d_name[1] == '.' && de->d_name[2] == '\0') + is_dot_dot = true; + } + + if (is_dot_dot && !(dir->__flags & dirent_isroot)) + de->d_ino = readdir_get_ino (((fhandler_base *) + dir->__fh)->get_name (), + true); + else + { + /* Compute d_ino by combining filename hash with directory hash. */ + de->d_ino = ((fhandler_base *) dir->__fh)->get_ino (); + if (!is_dot && !is_dot_dot) + { + PUNICODE_STRING w32name = + ((fhandler_base *) dir->__fh)->pc.get_nt_native_path (); + DWORD devn = ((fhandler_base *) dir->__fh)->get_device (); + /* Paths below /proc don't have a Win32 pendant. */ + if (isproc_dev (devn)) + de->d_ino = hash_path_name (de->d_ino, L"/"); + else if (w32name->Buffer[w32name->Length / sizeof (WCHAR) - 1] + != L'\\') + de->d_ino = hash_path_name (de->d_ino, L"\\"); + de->d_ino = hash_path_name (de->d_ino, de->d_name); + } + } + } + /* This fills out the old 32 bit d_ino field for old applications, + build under Cygwin before 1.5.x. */ + de->__d_internal1 = de->d_ino; + } + __except (NO_ERROR) + { + res = EFAULT; + } + __endtry return res; } @@ -200,13 +207,15 @@ readdir_r (DIR *__restrict dir, dirent *__restrict de, dirent **__restrict ode) extern "C" long telldir (DIR *dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (dir->__d_cookie != __DIRENT_COOKIE) - return 0; - return ((fhandler_base *) dir->__fh)->telldir (dir); + __try + { + if (dir->__d_cookie == __DIRENT_COOKIE) + return ((fhandler_base *) dir->__fh)->telldir (dir); + set_errno (EBADF); + } + __except (EFAULT) {} + __endtry + return -1; } /* telldir was never defined using off_t in POSIX, only in early versions @@ -222,14 +231,17 @@ telldir64 (DIR *dir) extern "C" void seekdir (DIR *dir, long loc) { - myfault efault; - if (efault.faulted (EFAULT)) - return; - - if (dir->__d_cookie != __DIRENT_COOKIE) - return; - dir->__flags &= dirent_info_mask; - return ((fhandler_base *) dir->__fh)->seekdir (dir, loc); + __try + { + if (dir->__d_cookie == __DIRENT_COOKIE) + { + dir->__flags &= dirent_info_mask; + ((fhandler_base *) dir->__fh)->seekdir (dir, loc); + } + set_errno (EINVAL); /* Diagnosis */ + } + __except (EFAULT) {} + __endtry } /* seekdir was never defined using off_t in POSIX, only in early versions @@ -245,42 +257,45 @@ seekdir64 (DIR *dir, off_t loc) extern "C" void rewinddir (DIR *dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return; - - if (dir->__d_cookie != __DIRENT_COOKIE) - return; - dir->__flags &= dirent_info_mask; - return ((fhandler_base *) dir->__fh)->rewinddir (dir); + __try + { + if (dir->__d_cookie == __DIRENT_COOKIE) + { + dir->__flags &= dirent_info_mask; + ((fhandler_base *) dir->__fh)->rewinddir (dir); + } + set_errno (EINVAL); /* Diagnosis */ + } + __except (EFAULT) {} + __endtry } /* closedir: POSIX 5.1.2.1 */ extern "C" int closedir (DIR *dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (dir->__d_cookie != __DIRENT_COOKIE) + __try { + if (dir->__d_cookie == __DIRENT_COOKIE) + { + /* Reset the marker in case the caller tries to use `dir' again. */ + dir->__d_cookie = 0; + + int res = ((fhandler_base *) dir->__fh)->closedir (dir); + + close (dir->__d_fd); + free (dir->__d_dirname); + free (dir->__d_dirent); + free (dir); + syscall_printf ("%R = closedir(%p)", res, dir); + return res; + } set_errno (EBADF); - syscall_printf ("%R = closedir(%p)", -1, dir); - return -1; } - - /* Reset the marker in case the caller tries to use `dir' again. */ - dir->__d_cookie = 0; - - int res = ((fhandler_base *) dir->__fh)->closedir (dir); - - close (dir->__d_fd); - free (dir->__d_dirname); - free (dir->__d_dirent); - free (dir); - syscall_printf ("%R = closedir(%p)", res, dir); - return res; + __except (EFAULT) {} + __endtry + syscall_printf ("%R = closedir(%p)", -1, dir); + return -1; } /* mkdir: POSIX 5.4.1.1 */ @@ -291,43 +306,42 @@ mkdir (const char *dir, mode_t mode) fhandler_base *fh = NULL; tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - /* POSIX says mkdir("symlink-to-missing/") should create the - directory "missing", but Linux rejects it with EEXIST. Copy - Linux behavior for now. */ - - if (!*dir) + __try { - set_errno (ENOENT); - goto done; - } - if (isdirsep (dir[strlen (dir) - 1])) - { - /* This converts // to /, but since both give EEXIST, we're okay. */ - char *buf; - char *p = stpcpy (buf = tp.c_get (), dir) - 1; - dir = buf; - while (p > dir && isdirsep (*p)) - *p-- = '\0'; - } - if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) - goto done; /* errno already set */; + /* POSIX says mkdir("symlink-to-missing/") should create the + directory "missing", but Linux rejects it with EEXIST. Copy + Linux behavior for now. */ - if (fh->error ()) - { - debug_printf ("got %d error from build_fh_name", fh->error ()); - set_errno (fh->error ()); - } - else if (has_dot_last_component (dir, true)) - set_errno (fh->exists () ? EEXIST : ENOENT); - else if (!fh->mkdir (mode)) - res = 0; - delete fh; + if (!*dir) + { + set_errno (ENOENT); + __leave; + } + if (isdirsep (dir[strlen (dir) - 1])) + { + /* This converts // to /, but since both give EEXIST, we're okay. */ + char *buf; + char *p = stpcpy (buf = tp.c_get (), dir) - 1; + dir = buf; + while (p > dir && isdirsep (*p)) + *p-- = '\0'; + } + if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) + __leave; /* errno already set */; - done: + if (fh->error ()) + { + debug_printf ("got %d error from build_fh_name", fh->error ()); + set_errno (fh->error ()); + } + else if (has_dot_last_component (dir, true)) + set_errno (fh->exists () ? EEXIST : ENOENT); + else if (!fh->mkdir (mode)) + res = 0; + delete fh; + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = mkdir(%s, %d)", res, dir, mode); return res; } @@ -339,33 +353,28 @@ rmdir (const char *dir) int res = -1; fhandler_base *fh = NULL; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) - goto done; /* errno already set */; - - if (fh->error ()) + __try { - debug_printf ("got %d error from build_fh_name", fh->error ()); - set_errno (fh->error ()); - } - else if (!fh->exists ()) - set_errno (ENOENT); - else if (has_dot_last_component (dir, false)) - set_errno (EINVAL); - else if (isdev_dev (fh->dev ())) - { - set_errno (ENOTEMPTY); - goto done; - } - else if (!fh->rmdir ()) - res = 0; + if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW))) + __leave; /* errno already set */; - delete fh; - - done: + if (fh->error ()) + { + debug_printf ("got %d error from build_fh_name", fh->error ()); + set_errno (fh->error ()); + } + else if (!fh->exists ()) + set_errno (ENOENT); + else if (has_dot_last_component (dir, false)) + set_errno (EINVAL); + else if (isdev_dev (fh->dev ())) + set_errno (ENOTEMPTY); + else if (!fh->rmdir ()) + res = 0; + delete fh; + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = rmdir(%s)", res, dir); return res; } diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index c27adf675..46d654f0b 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -1,7 +1,7 @@ /* dlfcn.cc Copyright 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010, 2011, 2013 Red Hat, Inc. + 2010, 2011, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -20,7 +20,7 @@ details. */ #include "tls_pbuf.h" #include "ntdll.h" -static void __stdcall +static void set_dl_error (const char *str) { strcpy (_my_tls.locals.dl_buffer, strerror (get_errno ())); @@ -30,10 +30,10 @@ set_dl_error (const char *str) /* Look for an executable file given the name and the environment variable to use for searching (eg., PATH); returns the full pathname (static buffer) if found or NULL if not. */ -inline const char * __stdcall +inline const char * check_path_access (const char *mywinenv, const char *name, path_conv& buf) { - return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_CWD | FE_DLL); + return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_DLL); } /* Search LD_LIBRARY_PATH for dll, if it exists. Search /usr/bin and /usr/lib @@ -41,7 +41,7 @@ check_path_access (const char *mywinenv, const char *name, path_conv& buf) static inline bool gfpod_helper (const char *name, path_conv &real_filename) { - if (isabspath (name)) + if (strchr (name, '/')) real_filename.check (name, PC_SYM_FOLLOW | PC_NULLEMPTY); else if (!check_path_access ("LD_LIBRARY_PATH=", name, real_filename)) check_path_access ("/usr/bin:/usr/lib", name, real_filename); @@ -50,7 +50,7 @@ gfpod_helper (const char *name, path_conv &real_filename) return !real_filename.error; } -static bool __stdcall +static bool get_full_path_of_dll (const char* str, path_conv &real_filename) { int len = strlen (str); @@ -135,6 +135,7 @@ dlopen (const char *name, int flags) if (last_bs && !wcschr (last_bs, L'.')) wcscat (last_bs, L"."); +#ifndef __x86_64__ /* Workaround for broken DLLs built against Cygwin versions 1.7.0-49 up to 1.7.0-57. They override the cxx_malloc pointer in their DLL initialization code even if loaded dynamically. This is a @@ -150,18 +151,20 @@ dlopen (const char *name, int flags) /* Store original cxx_malloc pointer. */ struct per_process_cxx_malloc *tmp_malloc; tmp_malloc = __cygwin_user_data.cxx_malloc; +#endif - if (!(flags & RTLD_NOLOAD) - || (ret = GetModuleHandleW (path)) != NULL) - { - ret = (void *) LoadLibraryW (path); - if (ret && (flags & RTLD_NODELETE)) - GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN, path, - (HMODULE *) &ret); - } + if (flags & RTLD_NOLOAD) + GetModuleHandleExW (0, path, (HMODULE *) &ret); + else + ret = (void *) LoadLibraryW (path); + if (ret && (flags & RTLD_NODELETE)) + GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN, path, + (HMODULE *) &ret); +#ifndef __x86_64__ /* Restore original cxx_malloc pointer. */ __cygwin_user_data.cxx_malloc = tmp_malloc; +#endif if (!ret) __seterrno (); diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 3fcb448b9..51ef186a3 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -640,7 +640,7 @@ dll_dllcrt0_1 (VOID *x) when loaded either statically or dynamically. Because this leaves a stale pointer into demapped memory space if the DLL is unloaded by a call to dlclose, we prevent this happening for dynamically - loaded DLLS in dlopen by saving and restoring cxx_malloc around + loaded DLLs in dlopen by saving and restoring cxx_malloc around the call to LoadLibrary, which invokes the DLL's startup sequence. Modern DLLs won't even attempt to override the pointer when loaded statically, but will write their overrides directly into the diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 0dc548c3d..17ed51f9a 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -371,9 +371,10 @@ dtable::init_std_file_from_handle (int fd, HANDLE handle) FILE_ACCESS_INFORMATION fai; int openflags = O_BINARY; - /* Console windows are not kernel objects, so the access mask returned - by NtQueryInformationFile is meaningless. CMD always hands down - stdin handles as R/O handles, but our tty slave sides are R/W. */ + /* Console windows are no kernel objects up to Windows 7/2008R2, so the + access mask returned by NtQueryInformationFile is meaningless. CMD + always hands down stdin handles as R/O handles, but our tty slave + sides are R/W. */ if (fh->is_tty ()) { openflags |= O_RDWR; diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index f56d19517..9b9be4a00 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -2,7 +2,7 @@ process's environment. Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for @@ -396,18 +396,15 @@ win_env::add_cache (const char *in_posix, const char *in_native) if (in_native) { native = (char *) realloc (native, namelen + 1 + strlen (in_native)); - strcpy (native, name); - strcpy (native + namelen, in_native); + stpcpy (stpcpy (native, name), in_native); } else { tmp_pathbuf tp; char *buf = tp.c_get (); - strcpy (buf, name + namelen); towin32 (in_posix, buf, NT_MAX_PATH); native = (char *) realloc (native, namelen + 1 + strlen (buf)); - strcpy (native, name); - strcpy (native + namelen, buf); + stpcpy (stpcpy (native, name), buf); } MALLOC_CHECK; if (immediate && cygwin_finished_initializing) @@ -480,8 +477,9 @@ posify_maybe (char **here, const char *value, char *outenv) { /* The conversion routine removed elements from a path list so we have to recalculate the windows path to remove elements there, too. */ - char cleanvalue[strlen (value) + 1]; - conv->towin32 (newvalue, cleanvalue, sizeof cleanvalue); + tmp_pathbuf tp; + char *cleanvalue = tp.c_get (); + conv->towin32 (newvalue, cleanvalue, NT_MAX_PATH); conv->add_cache (newvalue, cleanvalue); } @@ -664,19 +662,22 @@ _addenv (const char *name, const char *value, int overwrite) extern "C" int putenv (char *str) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (*str) + __try { - char *eq = strchr (str, '='); - if (eq) - return _addenv (str, eq + 1, -1); + if (*str) + { + char *eq = strchr (str, '='); + if (eq) + return _addenv (str, eq + 1, -1); - /* Remove str from the environment. */ - unsetenv (str); + /* Remove str from the environment. */ + unsetenv (str); + } + return 0; } - return 0; + __except (EFAULT) {} + __endtry + return -1; } /* Set the value of the environment variable "name" to be @@ -684,15 +685,18 @@ putenv (char *str) extern "C" int setenv (const char *name, const char *value, int overwrite) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!name || !*name || strchr (name, '=')) + __try { - set_errno (EINVAL); - return -1; + if (!name || !*name || strchr (name, '=')) + { + set_errno (EINVAL); + __leave; + } + return _addenv (name, value, !!overwrite); } - return _addenv (name, value, !!overwrite); + __except (EFAULT) {} + __endtry + return -1; } /* Delete environment variable "name". */ @@ -701,22 +705,26 @@ unsetenv (const char *name) { register char **e; int offset; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!name || *name == '\0' || strchr (name, '=')) + + __try { - set_errno (EINVAL); - return -1; + if (!name || *name == '\0' || strchr (name, '=')) + { + set_errno (EINVAL); + __leave; + } + + while (my_findenv (name, &offset)) /* if set multiple times */ + /* Move up the rest of the array */ + for (e = cur_environ () + offset; ; e++) + if (!(*e = *(e + 1))) + break; + + return 0; } - - while (my_findenv (name, &offset)) /* if set multiple times */ - /* Move up the rest of the array */ - for (e = cur_environ () + offset; ; e++) - if (!(*e = *(e + 1))) - break; - - return 0; + __except (EFAULT) {} + __endtry + return -1; } /* Minimal list of Windows vars which must be converted to uppercase. @@ -822,96 +830,101 @@ environ_init (char **envp, int envc) bool envp_passed_in; bool got_something_from_registry; static char NO_COPY cygterm[] = "TERM=cygwin"; - myfault efault; tmp_pathbuf tp; - if (efault.faulted ()) - api_fatal ("internal error reading the windows environment - too many environment variables?"); - - char *tmpbuf = tp.t_get (); - got_something_from_registry = regopt (L"default", tmpbuf); - if (myself->progname[0]) - got_something_from_registry = regopt (myself->progname, tmpbuf) - || got_something_from_registry; - - if (!envp) - envp_passed_in = 0; - else + __try { - envc++; - envc *= sizeof (char *); - char **newenv = (char **) malloc (envc); - memcpy (newenv, envp, envc); - cfree (envp); + char *tmpbuf = tp.t_get (); + got_something_from_registry = regopt (L"default", tmpbuf); + if (myself->progname[0]) + got_something_from_registry = regopt (myself->progname, tmpbuf) + || got_something_from_registry; - /* Older applications relied on the fact that cygwin malloced elements of the - environment list. */ - envp = newenv; - if (ENVMALLOC) - for (char **e = newenv; *e; e++) - { - char *p = *e; - *e = strdup (p); - cfree (p); - } - envp_passed_in = 1; - goto out; + if (!envp) + envp_passed_in = 0; + else + { + envc++; + envc *= sizeof (char *); + char **newenv = (char **) malloc (envc); + memcpy (newenv, envp, envc); + cfree (envp); + + /* Older applications relied on the fact that cygwin malloced elements of the + environment list. */ + envp = newenv; + if (ENVMALLOC) + for (char **e = newenv; *e; e++) + { + char *p = *e; + *e = strdup (p); + cfree (p); + } + envp_passed_in = 1; + goto out; + } + + /* Allocate space for environment + trailing NULL + CYGWIN env. */ + lastenviron = envp = (char **) malloc ((4 + (envc = 100)) * sizeof (char *)); + + rawenv = GetEnvironmentStringsW (); + if (!rawenv) + { + system_printf ("GetEnvironmentStrings returned NULL, %E"); + return; + } + debug_printf ("GetEnvironmentStrings returned %p", rawenv); + + /* Current directory information is recorded as variables of the + form "=X:=X:\foo\bar; these must be changed into something legal + (we could just ignore them but maybe an application will + eventually want to use them). */ + for (i = 0, w = rawenv; *w != L'\0'; w = wcschr (w, L'\0') + 1, i++) + { + sys_wcstombs_alloc (&newp, HEAP_NOTHEAP, w); + if (i >= envc) + envp = (char **) realloc (envp, (4 + (envc += 100)) * sizeof (char *)); + envp[i] = newp; + if (*newp == '=') + *newp = '!'; + char *eq = strchrnul (newp, '='); + ucenv (newp, eq); /* uppercase env vars which need it */ + if (*newp == 'T' && strncmp (newp, "TERM=", 5) == 0) + sawTERM = 1; + else if (*newp == 'C' && strncmp (newp, "CYGWIN=", 7) == 0) + parse_options (newp + 7); + if (*eq) + posify_maybe (envp + i, *++eq ? eq : --eq, tmpbuf); + debug_printf ("%p: %s", envp[i], envp[i]); + } + + if (!sawTERM) + envp[i++] = strdup (cygterm); + envp[i] = NULL; + FreeEnvironmentStringsW (rawenv); + + out: + findenv_func = (char * (*)(const char*, int*)) my_findenv; + __cygwin_environ = envp; + update_envptrs (); + if (envp_passed_in) + { + p = getenv ("CYGWIN"); + if (p) + parse_options (p); + } + + if (got_something_from_registry) + parse_options (NULL); /* possibly export registry settings to + environment */ + MALLOC_CHECK; } - - /* Allocate space for environment + trailing NULL + CYGWIN env. */ - lastenviron = envp = (char **) malloc ((4 + (envc = 100)) * sizeof (char *)); - - rawenv = GetEnvironmentStringsW (); - if (!rawenv) + __except (NO_ERROR) { - system_printf ("GetEnvironmentStrings returned NULL, %E"); - return; + api_fatal ("internal error reading the windows environment " + "- too many environment variables?"); } - debug_printf ("GetEnvironmentStrings returned %p", rawenv); - - /* Current directory information is recorded as variables of the - form "=X:=X:\foo\bar; these must be changed into something legal - (we could just ignore them but maybe an application will - eventually want to use them). */ - for (i = 0, w = rawenv; *w != L'\0'; w = wcschr (w, L'\0') + 1, i++) - { - sys_wcstombs_alloc (&newp, HEAP_NOTHEAP, w); - if (i >= envc) - envp = (char **) realloc (envp, (4 + (envc += 100)) * sizeof (char *)); - envp[i] = newp; - if (*newp == '=') - *newp = '!'; - char *eq = strchrnul (newp, '='); - ucenv (newp, eq); /* uppercase env vars which need it */ - if (*newp == 'T' && strncmp (newp, "TERM=", 5) == 0) - sawTERM = 1; - else if (*newp == 'C' && strncmp (newp, "CYGWIN=", 7) == 0) - parse_options (newp + 7); - if (*eq) - posify_maybe (envp + i, *++eq ? eq : --eq, tmpbuf); - debug_printf ("%p: %s", envp[i], envp[i]); - } - - if (!sawTERM) - envp[i++] = strdup (cygterm); - envp[i] = NULL; - FreeEnvironmentStringsW (rawenv); - -out: - findenv_func = (char * (*)(const char*, int*)) my_findenv; - __cygwin_environ = envp; - update_envptrs (); - if (envp_passed_in) - { - p = getenv ("CYGWIN"); - if (p) - parse_options (p); - } - - if (got_something_from_registry) - parse_options (NULL); /* possibly export registry settings to - environment */ - MALLOC_CHECK; + __endtry } /* Function called by qsort to sort environment strings. */ @@ -1017,6 +1030,21 @@ spenv::retrieve (bool no_envblock, const char *const env) return getwinenveq (name, namelen, HEAP_1_STR); } +static inline int +raise_envblock (int new_tl, PWCHAR &envblock, PWCHAR &s) +{ + int tl = new_tl + 100; + PWCHAR new_envblock = + (PWCHAR) realloc (envblock, (2 + tl) * sizeof (WCHAR)); + /* If realloc moves the block, move `s' with it. */ + if (new_envblock != envblock) + { + s += new_envblock - envblock; + envblock = new_envblock; + } + return tl; +} + #define SPENVS_SIZE (sizeof (spenvs) / sizeof (spenvs[0])) /* Create a Windows-style environment block, i.e. a typical character buffer @@ -1081,14 +1109,14 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, for (unsigned i = 0; i < SPENVS_SIZE; i++) if (!saw_spenv[i] && (spenvs[i].force_into_environment || cygheap->user.issetuid ())) { - *dstp = spenvs[i].retrieve (false); - if (*dstp && *dstp != env_dontadd) - { - *pass_dstp++ = *dstp; - tl += strlen (*dstp) + 1; - dstp++; - } - } + *dstp = spenvs[i].retrieve (false); + if (*dstp && *dstp != env_dontadd) + { + *pass_dstp++ = *dstp; + tl += strlen (*dstp) + 1; + dstp++; + } + } envc = dstp - newenv; /* Number of entries in newenv */ assert ((size_t) envc <= (n + SPENVS_SIZE)); @@ -1111,6 +1139,7 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, PWCHAR s; envblock = (PWCHAR) malloc ((2 + tl) * sizeof (WCHAR)); int new_tl = 0; + bool saw_PATH = false; for (srcp = pass_env, s = envblock; *srcp; srcp++) { const char *p; @@ -1129,7 +1158,17 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, /* See if this entry requires posix->win32 conversion. */ conv = getwinenv (*srcp, rest, &temp); if (conv) - p = conv->native; /* Use win32 path */ + { + p = conv->native; /* Use win32 path */ + /* Does PATH exist in the environment? */ + if (**srcp == 'P') + { + /* And is it non-empty? */ + if (!conv->native || !conv->native[0]) + continue; + saw_PATH = true; + } + } else p = *srcp; /* Don't worry about it */ @@ -1138,19 +1177,9 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, /* See if we need to increase the size of the block. */ if (new_tl > tl) - { - tl = new_tl + 100; - PWCHAR new_envblock = - (PWCHAR) realloc (envblock, (2 + tl) * sizeof (WCHAR)); - /* If realloc moves the block, move `s' with it. */ - if (new_envblock != envblock) - { - s += new_envblock - envblock; - envblock = new_envblock; - } - } + tl = raise_envblock (new_tl, envblock, s); - int slen = sys_mbstowcs (s, len, p); + len = sys_mbstowcs (s, len, p); /* See if environment variable is "special" in a Windows sense. Under NT, the current directories for visited drives are stored @@ -1159,7 +1188,17 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc, if (s[0] == L'!' && (iswdrive (s + 1) || (s[1] == L':' && s[2] == L':')) && s[3] == L'=') *s = L'='; - s += slen + 1; + s += len + 1; + } + /* If PATH doesn't exist in the environment, add a PATH with just + Cygwin's bin dir to the Windows env to allow loading system DLLs + during execve. */ + if (!saw_PATH) + { + new_tl += cygheap->installation_dir_len + 5; + if (new_tl > tl) + tl = raise_envblock (new_tl, envblock, s); + s = wcpcpy (wcpcpy (s, L"PATH="), cygheap->installation_dir) + 1; } *s = L'\0'; /* Two null bytes at the end */ assert ((s - envblock) <= tl); /* Detect if we somehow ran over end diff --git a/winsup/cygwin/exception.h b/winsup/cygwin/exception.h index 83cb21fcf..3686bb0b2 100644 --- a/winsup/cygwin/exception.h +++ b/winsup/cygwin/exception.h @@ -94,8 +94,10 @@ to install your own exception filter. This one is documented. a teensy bit of detail of the innards of exception handling (i.e. what we have to do). */ -typedef int (exception_handler) (EXCEPTION_RECORD *, struct _exception_list *, - CONTEXT *, void *); +typedef EXCEPTION_DISPOSITION (exception_handler) (EXCEPTION_RECORD *, + struct _exception_list *, + CONTEXT *, + void *); typedef struct _exception_list { @@ -104,70 +106,51 @@ typedef struct _exception_list } exception_list; extern exception_list *_except_list asm ("%fs:0"); -#else -typedef void exception_list; -#endif /* !__x86_64 */ +typedef void *PDISPATCHER_CONTEXT; class exception { -#ifdef __x86_64__ - static LONG myfault_handle (LPEXCEPTION_POINTERS ep); -#else exception_list el; exception_list *save; -#endif /* __x86_64__ */ - static int handle (EXCEPTION_RECORD *, exception_list *, CONTEXT *, void *); + static EXCEPTION_DISPOSITION handle (EXCEPTION_RECORD *, exception_list *, + CONTEXT *, PDISPATCHER_CONTEXT); +public: + exception () __attribute__ ((always_inline)) + { + /* Install SEH handler. */ + save = _except_list; + el.handler = handle; + el.prev = _except_list; + _except_list = ⪙ + }; + ~exception () __attribute__ ((always_inline)) { _except_list = save; } +}; + +#else /* __x86_64__ */ + +#define exception_list void +typedef struct _DISPATCHER_CONTEXT *PDISPATCHER_CONTEXT; + +class exception +{ + static EXCEPTION_DISPOSITION myfault (EXCEPTION_RECORD *, exception_list *, + CONTEXT *, PDISPATCHER_CONTEXT); + static EXCEPTION_DISPOSITION handle (EXCEPTION_RECORD *, exception_list *, + CONTEXT *, PDISPATCHER_CONTEXT); public: exception () __attribute__ ((always_inline)) { /* Install SEH handler. */ -#ifdef __x86_64__ asm volatile ("\n\ 1: \n\ .seh_handler \ - _ZN9exception6handleEP17_EXCEPTION_RECORDPvP8_CONTEXTS2_, \ + _ZN9exception6handleEP17_EXCEPTION_RECORDPvP8_CONTEXTP19_DISPATCHER_CONTEXT, \ @except \n\ .seh_handlerdata \n\ .long 1 \n\ .rva 1b, 2f, 2f, 2f \n\ .seh_code \n"); -#else - save = _except_list; - el.handler = handle; - el.prev = _except_list; - _except_list = ⪙ -#endif /* __x86_64__ */ }; -#ifdef __x86_64__ - static void install_myfault_handler () __attribute__ ((always_inline)) - { - /* Install myfault exception handler as VEH. Here's what happens: - Some Windows DLLs (advapi32, for instance) are using SEH to catch - exceptions inside its own functions. If we install a VEH handler - to catch all exceptions, our Cygwin VEH handler would illegitimatly - handle exceptions inside of Windows DLLs which are usually handled - by its own SEH handler. So, for standard exceptions we use an SEH - handler as installed in the constructor above so as not to override - the SEH handlers in Windows DLLs. - But we have a special case, myfault handling. The myfault handling - catches exceptions inside of the Cygwin DLL, some of them entirely - expected as in verifyable_object_isvalid. The ultimately right thing - to do would be to install SEH handlers for each of these cases. - But there are two problems with that: - - 1. It would be a massive and, partially unreliable change in the - calling functions due to the incomplete SEH support in GCC. - - 2. It doesn't always work. Certain DLLs appear to call Cygwin - functions during DLL initialization while the SEH handler is - not installed in the active call frame. For these cases we - need a more generic approach. - - So, what we do here is to install a myfault VEH handler. This - function is called from dll_crt0_0, so the myfault handler is - available very early. */ - AddVectoredExceptionHandler (1, myfault_handle); - } ~exception () __attribute__ ((always_inline)) { asm volatile ("\n\ @@ -175,11 +158,10 @@ public: 2: \n\ nop \n"); } -#else - ~exception () __attribute__ ((always_inline)) { _except_list = save; } -#endif /* !__x86_64__ */ }; +#endif /* !__x86_64__ */ + class cygwin_exception { PUINT_PTR framep; diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 0e8a977d4..4e9c77036 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -341,38 +341,41 @@ void cygwin_exception::dumpstack () { static bool already_dumped; - myfault efault; - if (efault.faulted ()) - return; - if (already_dumped || cygheap->rlim_core == 0Ul) - return; - already_dumped = true; - open_stackdumpfile (); - - if (e) - dump_exception (); - - int i; - - thestack.init (framep, 1, ctx); /* Initialize from the input CONTEXT */ -#ifdef __x86_64__ - small_printf ("Stack trace:\r\nFrame Function Args\r\n"); -#else - small_printf ("Stack trace:\r\nFrame Function Args\r\n"); -#endif - for (i = 0; i < 16 && thestack++; i++) + __try { - small_printf (_AFMT " " _AFMT, thestack.sf.AddrFrame.Offset, - thestack.sf.AddrPC.Offset); - for (unsigned j = 0; j < NPARAMS; j++) - small_printf ("%s" _AFMT, j == 0 ? " (" : ", ", thestack.sf.Params[j]); - small_printf (")\r\n"); + if (already_dumped || cygheap->rlim_core == 0Ul) + return; + already_dumped = true; + open_stackdumpfile (); + + if (e) + dump_exception (); + + int i; + + thestack.init (framep, 1, ctx); /* Initialize from the input CONTEXT */ +#ifdef __x86_64__ + small_printf ("Stack trace:\r\nFrame Function Args\r\n"); +#else + small_printf ("Stack trace:\r\nFrame Function Args\r\n"); +#endif + for (i = 0; i < 16 && thestack++; i++) + { + small_printf (_AFMT " " _AFMT, thestack.sf.AddrFrame.Offset, + thestack.sf.AddrPC.Offset); + for (unsigned j = 0; j < NPARAMS; j++) + small_printf ("%s" _AFMT, j == 0 ? " (" : ", ", + thestack.sf.Params[j]); + small_printf (")\r\n"); + } + small_printf ("End of stack trace%s\n", + i == 16 ? " (more stack frames may be present)" : ""); + if (h) + NtClose (h); } - small_printf ("End of stack trace%s\n", - i == 16 ? " (more stack frames may be present)" : ""); - if (h) - NtClose (h); + __except (NO_ERROR) {} + __endtry } bool @@ -549,40 +552,24 @@ rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e) #endif /* __x86_64 */ #ifdef __x86_64__ -/* myfault vectored exception handler */ -LONG -exception::myfault_handle (LPEXCEPTION_POINTERS ep) +/* myfault exception handler. */ +EXCEPTION_DISPOSITION +exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, + PDISPATCHER_CONTEXT dispatch) { - _cygtls& me = _my_tls; - - if (me.andreas) - { - /* Only handle the minimum amount of exceptions the myfault handler - was designed for. */ - switch (ep->ExceptionRecord->ExceptionCode) - { - case STATUS_ACCESS_VIOLATION: - case STATUS_DATATYPE_MISALIGNMENT: -#if 0 - /* PAGE_GUARD-based stack commits are based on structured exception - handling. Short-circuiting STATUS_STACK_OVERFLOW in a vectored - exception handler disables that, which can ultimately result in - a spurious SEGV. */ - case STATUS_STACK_OVERFLOW: -#endif - case STATUS_ARRAY_BOUNDS_EXCEEDED: - me.andreas->leave (); /* Return from a "san" caught fault */ - default: - break; - } - } - return EXCEPTION_CONTINUE_SEARCH; + PSCOPE_TABLE table = (PSCOPE_TABLE) dispatch->HandlerData; + RtlUnwindEx (frame, + (char *) dispatch->ImageBase + table->ScopeRecord[0].JumpTarget, + e, 0, in, dispatch->HistoryTable); + /* NOTREACHED, make gcc happy. */ + return ExceptionContinueSearch; } -#endif /* __x86_64 */ +#endif /* Main exception handler. */ -int -exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *) +EXCEPTION_DISPOSITION +exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, + PDISPATCHER_CONTEXT dispatch) { static bool NO_COPY debugging; _cygtls& me = _my_tls; diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index 47b18809c..fd0c46864 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -243,11 +243,13 @@ cygwin_internal (cygwin_getinfo_types t, ...) break; case CW_USER_DATA: +#ifndef __x86_64__ /* This is a kludge to work around a version of _cygwin_common_crt0 which overwrote the cxx_malloc field with the local DLL copy. Hilarity ensues if the DLL is not loaded like while the process is forking. */ __cygwin_user_data.cxx_malloc = &default_cygwin_cxx_malloc; +#endif res = (uintptr_t) &__cygwin_user_data; break; @@ -572,6 +574,10 @@ cygwin_internal (cygwin_getinfo_types t, ...) } break; + case CW_FIXED_ATEXIT: + res = 0; + break; + default: set_errno (ENOSYS); } diff --git a/winsup/cygwin/fcntl.cc b/winsup/cygwin/fcntl.cc index ea9a71a5e..babb06424 100644 --- a/winsup/cygwin/fcntl.cc +++ b/winsup/cygwin/fcntl.cc @@ -1,7 +1,7 @@ /* fcntl.cc: fcntl syscall Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, - 2010, 2011, 2012, 2013 Red Hat, Inc. + 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -28,53 +28,54 @@ fcntl64 (int fd, int cmd, ...) pthread_testcancel (); - debug_printf ("fcntl(%d, %d, ...)", fd, cmd); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - cygheap_fdget cfd (fd, true); - if (cfd < 0) - goto done; - - /* FIXME? All numerical args to fcntl are defined as long on Linux. - This relies on a really dirty trick on x86_64: A 32 bit mov to - a register (e.g. mov $1, %edx) always sets the high 32 bit to 0. - We're following the Linux lead here since the third arg to any - function is in a register anyway (%r8 in MS ABI). That's the easy - case which is covered here by always reading the arg with - sizeof (intptr_t) == sizeof (long) == sizeof (void*) which matches - all targets. - - However, the POSIX standard defines all numerical args as type int. - If we take that literally, we had a (small) problem on 64 bit, since - sizeof (void*) != sizeof (int). If we would like to follow POSIX - more closely than Linux, we'd have to call va_arg on a per cmd basis. */ - - va_start (args, cmd); - arg = va_arg (args, intptr_t); - va_end (args); - - switch (cmd) + __try { - case F_DUPFD: - case F_DUPFD_CLOEXEC: - if (arg >= 0 && arg < OPEN_MAX_MAX) + + debug_printf ("fcntl(%d, %d, ...)", fd, cmd); + cygheap_fdget cfd (fd, true); + if (cfd < 0) + __leave; + + /* FIXME? All numerical args to fcntl are defined as long on Linux. + This relies on a really dirty trick on x86_64: A 32 bit mov to + a register (e.g. mov $1, %edx) always sets the high 32 bit to 0. + We're following the Linux lead here since the third arg to any + function is in a register anyway (%r8 in MS ABI). That's the easy + case which is covered here by always reading the arg with + sizeof (intptr_t) == sizeof (long) == sizeof (void*) which matches + all targets. + + However, the POSIX standard defines all numerical args as type int. + If we take that literally, we had a (small) problem on 64 bit, since + sizeof (void*) != sizeof (int). If we would like to follow POSIX more + closely than Linux, we'd have to call va_arg on a per cmd basis. */ + + va_start (args, cmd); + arg = va_arg (args, intptr_t); + va_end (args); + + switch (cmd) { - int flags = cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0; - res = cygheap->fdtab.dup3 (fd, cygheap_fdnew ((arg) - 1), flags); + case F_DUPFD: + case F_DUPFD_CLOEXEC: + if (arg >= 0 && arg < OPEN_MAX_MAX) + { + int flags = cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0; + res = cygheap->fdtab.dup3 (fd, cygheap_fdnew ((arg) - 1), flags); + } + else + { + set_errno (EINVAL); + res = -1; + } + break; + default: + res = cfd->fcntl (cmd, arg); + break; } - else - { - set_errno (EINVAL); - res = -1; - } - break; - default: - res = cfd->fcntl (cmd, arg); - break; } -done: + __except (EFAULT) {} + __endtry syscall_printf ("%R = fcntl(%d, %d, %ly)", res, fd, cmd, arg); return res; } @@ -91,32 +92,34 @@ _fcntl (int fd, int cmd, ...) struct __flock32 *src = NULL; struct flock dst; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - va_start (args, cmd); - arg = va_arg (args, intptr_t); - va_end (args); - if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) + __try { - src = (struct __flock32 *) arg; - dst.l_type = src->l_type; - dst.l_whence = src->l_whence; - dst.l_start = src->l_start; - dst.l_len = src->l_len; - dst.l_pid = src->l_pid; - arg = (intptr_t) &dst; + va_start (args, cmd); + arg = va_arg (args, intptr_t); + va_end (args); + if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) + { + src = (struct __flock32 *) arg; + dst.l_type = src->l_type; + dst.l_whence = src->l_whence; + dst.l_start = src->l_start; + dst.l_len = src->l_len; + dst.l_pid = src->l_pid; + arg = (intptr_t) &dst; + } + int res = fcntl64 (fd, cmd, arg); + if (cmd == F_GETLK) + { + src->l_type = dst.l_type; + src->l_whence = dst.l_whence; + src->l_start = dst.l_start; + src->l_len = dst.l_len; + src->l_pid = (short) dst.l_pid; + } + return res; } - int res = fcntl64 (fd, cmd, arg); - if (cmd == F_GETLK) - { - src->l_type = dst.l_type; - src->l_whence = dst.l_whence; - src->l_start = dst.l_start; - src->l_len = dst.l_len; - src->l_pid = (short) dst.l_pid; - } - return res; + __except (EFAULT) + __endtry + return -1; } #endif diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 19f629124..54c81dce6 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -77,7 +77,8 @@ enum conn_state unconnected = 0, connect_pending = 1, connected = 2, - connect_failed = 3 + listener = 3, + connect_failed = 4 }; enum line_edit_status @@ -502,6 +503,7 @@ class fhandler_socket: public fhandler_base int af_local_accept (); public: int af_local_connect (); + int af_local_set_no_getpeereid (); void af_local_set_sockpair_cred (); private: @@ -528,12 +530,12 @@ class fhandler_socket: public fhandler_base unsigned saw_shutdown_read : 1; /* Socket saw a SHUT_RD */ unsigned saw_shutdown_write : 1; /* Socket saw a SHUT_WR */ unsigned saw_reuseaddr : 1; /* Socket saw SO_REUSEADDR call */ - unsigned listener : 1; /* listen called */ - unsigned connect_state : 2; + unsigned connect_state : 3; + unsigned no_getpeereid : 1; public: status_flags () : async_io (0), saw_shutdown_read (0), saw_shutdown_write (0), - listener (0), connect_state (unconnected) + connect_state (unconnected), no_getpeereid (0) {} } status; @@ -554,8 +556,8 @@ class fhandler_socket: public fhandler_base IMPLEMENT_STATUS_FLAG (bool, saw_shutdown_read) IMPLEMENT_STATUS_FLAG (bool, saw_shutdown_write) IMPLEMENT_STATUS_FLAG (bool, saw_reuseaddr) - IMPLEMENT_STATUS_FLAG (bool, listener) IMPLEMENT_STATUS_FLAG (conn_state, connect_state) + IMPLEMENT_STATUS_FLAG (bool, no_getpeereid) int bind (const struct sockaddr *name, int namelen); int connect (const struct sockaddr *name, int namelen); @@ -582,7 +584,11 @@ class fhandler_socket: public fhandler_base int ioctl (unsigned int cmd, void *); int fcntl (int cmd, intptr_t); - off_t lseek (off_t, int) { return 0; } + off_t lseek (off_t, int) + { + set_errno (ESPIPE); + return -1; + } int shutdown (int how); int close (); void hclose (HANDLE) {close ();} @@ -1135,7 +1141,11 @@ class fhandler_serial: public fhandler_base int switch_modem_lines (int set, int clr); int tcsetattr (int a, const struct termios *t); int tcgetattr (struct termios *t); - off_t lseek (off_t, int) { return 0; } + off_t lseek (off_t, int) + { + set_errno (ESPIPE); + return -1; + } int tcflush (int); bool is_tty () const { return true; } void fixup_after_fork (HANDLE parent); @@ -1498,7 +1508,7 @@ class fhandler_pty_slave: public fhandler_pty_common HANDLE inuse; // used to indicate that a tty is in use /* Helper functions for fchmod and fchown. */ - bool fch_open_handles (); + bool fch_open_handles (bool chown); int fch_set_sd (security_descriptor &sd, bool chown); void fch_close_handles (); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index b5415c9ce..22709ffdf 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -648,9 +648,9 @@ fhandler_base::fstat_helper (struct stat *buf, /* We have to re-open the file. Either the file is not opened for reading, or the read will change the file position of the original handle. */ - pc.init_reopen_attr (&attr, h); status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA, - &attr, &io, FILE_SHARE_VALID_FLAGS, + pc.init_reopen_attr (attr, h), &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS (status)) @@ -690,7 +690,7 @@ fhandler_base::fstat_helper (struct stat *buf, } done: - syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=%y, st_ino=%D" + syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=0%o, st_ino=%D" "st_atim=%lx.%lx st_ctim=%lx.%lx " "st_mtim=%lx.%lx st_birthtim=%lx.%lx", pc.get_nt_native_path (), buf, @@ -776,6 +776,26 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs) } ret = 0; } + else if (status == STATUS_INVALID_PARAMETER /* Netapp */ + || status == STATUS_INVALID_INFO_CLASS) + { + FILE_FS_SIZE_INFORMATION fsi; + status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi, + FileFsSizeInformation); + if (NT_SUCCESS (status)) + { + sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit; + sfs->f_frsize = sfs->f_bsize; + sfs->f_blocks = (fsblkcnt_t) fsi.TotalAllocationUnits.QuadPart; + sfs->f_bfree = sfs->f_bavail = + (fsblkcnt_t) fsi.AvailableAllocationUnits.QuadPart; + ret = 0; + } + else + debug_printf ("%y = NtQueryVolumeInformationFile" + "(%S, FileFsSizeInformation)", + status, pc.get_nt_native_path ()); + } else debug_printf ("%y = NtQueryVolumeInformationFile" "(%S, FileFsFullSizeInformation)", @@ -870,9 +890,9 @@ fhandler_disk_file::fchmod (mode_t mode) OBJECT_ATTRIBUTES attr; HANDLE fh; - pc.init_reopen_attr (&attr, get_handle ()); - if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io, - FILE_SHARE_VALID_FLAGS, + if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, + pc.init_reopen_attr (attr, get_handle ()), + &io, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT))) { NtSetAttributesFile (fh, pc.file_attributes ()); @@ -1360,9 +1380,9 @@ fhandler_base::utimens_fs (const struct timespec *tvp) OBJECT_ATTRIBUTES attr; HANDLE fh; - pc.init_reopen_attr (&attr, get_handle ()); - if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io, - FILE_SHARE_VALID_FLAGS, + if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, + pc.init_reopen_attr (attr, get_handle ()), + &io, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT))) { NtSetInformationFile (fh, &io, &fbi, sizeof fbi, @@ -1531,8 +1551,8 @@ fhandler_disk_file::prw_open (bool write) /* First try to open with the original access mask */ ACCESS_MASK access = get_access (); - pc.init_reopen_attr (&attr, get_handle ()); - status = NtOpenFile (&prw_handle, access, &attr, &io, + status = NtOpenFile (&prw_handle, access, + pc.init_reopen_attr (attr, get_handle ()), &io, FILE_SHARE_VALID_FLAGS, get_options ()); if (status == STATUS_ACCESS_DENIED) { diff --git a/winsup/cygwin/fhandler_netdrive.cc b/winsup/cygwin/fhandler_netdrive.cc index b4189fa8a..39ec0cdff 100644 --- a/winsup/cygwin/fhandler_netdrive.cc +++ b/winsup/cygwin/fhandler_netdrive.cc @@ -1,6 +1,7 @@ /* fhandler_netdrive.cc: fhandler for // and //MACHINE handling - Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 + Red Hat, Inc. This file is part of Cygwin. @@ -17,6 +18,7 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "cygthread.h" +#include "tls_pbuf.h" #include @@ -47,19 +49,20 @@ static DWORD WINAPI thread_netdrive (void *arg) { netdriveinf *ndi = (netdriveinf *) arg; - char provider[256], *dummy = NULL; - LPNETRESOURCE nro; + WCHAR provider[256], *dummy = NULL; + LPNETRESOURCEW nro; DWORD cnt, size; struct net_hdls *nh; + tmp_pathbuf tp; ReleaseSemaphore (ndi->sem, 1, NULL); switch (ndi->what) { case GET_RESOURCE_OPENENUMTOP: - nro = (LPNETRESOURCE) alloca (size = 4096); + nro = (LPNETRESOURCEW) tp.c_get (); nh = (struct net_hdls *) ndi->out; - ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider, - (size = 256, &size)); + ndi->ret = WNetGetProviderNameW (WNNC_NET_LANMAN, provider, + (size = 256, &size)); if (ndi->ret != NO_ERROR) break; memset (nro, 0, sizeof *nro); @@ -69,33 +72,35 @@ thread_netdrive (void *arg) nro->dwUsage = RESOURCEUSAGE_RESERVED | RESOURCEUSAGE_CONTAINER; nro->lpRemoteName = provider; nro->lpProvider = provider; - ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_ALL, nro, &nh->net); + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->net); if (ndi->ret != NO_ERROR) break; - while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro, - (size = 4096, &size))) == NO_ERROR) + while ((ndi->ret = WNetEnumResourceW (nh->net, (cnt = 1, &cnt), nro, + (size = NT_MAX_PATH, &size))) + == NO_ERROR) { - ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_ALL, nro, &nh->dom); + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); if (ndi->ret == NO_ERROR) break; } break; case GET_RESOURCE_OPENENUM: - nro = (LPNETRESOURCE) alloca (size = 4096); + nro = (LPNETRESOURCEW) tp.c_get (); nh = (struct net_hdls *) ndi->out; - ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider, + ndi->ret = WNetGetProviderNameW (WNNC_NET_LANMAN, provider, (size = 256, &size)); if (ndi->ret != NO_ERROR) break; - ((LPNETRESOURCE) ndi->in)->lpProvider = provider; - ndi->ret = WNetGetResourceInformation ((LPNETRESOURCE) ndi->in, - nro, &size, &dummy); + ((LPNETRESOURCEW) ndi->in)->lpProvider = provider; + ndi->ret = WNetGetResourceInformationW ((LPNETRESOURCEW) ndi->in, nro, + (size = NT_MAX_PATH, &size), + &dummy); if (ndi->ret != NO_ERROR) break; - ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_ALL, nro, &nh->dom); + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); break; case GET_RESOURCE_ENUM: nh = (struct net_hdls *) ndi->in; @@ -104,19 +109,20 @@ thread_netdrive (void *arg) ndi->ret = ERROR_NO_MORE_ITEMS; break; } - while ((ndi->ret = WNetEnumResource (nh->dom, (cnt = 1, &cnt), - (LPNETRESOURCE) ndi->out, - &ndi->outsize)) != NO_ERROR + nro = (LPNETRESOURCEW) tp.c_get (); + while ((ndi->ret = WNetEnumResourceW (nh->dom, (cnt = 1, &cnt), + (LPNETRESOURCEW) ndi->out, + &ndi->outsize)) != NO_ERROR && nh->net) { WNetCloseEnum (nh->dom); nh->dom = NULL; - nro = (LPNETRESOURCE) alloca (size = 4096); - while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro, - (size = 4096, &size))) == NO_ERROR) + while ((ndi->ret = WNetEnumResourceW (nh->net, (cnt = 1, &cnt), nro, + (size = NT_MAX_PATH, &size))) + == NO_ERROR) { - ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_ALL, nro, &nh->dom); + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); if (ndi->ret == NO_ERROR) break; } @@ -152,15 +158,20 @@ fhandler_netdrive::exists () size_t len = strlen (get_name ()); if (len == 2) return virt_rootdir; + char namebuf[len + 1]; + tmp_pathbuf tp; + PWCHAR name = tp.w_get (); + for (to = namebuf, from = get_name (); *from; to++, from++) *to = (*from == '/') ? '\\' : *from; *to = '\0'; struct net_hdls nh = { NULL, NULL }; - NETRESOURCE nr = {0}; + NETRESOURCEW nr = {0}; nr.dwType = RESOURCETYPE_DISK; - nr.lpRemoteName = namebuf; + sys_mbstowcs (name, NT_MAX_PATH, namebuf); + nr.lpRemoteName = name; DWORD ret = create_thread_and_wait (GET_RESOURCE_OPENENUM, &nr, &nh, 0, "WNetOpenEnum"); if (nh.dom) @@ -190,28 +201,30 @@ fhandler_netdrive::fstat (struct stat *buf) int fhandler_netdrive::readdir (DIR *dir, dirent *de) { - NETRESOURCE *nro; + NETRESOURCEW *nro; DWORD ret; int res; + tmp_pathbuf tp; if (!dir->__d_position) { size_t len = strlen (get_name ()); - char *namebuf = NULL; - NETRESOURCE nr = { 0 }; + PWCHAR name = NULL; + NETRESOURCEW nr = { 0 }; struct net_hdls *nh; if (len != 2) /* // */ { const char *from; char *to; - namebuf = (char *) alloca (len + 1); + char *namebuf = (char *) alloca (len + 1); for (to = namebuf, from = get_name (); *from; to++, from++) *to = (*from == '/') ? '\\' : *from; *to = '\0'; + name = tp.w_get (); + sys_mbstowcs (name, NT_MAX_PATH, namebuf); } - - nr.lpRemoteName = namebuf; + nr.lpRemoteName = name; nr.dwType = RESOURCETYPE_DISK; nh = (struct net_hdls *) ccalloc (HEAP_FHANDLER, 1, sizeof *nh); ret = create_thread_and_wait (len == 2 ? GET_RESOURCE_OPENENUMTOP @@ -225,28 +238,37 @@ fhandler_netdrive::readdir (DIR *dir, dirent *de) } dir->__handle = (HANDLE) nh; } - ret = create_thread_and_wait (GET_RESOURCE_ENUM, dir->__handle, - nro = (LPNETRESOURCE) alloca (16384), - 16384, "WnetEnumResource"); + nro = (LPNETRESOURCEW) tp.c_get (); + ret = create_thread_and_wait (GET_RESOURCE_ENUM, dir->__handle, nro, + NT_MAX_PATH, "WnetEnumResource"); if (ret != NO_ERROR) res = geterrno_from_win_error (ret); else { dir->__d_position++; - char *bs = strrchr (nro->lpRemoteName, '\\'); - strcpy (de->d_name, bs ? bs + 1 : nro->lpRemoteName); + PWCHAR bs = wcsrchr (nro->lpRemoteName, L'\\'); + bs = bs ? bs + 1 : nro->lpRemoteName; if (strlen (get_name ()) == 2) { - strlwr (de->d_name); + UNICODE_STRING ss, ds; + + tp.u_get (&ds); + RtlInitUnicodeString (&ss, bs); + RtlDowncaseUnicodeString (&ds, &ss, FALSE); + sys_wcstombs (de->d_name, sizeof de->d_name, + ds.Buffer, ds.Length / sizeof (WCHAR)); de->d_ino = hash_path_name (get_ino (), de->d_name); } else { - de->d_ino = readdir_get_ino (nro->lpRemoteName, false); + sys_wcstombs (de->d_name, sizeof de->d_name, bs); + char *rpath = tp.c_get (); + sys_wcstombs (rpath, NT_MAX_PATH, nro->lpRemoteName); + de->d_ino = readdir_get_ino (rpath, false); /* We can't trust remote inode numbers of only 32 bit. That means, remote NT4 NTFS, as well as shares of Samba version < 3.0. */ if (de->d_ino <= UINT32_MAX) - de->d_ino = hash_path_name (0, nro->lpRemoteName); + de->d_ino = hash_path_name (0, rpath); } de->d_type = DT_DIR; diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc index 5db8ea2c5..34acf4ed1 100644 --- a/winsup/cygwin/fhandler_proc.cc +++ b/winsup/cygwin/fhandler_proc.cc @@ -17,6 +17,7 @@ details. */ #include "cygerrno.h" #include "security.h" #include "path.h" +#include "shared_info.h" #include "fhandler.h" #include "fhandler_virtual.h" #include "pinfo.h" @@ -44,6 +45,7 @@ static off_t format_proc_uptime (void *, char *&); static off_t format_proc_cpuinfo (void *, char *&); static off_t format_proc_partitions (void *, char *&); static off_t format_proc_self (void *, char *&); +static off_t format_proc_cygdrive (void *, char *&); static off_t format_proc_mounts (void *, char *&); static off_t format_proc_filesystems (void *, char *&); static off_t format_proc_swaps (void *, char *&); @@ -55,6 +57,7 @@ static const virt_tab_t proc_tab[] = { { _VN ("."), FH_PROC, virt_directory, NULL }, { _VN (".."), FH_PROC, virt_directory, NULL }, { _VN ("cpuinfo"), FH_PROC, virt_file, format_proc_cpuinfo }, + { _VN ("cygdrive"), FH_PROC, virt_symlink, format_proc_cygdrive }, { _VN ("devices"), FH_PROC, virt_file, format_proc_devices }, { _VN ("filesystems"), FH_PROC, virt_file, format_proc_filesystems }, { _VN ("loadavg"), FH_PROC, virt_file, format_proc_loadavg }, @@ -1178,6 +1181,10 @@ format_proc_partitions (void *, char *&destbuf) char *buf = tp.c_get (); char *bufptr = buf; char *ioctl_buf = tp.c_get (); + PWCHAR mp_buf = tp.w_get (); + WCHAR fpath[MAX_PATH]; + WCHAR gpath[MAX_PATH]; + DWORD len; /* Open \Device object directory. */ wchar_t wpath[MAX_PATH] = L"\\Device"; @@ -1216,13 +1223,12 @@ format_proc_partitions (void *, char *&destbuf) continue; /* Got it. Now construct the path to the entire disk, which is "\\Device\\HarddiskX\\Partition0", and open the disk with - minimum permssions. */ + minimum permissions. */ unsigned long drive_num = wcstoul (dbi->ObjectName.Buffer + 8, NULL, 10); wcscpy (wpath, dbi->ObjectName.Buffer); PWCHAR wpart = wpath + dbi->ObjectName.Length / sizeof (WCHAR); - __small_swprintf (wpart, L"\\Partition0"); - upath.Length = dbi->ObjectName.Length - + wcslen (wpart) * sizeof (WCHAR); + wcpcpy (wpart, L"\\Partition0"); + upath.Length = dbi->ObjectName.Length + 22; upath.MaximumLength = upath.Length + sizeof (WCHAR); InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, dirhdl, NULL); @@ -1236,7 +1242,7 @@ format_proc_partitions (void *, char *&destbuf) } if (!got_one) { - print ("major minor #blocks name\n\n"); + print ("major minor #blocks name win-mounts\n\n"); got_one = true; } /* Fetch partition info for the entire disk to get its size. */ @@ -1305,9 +1311,27 @@ format_proc_partitions (void *, char *&destbuf) if (part_num == 0) continue; dev.parsedisk (drive_num, part_num); - bufptr += __small_sprintf (bufptr, "%5d %5d %9U %s\n", + + bufptr += __small_sprintf (bufptr, "%5d %5d %9U %s", dev.get_major (), dev.get_minor (), size >> 10, dev.name + 5); + /* Check if the partition is mounted in Windows and, if so, + print the mount point list. */ + __small_swprintf (fpath, + L"\\\\?\\GLOBALROOT\\Device\\%S\\Partition%u\\", + &dbi->ObjectName, part_num); + if (GetVolumeNameForVolumeMountPointW (fpath, gpath, MAX_PATH) + && GetVolumePathNamesForVolumeNameW (gpath, mp_buf, + NT_MAX_PATH, &len)) + { + len = strlen (dev.name + 5); + while (len++ < 6) + *bufptr++ = ' '; + for (PWCHAR p = mp_buf; *p; p = wcschr (p, L'\0') + 1) + bufptr += __small_sprintf (bufptr, " %W", p); + } + + *bufptr++ = '\n'; } NtClose (devhdl); } @@ -1328,6 +1352,16 @@ format_proc_self (void *, char *&destbuf) return __small_sprintf (destbuf, "%d", getpid ()); } +static off_t +format_proc_cygdrive (void *, char *&destbuf) +{ + destbuf = (char *) crealloc_abort (destbuf, mount_table->cygdrive_len + 1); + char *dend = stpcpy (destbuf, mount_table->cygdrive); + if (dend > destbuf + 1) /* cygdrive != "/"? */ + *--dend = '\0'; + return dend - destbuf; +} + static off_t format_proc_mounts (void *, char *&destbuf) { diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc index 1e5a02806..01de47436 100644 --- a/winsup/cygwin/fhandler_process.cc +++ b/winsup/cygwin/fhandler_process.cc @@ -1161,8 +1161,8 @@ format_process_statm (void *data, char *&destbuf) &vmlib, &vmshare)) return 0; destbuf = (char *) crealloc_abort (destbuf, 96); - return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld", - vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0); + return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld 0\n", + vmsize, vmrss, vmshare, vmtext, vmlib, vmdata); } extern "C" { diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 793578f39..8f38712a8 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -400,7 +400,10 @@ fhandler_socket::af_local_connect () if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) return 0; - debug_printf ("af_local_connect called"); + debug_printf ("af_local_connect called, no_getpeereid=%d", no_getpeereid ()); + if (no_getpeereid ()) + return 0; + bool orig_async_io, orig_is_nonblocking; af_local_setblocking (orig_async_io, orig_is_nonblocking); if (!af_local_send_secret () || !af_local_recv_secret () @@ -418,7 +421,10 @@ fhandler_socket::af_local_connect () int fhandler_socket::af_local_accept () { - debug_printf ("af_local_accept called"); + debug_printf ("af_local_accept called, no_getpeereid=%d", no_getpeereid ()); + if (no_getpeereid ()) + return 0; + bool orig_async_io, orig_is_nonblocking; af_local_setblocking (orig_async_io, orig_is_nonblocking); if (!af_local_recv_secret () || !af_local_send_secret () @@ -434,6 +440,25 @@ fhandler_socket::af_local_accept () return 0; } +int +fhandler_socket::af_local_set_no_getpeereid () +{ + if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) + { + set_errno (EINVAL); + return -1; + } + if (connect_state () != unconnected) + { + set_errno (EALREADY); + return -1; + } + + debug_printf ("no_getpeereid set"); + no_getpeereid (true); + return 0; +} + void fhandler_socket::af_local_set_cred () { @@ -458,6 +483,7 @@ fhandler_socket::af_local_copy (fhandler_socket *sock) sock->sec_peer_pid = sec_peer_pid; sock->sec_peer_uid = sec_peer_uid; sock->sec_peer_gid = sec_peer_gid; + sock->no_getpeereid (no_getpeereid ()); } void @@ -1180,8 +1206,7 @@ fhandler_socket::listen (int backlog) { if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) af_local_set_cred (); - connect_state (connected); - listener (true); + connect_state (listener); /* gets set to connected on accepted socket. */ } else set_winsock_errno (); @@ -1195,7 +1220,17 @@ fhandler_socket::accept4 (struct sockaddr *peer, int *len, int flags) struct sockaddr_storage lpeer; int llen = sizeof (struct sockaddr_storage); - int res = 0; + int res = (int) INVALID_SOCKET; + + /* Windows event handling does not check for the validity of the desired + flags so we have to do it here. */ + if (connect_state () != listener) + { + WSASetLastError (WSAEINVAL); + set_winsock_errno (); + goto out; + } + while (!(res = wait_for_events (FD_ACCEPT | FD_CLOSE, 0)) && (res = ::accept (get_socket (), (struct sockaddr *) &lpeer, &llen)) == SOCKET_ERROR @@ -2259,26 +2294,28 @@ fhandler_socket::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid) set_errno (EINVAL); return -1; } + if (no_getpeereid ()) + { + set_errno (ENOTSUP); + return -1; + } if (connect_state () != connected) { set_errno (ENOTCONN); return -1; } - if (sec_peer_pid == (pid_t) 0) - { - set_errno (ENOTCONN); /* Usually when calling getpeereid on - accepting (instead of accepted) socket. */ - return -1; - } - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (pid) - *pid = sec_peer_pid; - if (euid) - *euid = sec_peer_uid; - if (egid) - *egid = sec_peer_gid; - return 0; + __try + { + if (pid) + *pid = sec_peer_pid; + if (euid) + *euid = sec_peer_uid; + if (egid) + *egid = sec_peer_gid; + return 0; + } + __except (EFAULT) {} + __endtry + return -1; } diff --git a/winsup/cygwin/fhandler_tape.cc b/winsup/cygwin/fhandler_tape.cc index 91d5b12aa..5b09e4262 100644 --- a/winsup/cygwin/fhandler_tape.cc +++ b/winsup/cygwin/fhandler_tape.cc @@ -2,7 +2,7 @@ classes. Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010, 2011, 2012, 2013 Red Hat, Inc. + 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -977,159 +977,163 @@ mtinfo_drive::set_options (HANDLE mt, int32_t options) int mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf) { - myfault efault; - if (efault.faulted ()) - return ERROR_NOACCESS; - if (cmd == MTIOCTOP) + __try { - struct mtop *op = (struct mtop *) buf; - if (lasterr == ERROR_BUS_RESET) + if (cmd == MTIOCTOP) { - /* If a bus reset occurs, block further access to this device - until the user rewinds, unloads or in any other way tries - to maintain a well-known tape position. */ - if (op->mt_op != MTREW && op->mt_op != MTOFFL - && op->mt_op != MTRETEN && op->mt_op != MTERASE - && op->mt_op != MTSEEK && op->mt_op != MTEOM) - return ERROR_BUS_RESET; - /* Try to maintain last lock state after bus reset. */ - if (lock >= auto_locked && PrepareTape (mt, TAPE_LOCK, FALSE)) + struct mtop *op = (struct mtop *) buf; + if (lasterr == ERROR_BUS_RESET) { - debug_printf ("Couldn't relock drive after bus reset."); - lock = unlocked; + /* If a bus reset occurs, block further access to this device + until the user rewinds, unloads or in any other way tries + to maintain a well-known tape position. */ + if (op->mt_op != MTREW && op->mt_op != MTOFFL + && op->mt_op != MTRETEN && op->mt_op != MTERASE + && op->mt_op != MTSEEK && op->mt_op != MTEOM) + return ERROR_BUS_RESET; + /* Try to maintain last lock state after bus reset. */ + if (lock >= auto_locked && PrepareTape (mt, TAPE_LOCK, FALSE)) + { + debug_printf ("Couldn't relock drive after bus reset."); + lock = unlocked; + } } - } - switch (op->mt_op) - { - case MTRESET: - break; - case MTFSF: - set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false); - break; - case MTBSF: - set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false); - break; - case MTFSR: - set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false); - break; - case MTBSR: - set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false); - break; - case MTWEOF: - write_marks (mt, TAPE_FILEMARKS, op->mt_count); - break; - case MTREW: - set_pos (mt, TAPE_REWIND, 0, false); - break; - case MTOFFL: - case MTUNLOAD: - prepare (mt, TAPE_UNLOAD); - break; - case MTNOP: - lasterr = 0; - break; - case MTRETEN: - if (!get_feature (TAPE_DRIVE_TENSION)) - lasterr = ERROR_INVALID_PARAMETER; - else if (!set_pos (mt, TAPE_REWIND, 0, false)) - prepare (mt, TAPE_TENSION); - break; - case MTBSFM: - set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true); - break; - case MTFSFM: - set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, true); - break; - case MTEOM: - if (fast_eom () && get_feature (TAPE_DRIVE_END_OF_DATA)) - set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false); - else - set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false); - break; - case MTERASE: - erase (mt, TAPE_ERASE_LONG); - break; - case MTRAS1: - case MTRAS2: - case MTRAS3: - lasterr = ERROR_INVALID_PARAMETER; - break; - case MTSETBLK: - if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE)) - { - lasterr = ERROR_INVALID_PARAMETER; + switch (op->mt_op) + { + case MTRESET: break; - } - if ((DWORD) op->mt_count == mp ()->BlockSize) - { - /* Nothing has changed. */ + case MTFSF: + set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false); + break; + case MTBSF: + set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false); + break; + case MTFSR: + set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false); + break; + case MTBSR: + set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false); + break; + case MTWEOF: + write_marks (mt, TAPE_FILEMARKS, op->mt_count); + break; + case MTREW: + set_pos (mt, TAPE_REWIND, 0, false); + break; + case MTOFFL: + case MTUNLOAD: + prepare (mt, TAPE_UNLOAD); + break; + case MTNOP: lasterr = 0; break; - } - if ((op->mt_count == 0 && !get_feature (TAPE_DRIVE_VARIABLE_BLOCK)) - || (op->mt_count > 0 - && ((DWORD) op->mt_count < dp ()->MinimumBlockSize - || (DWORD) op->mt_count > dp ()->MaximumBlockSize))) - { + case MTRETEN: + if (!get_feature (TAPE_DRIVE_TENSION)) + lasterr = ERROR_INVALID_PARAMETER; + else if (!set_pos (mt, TAPE_REWIND, 0, false)) + prepare (mt, TAPE_TENSION); + break; + case MTBSFM: + set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true); + break; + case MTFSFM: + set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, true); + break; + case MTEOM: + if (fast_eom () && get_feature (TAPE_DRIVE_END_OF_DATA)) + set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false); + else + set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false); + break; + case MTERASE: + erase (mt, TAPE_ERASE_LONG); + break; + case MTRAS1: + case MTRAS2: + case MTRAS3: lasterr = ERROR_INVALID_PARAMETER; break; - } - if (set_blocksize (mt, op->mt_count) - && lasterr == ERROR_INVALID_FUNCTION) - lasterr = ERROR_INVALID_BLOCK_LENGTH; - break; - case MTSEEK: - if (get_feature (TAPE_DRIVE_LOGICAL_BLK)) - set_pos (mt, TAPE_LOGICAL_BLOCK, op->mt_count, false); - else if (!get_pos (mt)) - set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, - op->mt_count - block, false); - break; - case MTTELL: - if (!get_pos (mt)) - op->mt_count = (int) block; - break; - case MTFSS: - set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false); - break; - case MTBSS: - set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false); - break; - case MTWSM: - write_marks (mt, TAPE_SETMARKS, op->mt_count); - break; - case MTLOCK: - prepare (mt, TAPE_LOCK); - break; - case MTUNLOCK: - prepare (mt, TAPE_UNLOCK); - break; - case MTLOAD: - prepare (mt, TAPE_LOAD); - break; - case MTCOMPRESSION: - set_compression (mt, op->mt_count); - break; - case MTSETPART: - set_partition (mt, op->mt_count); - break; - case MTMKPART: - create_partitions (mt, op->mt_count); - break; - case MTSETDRVBUFFER: - set_options (mt, op->mt_count); - break; - case MTSETDENSITY: - default: - lasterr = ERROR_INVALID_PARAMETER; - break; + case MTSETBLK: + if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE)) + { + lasterr = ERROR_INVALID_PARAMETER; + break; + } + if ((DWORD) op->mt_count == mp ()->BlockSize) + { + /* Nothing has changed. */ + lasterr = 0; + break; + } + if ((op->mt_count == 0 && !get_feature (TAPE_DRIVE_VARIABLE_BLOCK)) + || (op->mt_count > 0 + && ((DWORD) op->mt_count < dp ()->MinimumBlockSize + || (DWORD) op->mt_count > dp ()->MaximumBlockSize))) + { + lasterr = ERROR_INVALID_PARAMETER; + break; + } + if (set_blocksize (mt, op->mt_count) + && lasterr == ERROR_INVALID_FUNCTION) + lasterr = ERROR_INVALID_BLOCK_LENGTH; + break; + case MTSEEK: + if (get_feature (TAPE_DRIVE_LOGICAL_BLK)) + set_pos (mt, TAPE_LOGICAL_BLOCK, op->mt_count, false); + else if (!get_pos (mt)) + set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, + op->mt_count - block, false); + break; + case MTTELL: + if (!get_pos (mt)) + op->mt_count = (int) block; + break; + case MTFSS: + set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false); + break; + case MTBSS: + set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false); + break; + case MTWSM: + write_marks (mt, TAPE_SETMARKS, op->mt_count); + break; + case MTLOCK: + prepare (mt, TAPE_LOCK); + break; + case MTUNLOCK: + prepare (mt, TAPE_UNLOCK); + break; + case MTLOAD: + prepare (mt, TAPE_LOAD); + break; + case MTCOMPRESSION: + set_compression (mt, op->mt_count); + break; + case MTSETPART: + set_partition (mt, op->mt_count); + break; + case MTMKPART: + create_partitions (mt, op->mt_count); + break; + case MTSETDRVBUFFER: + set_options (mt, op->mt_count); + break; + case MTSETDENSITY: + default: + lasterr = ERROR_INVALID_PARAMETER; + break; + } } + else if (cmd == MTIOCGET) + get_status (mt, (struct mtget *) buf); + else if (cmd == MTIOCPOS && !get_pos (mt)) + ((struct mtpos *) buf)->mt_blkno = (long) block; } - else if (cmd == MTIOCGET) - get_status (mt, (struct mtget *) buf); - else if (cmd == MTIOCPOS && !get_pos (mt)) - ((struct mtpos *) buf)->mt_blkno = (long) block; - + __except (NO_ERROR) + { + lasterr = ERROR_NOACCESS; + } + __endtry return lasterr; } diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 9d928bd64..eea635f89 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -1102,17 +1102,18 @@ fhandler_pty_slave::fstat (struct stat *st) /* Helper function for fchmod and fchown, which just opens all handles and signals success via bool return. */ bool -fhandler_pty_slave::fch_open_handles () +fhandler_pty_slave::fch_open_handles (bool chown) { char buf[MAX_PATH]; + DWORD write_access = WRITE_DAC | (chown ? WRITE_OWNER : 0); _tc = cygwin_shared->tty[get_minor ()]; shared_name (buf, INPUT_AVAILABLE_EVENT, get_minor ()); - input_available_event = OpenEvent (READ_CONTROL | WRITE_DAC | WRITE_OWNER, + input_available_event = OpenEvent (READ_CONTROL | write_access, TRUE, buf); - output_mutex = get_ttyp ()->open_output_mutex (WRITE_DAC | WRITE_OWNER); - input_mutex = get_ttyp ()->open_input_mutex (WRITE_DAC | WRITE_OWNER); - inuse = get_ttyp ()->open_inuse (WRITE_DAC | WRITE_OWNER); + output_mutex = get_ttyp ()->open_output_mutex (write_access); + input_mutex = get_ttyp ()->open_input_mutex (write_access); + inuse = get_ttyp ()->open_inuse (write_access); if (!input_available_event || !output_mutex || !input_mutex || !inuse) { __seterrno (); @@ -1166,7 +1167,7 @@ fhandler_pty_slave::fchmod (mode_t mode) if (!input_available_event) { to_close = true; - if (!fch_open_handles ()) + if (!fch_open_handles (false)) goto errout; } sd.malloc (sizeof (SECURITY_DESCRIPTOR)); @@ -1195,7 +1196,7 @@ fhandler_pty_slave::fchown (uid_t uid, gid_t gid) if (!input_available_event) { to_close = true; - if (!fch_open_handles ()) + if (!fch_open_handles (true)) goto errout; } sd.malloc (sizeof (SECURITY_DESCRIPTOR)); diff --git a/winsup/cygwin/flock.cc b/winsup/cygwin/flock.cc index 4250aba3f..540d914e3 100644 --- a/winsup/cygwin/flock.cc +++ b/winsup/cygwin/flock.cc @@ -1761,37 +1761,37 @@ flock (int fd, int operation) int cmd; struct flock fl = { 0, SEEK_SET, 0, 0, 0 }; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - cygheap_fdget cfd (fd, true); - if (cfd < 0) - goto done; - - cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW; - switch (operation & (~LOCK_NB)) + __try { - case LOCK_EX: - fl.l_type = F_WRLCK; - break; - case LOCK_SH: - fl.l_type = F_RDLCK; - break; - case LOCK_UN: - fl.l_type = F_UNLCK; - break; - default: - set_errno (EINVAL); - goto done; + cygheap_fdget cfd (fd, true); + if (cfd < 0) + __leave; + + cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW; + switch (operation & (~LOCK_NB)) + { + case LOCK_EX: + fl.l_type = F_WRLCK; + break; + case LOCK_SH: + fl.l_type = F_RDLCK; + break; + case LOCK_UN: + fl.l_type = F_UNLCK; + break; + default: + set_errno (EINVAL); + __leave; + } + if (!cfd->mandatory_locking ()) + fl.l_type |= F_FLOCK; + res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl) + : cfd->lock (cmd, &fl); + if ((res == -1) && ((get_errno () == EAGAIN) || (get_errno () == EACCES))) + set_errno (EWOULDBLOCK); } - if (!cfd->mandatory_locking ()) - fl.l_type |= F_FLOCK; - res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl) - : cfd->lock (cmd, &fl); - if ((res == -1) && ((get_errno () == EAGAIN) || (get_errno () == EACCES))) - set_errno (EWOULDBLOCK); -done: + __except (EFAULT) {} + __endtry syscall_printf ("%R = flock(%d, %d)", res, fd, operation); return res; } @@ -1805,50 +1805,50 @@ lockf (int filedes, int function, off_t size) pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - cygheap_fdget cfd (filedes, true); - if (cfd < 0) - goto done; - - fl.l_start = 0; - fl.l_len = size; - fl.l_whence = SEEK_CUR; - - switch (function) + __try { - case F_ULOCK: - cmd = F_SETLK; - fl.l_type = F_UNLCK; - break; - case F_LOCK: - cmd = F_SETLKW; - fl.l_type = F_WRLCK; - break; - case F_TLOCK: - cmd = F_SETLK; - fl.l_type = F_WRLCK; - break; - case F_TEST: - fl.l_type = F_WRLCK; - if (cfd->lock (F_GETLK, &fl) == -1) - goto done; - if (fl.l_type == F_UNLCK || fl.l_pid == getpid ()) - res = 0; - else - errno = EAGAIN; - goto done; - /* NOTREACHED */ - default: - errno = EINVAL; - goto done; - /* NOTREACHED */ + cygheap_fdget cfd (filedes, true); + if (cfd < 0) + __leave; + + fl.l_start = 0; + fl.l_len = size; + fl.l_whence = SEEK_CUR; + + switch (function) + { + case F_ULOCK: + cmd = F_SETLK; + fl.l_type = F_UNLCK; + break; + case F_LOCK: + cmd = F_SETLKW; + fl.l_type = F_WRLCK; + break; + case F_TLOCK: + cmd = F_SETLK; + fl.l_type = F_WRLCK; + break; + case F_TEST: + fl.l_type = F_WRLCK; + if (cfd->lock (F_GETLK, &fl) == -1) + __leave; + if (fl.l_type == F_UNLCK || fl.l_pid == getpid ()) + res = 0; + else + errno = EAGAIN; + __leave; + /* NOTREACHED */ + default: + errno = EINVAL; + __leave; + /* NOTREACHED */ + } + res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl) + : cfd->lock (cmd, &fl); } - res = cfd->mandatory_locking () ? cfd->mand_lock (cmd, &fl) - : cfd->lock (cmd, &fl); -done: + __except (EFAULT) {} + __endtry syscall_printf ("%R = lockf(%d, %d, %D)", res, filedes, function, size); return res; } diff --git a/winsup/cygwin/gendef b/winsup/cygwin/gendef index cef34a5ab..a1c549fc5 100755 --- a/winsup/cygwin/gendef +++ b/winsup/cygwin/gendef @@ -1,5 +1,5 @@ #!/usr/bin/perl -# Copyright 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2011, 2012, 2013 +# Copyright 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2011, 2012, 2013, 2014 # Red Hat, Inc. # # This file is part of Cygwin. @@ -188,10 +188,12 @@ _sigbe: # return here after cygwin syscall .seh_proc sigdelayed sigdelayed: pushq %r10 # used for return address injection - .seh_pushreg %rbp + .seh_pushreg %r10 pushq %rbp .seh_pushreg %rbp movq %rsp,%rbp + pushf + .seh_pushreg %rax # fake, there's no .seh_pushreg for the flags # stack is aligned or unaligned on entry! # make sure it is aligned from here on # We could be called from an interrupted thread which doesn't know @@ -224,9 +226,8 @@ sigdelayed: .seh_pushreg %rbx pushq %rax .seh_pushreg %rax - pushf - subq \$0x130,%rsp - .seh_stackalloc 0x130 + subq \$0x128,%rsp + .seh_stackalloc 0x128 fnstcw 0x120(%rsp) movdqa %xmm15,0x110(%rsp) movdqa %xmm14,0x100(%rsp) @@ -288,8 +289,7 @@ sigdelayed: movdqa 0x110(%rsp),%xmm15 fninit fldcw 0x120(%rsp) - addq \$0x130,%rsp - popf + addq \$0x128,%rsp popq %rax popq %rbx popq %rcx @@ -304,6 +304,8 @@ sigdelayed: popq %r14 popq %r15 movq %rbp,%rsp + subq \$8, %rsp + popf popq %rbp xchgq %r10,(%rsp) ret @@ -366,7 +368,7 @@ stabilize_sig_stack: movq %gs:8,%r12 1: movl \$1,%r10d xchgl %r10d,$tls::stacklock(%r12) - movl %r10d,$tls::spinning(%r12) # flag if we are waiting for lock + movl %r10d,$tls::spinning(%r12) # flag if we are waiting for lock testl %r10d,%r10d jz 2f pause @@ -374,14 +376,14 @@ stabilize_sig_stack: 2: incl $tls::incyg(%r12) cmpl \$0,$tls::sig(%r12) jz 3f - decl $tls::stacklock(%r12) # unlock - movq \$$tls::start_offset,%rcx # point to beginning - addq %r12,%rcx # of tls block + decl $tls::stacklock(%r12) # unlock + movq \$$tls::start_offset,%rcx # point to beginning + addq %r12,%rcx # of tls block call _ZN7_cygtls19call_signal_handlerEv jmp 1b 3: decl $tls::incyg(%r12) addq \$0x20,%rsp - movq %r12,%r11 # return tls addr in r11 + movq %r12,%r11 # return tls addr in r11 popq %r12 ret .seh_endproc @@ -393,13 +395,13 @@ EOF __sigfe_maybe: pushl %ebx pushl %edx - movl %fs:4,%ebx # location of bottom of stack - addl \$$tls::initialized,%ebx # where we will be looking - cmpl %ebx,%esp # stack loc > than tls - jge 0f # yep. we don't have a tls. - subl \$$tls::initialized,%ebx # where we will be looking + movl %fs:4,%ebx # location of bottom of stack + addl \$$tls::initialized,%ebx # where we will be looking + cmpl %ebx,%esp # stack loc > than tls + jge 0f # yep. we don't have a tls. + subl \$$tls::initialized,%ebx # where we will be looking movl $tls::initialized(%ebx),%eax - cmpl \$0xc763173f,%eax # initialized? + cmpl \$0xc763173f,%eax # initialized? je 1f 0: popl %edx popl %ebx @@ -408,43 +410,43 @@ __sigfe_maybe: __sigfe: pushl %ebx pushl %edx - movl %fs:4,%ebx # location of bottom of stack -1: movl \$1,%eax # potential lock value - xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it - movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock - testl %eax,%eax # it will be zero - jz 2f # if so - call _yield # should be a short-time thing, so - jmp 1b # sleep and loop -2: movl \$4,%eax # have the lock, now increment the - xadd %eax,$tls::stackptr(%ebx) # stack pointer and get pointer - leal __sigbe,%edx # new place to return to - xchgl %edx,12(%esp) # exchange with real return value - movl %edx,(%eax) # store real return value on alt stack + movl %fs:4,%ebx # location of bottom of stack +1: movl \$1,%eax # potential lock value + xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it + movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock + testl %eax,%eax # it will be zero + jz 2f # if so + call _yield # should be a short-time thing, so + jmp 1b # sleep and loop +2: movl \$4,%eax # have the lock, now increment the + xadd %eax,$tls::stackptr(%ebx) # stack pointer and get pointer + leal __sigbe,%edx # new place to return to + xchgl %edx,12(%esp) # exchange with real return value + movl %edx,(%eax) # store real return value on alt stack incl $tls::incyg(%ebx) - decl $tls::stacklock(%ebx) # remove lock - popl %edx # restore saved value + decl $tls::stacklock(%ebx) # remove lock + popl %edx # restore saved value popl %ebx ret .global __sigbe -__sigbe: # return here after cygwin syscall - pushl %eax # don't clobber - pushl %ebx # tls pointer -1: movl %fs:4,%ebx # address of bottom of tls - movl \$1,%eax # potential lock value - xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it - movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock - testl %eax,%eax # it will be zero - jz 2f # if so - call _yield # sleep - jmp 1b # and loop -2: movl \$-4,%eax # now decrement aux stack - xadd %eax,$tls::stackptr(%ebx) # and get pointer - movl -4(%eax),%eax # get return address from signal stack - xchgl %eax,4(%esp) # swap return address with saved eax +__sigbe: # return here after cygwin syscall + pushl %eax # don't clobber + pushl %ebx # tls pointer +1: movl %fs:4,%ebx # address of bottom of tls + movl \$1,%eax # potential lock value + xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it + movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock + testl %eax,%eax # it will be zero + jz 2f # if so + call _yield # sleep + jmp 1b # and loop +2: movl \$-4,%eax # now decrement aux stack + xadd %eax,$tls::stackptr(%ebx) # and get pointer + movl -4(%eax),%eax # get return address from signal stack + xchgl %eax,4(%esp) # swap return address with saved eax decl $tls::incyg(%ebx) - decl $tls::stacklock(%ebx) # release lock + decl $tls::stacklock(%ebx) # release lock popl %ebx ret diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc index a004276d7..a37175469 100644 --- a/winsup/cygwin/globals.cc +++ b/winsup/cygwin/globals.cc @@ -1,7 +1,7 @@ /* globals.cc - Define global variables here. Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -69,7 +69,7 @@ int NO_COPY dynamically_loaded; /* Some CYGWIN environment variable variables. */ bool allow_glob = true; bool detect_bloda; -bool dos_file_warning = true; +bool dos_file_warning; bool ignore_case_with_glob; bool pipe_byte; bool reset_com; diff --git a/winsup/cygwin/include/cygwin/time.h b/winsup/cygwin/include/cygwin/time.h index 3dbc372a3..6a82ae657 100644 --- a/winsup/cygwin/include/cygwin/time.h +++ b/winsup/cygwin/include/cygwin/time.h @@ -26,6 +26,8 @@ time_t __cdecl timegm (struct tm *); #ifndef __STRICT_ANSI__ +extern int stime (const time_t *); + extern int daylight __asm__ (_SYMSTR (_daylight)); #ifndef __timezonefunc__ diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 9b9e24ac7..6c46a0b43 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -43,7 +43,7 @@ details. */ changes to the DLL and is mainly informative in nature. */ #define CYGWIN_VERSION_DLL_MAJOR 1007 -#define CYGWIN_VERSION_DLL_MINOR 32 +#define CYGWIN_VERSION_DLL_MINOR 33 /* Major numbers before CYGWIN_VERSION_DLL_EPOCH are incompatible. */ @@ -448,12 +448,19 @@ details. */ 272: Export tm_gmtoff and tm_zone members. 273: Skipped. 274: Export __cxa_atexit and __cxa_finalize. + 275: Add CW_SETENT, CW_GETENT, CW_ENDENT, CW_GETNSSSEP, CW_GETPWSID, + CW_GETGRSID, CW_CYGNAME_FROM_WINNAME as no-ops for forward compat. + 276: Export ffsl, ffsll. + 277: Add setsockopt(SO_PEERCRED). + 278: Add quotactl. + 279: Export stime. + 280: Static atexit in libcygwin.a, CW_FIXED_ATEXIT. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 274 +#define CYGWIN_VERSION_API_MINOR 280 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index 321a58ec8..6dbf2d322 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -150,7 +150,8 @@ typedef enum CW_GETNSSSEP, CW_GETPWSID, CW_GETGRSID, - CW_CYGNAME_FROM_WINNAME + CW_CYGNAME_FROM_WINNAME, + CW_FIXED_ATEXIT } cygwin_getinfo_types; #define CW_LOCK_PINFO CW_LOCK_PINFO @@ -208,6 +209,7 @@ typedef enum #define CW_GETPWSID CW_GETPWSID #define CW_GETGRSID CW_GETGRSID #define CW_CYGNAME_FROM_WINNAME CW_CYGNAME_FROM_WINNAME +#define CW_FIXED_ATEXIT CW_FIXED_ATEXIT /* Token type for CW_SET_EXTERNAL_TOKEN */ enum diff --git a/winsup/cygwin/include/sys/mount.h b/winsup/cygwin/include/sys/mount.h index 0f5c17240..458cf801f 100644 --- a/winsup/cygwin/include/sys/mount.h +++ b/winsup/cygwin/include/sys/mount.h @@ -1,7 +1,7 @@ /* sys/mount.h - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, 2010, 2012 - Red Hat, Inc. + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, 2010, 2012, + 2014 Red Hat, Inc. This file is part of Cygwin. @@ -12,6 +12,9 @@ details. */ #ifndef _SYS_MOUNT_H #define _SYS_MOUNT_H +#define BLOCK_SIZE 1024 +#define BLOCK_SIZE_BITS 10 + #ifdef __cplusplus extern "C" { #endif diff --git a/winsup/cygwin/include/sys/quota.h b/winsup/cygwin/include/sys/quota.h new file mode 100644 index 000000000..7edb53e8a --- /dev/null +++ b/winsup/cygwin/include/sys/quota.h @@ -0,0 +1,239 @@ +/* Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_QUOTA_H +#define _SYS_QUOTA_H 1 + +#include +#include + +/* This file is copied from Linux and kept verbatim, except for the below + Cygwin-specific blocks. */ + +#ifdef __CYGWIN__ +/* On Linux these defines live in . Move them here for easier + access. */ +/* Quota format type IDs */ +#define QFMT_VFS_OLD 1 +#define QFMT_VFS_V0 2 +#define QFMT_OCFS2 3 +#define QFMT_VFS_V1 4 +#endif + +/* + * Select between different incompatible quota versions. + * Default to the version used by Linux kernel version 2.4.22 + * or later. */ +#ifndef _LINUX_QUOTA_VERSION +# define _LINUX_QUOTA_VERSION 2 +#endif + +#if defined (__CYGWIN__) && _LINUX_QUOTA_VERSION != 2 +#error Cygwin only supports quota version 2. +#endif + +/* + * Convert diskblocks to blocks and the other way around. + * currently only to fool the BSD source. :-) + */ +#define dbtob(num) ((num) << 10) +#define btodb(num) ((num) >> 10) + +/* + * Convert count of filesystem blocks to diskquota blocks, meant + * for filesystems where i_blksize != BLOCK_SIZE + */ +#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE) + +/* + * Definitions for disk quotas imposed on the average user + * (big brother finally hits Linux). + * + * The following constants define the amount of time given a user + * before the soft limits are treated as hard limits (usually resulting + * in an allocation failure). The timer is started when the user crosses + * their soft limit, it is reset when they go below their soft limit. + */ +#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ +#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ + +#define MAXQUOTAS 2 +#define USRQUOTA 0 /* element used for user quotas */ +#define GRPQUOTA 1 /* element used for group quotas */ + +/* + * Definitions for the default names of the quotas files. + */ +#define INITQFNAMES { \ + "user", /* USRQUOTA */ \ + "group", /* GRPQUOTA */ \ + "undefined", \ +}; + +#define QUOTAFILENAME "quota" +#define QUOTAGROUP "staff" + +#define NR_DQHASH 43 /* Just an arbitrary number any suggestions ? */ +#define NR_DQUOTS 256 /* Number of quotas active at one time */ + +/* + * Command definitions for the 'quotactl' system call. + * The commands are broken into a main command defined below + * and a subcommand that is used to convey the type of + * quota that is being manipulated (see above). + */ +#define SUBCMDMASK 0x00ff +#define SUBCMDSHIFT 8 +#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) + +#if _LINUX_QUOTA_VERSION < 2 +# define Q_QUOTAON 0x0100 /* enable quotas */ +# define Q_QUOTAOFF 0x0200 /* disable quotas */ +# define Q_GETQUOTA 0x0300 /* get limits and usage */ +# define Q_SETQUOTA 0x0400 /* set limits and usage */ +# define Q_SETUSE 0x0500 /* set usage */ +# define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ +# define Q_SETQLIM 0x0700 /* set limits */ +# define Q_GETSTATS 0x0800 /* get collected stats */ +# define Q_RSQUASH 0x1000 /* set root_squash option */ +#else +# define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */ +# define Q_QUOTAON 0x800002 /* turn quotas on */ +# define Q_QUOTAOFF 0x800003 /* turn quotas off */ +# define Q_GETFMT 0x800004 /* get quota format used on given filesystem */ +# define Q_GETINFO 0x800005 /* get information about quota files */ +# define Q_SETINFO 0x800006 /* set information about quota files */ +# define Q_GETQUOTA 0x800007 /* get user quota structure */ +# define Q_SETQUOTA 0x800008 /* set user quota structure */ +#endif + +/* + * The following structure defines the format of the disk quota file + * (as it appears on disk) - the file is an array of these structures + * indexed by user or group number. + */ +#if _LINUX_QUOTA_VERSION < 2 +struct dqblk + { + u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ + u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */ + u_int32_t dqb_curblocks; /* current block count */ + u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */ + u_int32_t dqb_isoftlimit; /* preferred inode limit */ + u_int32_t dqb_curinodes; /* current # allocated inodes */ + time_t dqb_btime; /* time limit for excessive disk use */ + time_t dqb_itime; /* time limit for excessive files */ + }; +#else + +/* Flags that indicate which fields in dqblk structure are valid. */ +#define QIF_BLIMITS 1 +#define QIF_SPACE 2 +#define QIF_ILIMITS 4 +#define QIF_INODES 8 +#define QIF_BTIME 16 +#define QIF_ITIME 32 +#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS) +#define QIF_USAGE (QIF_SPACE | QIF_INODES) +#define QIF_TIMES (QIF_BTIME | QIF_ITIME) +#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES) + +struct dqblk + { + u_int64_t dqb_bhardlimit; /* absolute limit on disk quota blocks alloc */ + u_int64_t dqb_bsoftlimit; /* preferred limit on disk quota blocks */ + u_int64_t dqb_curspace; /* current quota block count */ + u_int64_t dqb_ihardlimit; /* maximum # allocated inodes */ + u_int64_t dqb_isoftlimit; /* preferred inode limit */ + u_int64_t dqb_curinodes; /* current # allocated inodes */ + u_int64_t dqb_btime; /* time limit for excessive disk use */ + u_int64_t dqb_itime; /* time limit for excessive files */ + u_int32_t dqb_valid; /* bitmask of QIF_* constants */ + }; +#endif + +/* + * Shorthand notation. + */ +#define dq_bhardlimit dq_dqb.dqb_bhardlimit +#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit +#if _LINUX_QUOTA_VERSION < 2 +# define dq_curblocks dq_dqb.dqb_curblocks +#else +# define dq_curspace dq_dqb.dqb_curspace +# define dq_valid dq_dqb.dqb_valid +#endif +#define dq_ihardlimit dq_dqb.dqb_ihardlimit +#define dq_isoftlimit dq_dqb.dqb_isoftlimit +#define dq_curinodes dq_dqb.dqb_curinodes +#define dq_btime dq_dqb.dqb_btime +#define dq_itime dq_dqb.dqb_itime + +#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk))) + +#if _LINUX_QUOTA_VERSION < 2 +struct dqstats + { + u_int32_t lookups; + u_int32_t drops; + u_int32_t reads; + u_int32_t writes; + u_int32_t cache_hits; + u_int32_t pages_allocated; + u_int32_t allocated_dquots; + u_int32_t free_dquots; + u_int32_t syncs; + }; +#else + +/* Flags that indicate which fields in dqinfo structure are valid. */ +# define IIF_BGRACE 1 +# define IIF_IGRACE 2 +# define IIF_FLAGS 4 +# define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS) + +struct dqinfo + { + u_int64_t dqi_bgrace; + u_int64_t dqi_igrace; + u_int32_t dqi_flags; + u_int32_t dqi_valid; + }; +#endif + +__BEGIN_DECLS + +extern int quotactl (int __cmd, const char *__special, int __id, + caddr_t __addr) __THROW; + +__END_DECLS + +#endif /* sys/quota.h */ diff --git a/winsup/cygwin/kernel32.cc b/winsup/cygwin/kernel32.cc index 06bc7d082..22558f74b 100644 --- a/winsup/cygwin/kernel32.cc +++ b/winsup/cygwin/kernel32.cc @@ -1,6 +1,6 @@ /* kernel32.cc: Win32 replacement functions. - Copyright 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -311,7 +311,8 @@ CreateFileMappingW (HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, OBJECT_ATTRIBUTES attr; NTSTATUS status; ULONG flags = 0; - ACCESS_MASK access = READ_CONTROL | SECTION_QUERY | SECTION_MAP_READ; + ACCESS_MASK access = STANDARD_RIGHTS_REQUIRED + | SECTION_QUERY | SECTION_MAP_READ; ULONG prot = flProtect & (PAGE_NOACCESS | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE diff --git a/winsup/cygwin/lib/atexit.c b/winsup/cygwin/lib/atexit.c new file mode 100644 index 000000000..a2dec6267 --- /dev/null +++ b/winsup/cygwin/lib/atexit.c @@ -0,0 +1,49 @@ +/* atexit.c: atexit entry point + + Copyright 2014 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 +#include +#include + +/* Statically linked replacement for the former cygwin_atexit. We need + the function here to be able to access the correct __dso_handle of the + caller's DSO. */ + +int +atexit (void (*fn) (void)) +{ + extern int __cxa_atexit(void (*)(void*), void*, void*); + extern void *__dso_handle; + extern void *__ImageBase; + + void *fixed_dso_handle = &__dso_handle; + /* Check for being called from inside the executable. If so, use NULL + as __dso_handle. This allows to link executables with GCC versions + not providing __dso_handle in crtbegin{S}.o. In this case our own + __dso_handle defined in lib/dso_handle.c is used. However, our + __dso_handle always points to &__ImageBase, while the __dso_handle + for executables provided by crtbegin.o usually points to NULL. + That's what we remodel here. */ + if (&__ImageBase == (void **) GetModuleHandleW (NULL)) + fixed_dso_handle = NULL; + /* With recent Cygwin versions starting with API version 0.280 we call + __cxa_atexit (which is actually the cygwin__cxa_atexit wrapper in + dcrt0.cc) with the address of __dso_handle since that's how g++ generates + calls to __cxa_atexit as well. However, when running an application + built with this atexit under an older Cygwin version, the __cxa_atexit + entry point is the one from newlib, which expects the *value* of + __dso_handle. So, check for the Cygwin version we're running under. + Older version prior to 0.280 don't know CW_FIXED_ATEXIT and return -1. + 0.280 and later return 0. */ + else if (cygwin_internal (CW_FIXED_ATEXIT) != 0) + fixed_dso_handle = __dso_handle; + + return __cxa_atexit ((void (*)(void*))fn, NULL, fixed_dso_handle); +} diff --git a/winsup/cygwin/lib/dso_handle.c b/winsup/cygwin/lib/dso_handle.c new file mode 100644 index 000000000..c3069b732 --- /dev/null +++ b/winsup/cygwin/lib/dso_handle.c @@ -0,0 +1,12 @@ +/* dso_handle.c: Provide default __dso_handle. + + Copyright 2014 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. */ + +extern void *__ImageBase; +void *__dso_handle = &__ImageBase; diff --git a/winsup/cygwin/libc/bsdlib.cc b/winsup/cygwin/libc/bsdlib.cc index 696a9f856..89bc9431c 100644 --- a/winsup/cygwin/libc/bsdlib.cc +++ b/winsup/cygwin/libc/bsdlib.cc @@ -249,8 +249,7 @@ getprogname (void) extern "C" void setprogname (const char *newprogname) { - myfault efault; - if (!efault.faulted (EFAULT)) + __try { /* Per BSD man page, setprogname keeps a pointer to the last path component of the argument. It does *not* copy the @@ -261,6 +260,8 @@ setprogname (const char *newprogname) else __progname = (char *)newprogname; } + __except (EFAULT) {} + __endtry } extern "C" void diff --git a/winsup/cygwin/libc/rexec.cc b/winsup/cygwin/libc/rexec.cc index c3074d381..f9be2231a 100644 --- a/winsup/cygwin/libc/rexec.cc +++ b/winsup/cygwin/libc/rexec.cc @@ -312,100 +312,101 @@ extern "C" int cygwin_rexec (char **ahost, unsigned short rport, char *name, char *pass, char *cmd, int *fd2p) { - struct sockaddr_in sin, sin2, from; - struct hostent *hp; - u_short port = 0; - int s, timo = 1, s3; - char c; - static char ahostbuf[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + struct sockaddr_in sin, sin2, from; + struct hostent *hp; + u_short port = 0; + int s, timo = 1, s3; + char c; + static char ahostbuf[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - hp = cygwin_gethostbyname(*ahost); - if (hp == 0) { - cygwin_herror(*ahost); - return (-1); - } - *ahost = strcpy (ahostbuf, hp->h_name); - ruserpass(hp->h_name, &name, &pass, NULL); - if (!name) - name = getlogin (); - if (!pass) - pass = almost_null; + __try + { + hp = cygwin_gethostbyname(*ahost); + if (hp == 0) { + cygwin_herror(*ahost); + return (-1); + } + *ahost = strcpy (ahostbuf, hp->h_name); + ruserpass(hp->h_name, &name, &pass, NULL); + if (!name) + name = getlogin (); + if (!pass) + pass = almost_null; retry: - s = cygwin_socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) { - perror("rexec: socket"); - return (-1); - } - sin.sin_family = hp->h_addrtype; - sin.sin_port = rport; - bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); - if (cygwin_connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - if (errno == ECONNREFUSED && timo <= 16) { - (void) close(s); - sleep(timo); - timo *= 2; - goto retry; - } - perror(hp->h_name); - return (-1); - } - if (fd2p == 0) { - (void) write(s, "", 1); - } else { - char num[8]; - int s2, sin2len; + s = cygwin_socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("rexec: socket"); + return (-1); + } + sin.sin_family = hp->h_addrtype; + sin.sin_port = rport; + bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); + if (cygwin_connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if (errno == ECONNREFUSED && timo <= 16) { + (void) close(s); + sleep(timo); + timo *= 2; + goto retry; + } + perror(hp->h_name); + return (-1); + } + if (fd2p == 0) { + (void) write(s, "", 1); + } else { + char num[8]; + int s2, sin2len; - s2 = cygwin_socket(AF_INET, SOCK_STREAM, 0); - if (s2 < 0) { - (void) close(s); - return (-1); + s2 = cygwin_socket(AF_INET, SOCK_STREAM, 0); + if (s2 < 0) { + (void) close(s); + return (-1); + } + cygwin_listen(s2, 1); + sin2len = sizeof (sin2); + if (cygwin_getsockname(s2, (struct sockaddr *)&sin2, &sin2len) < 0 || + sin2len != sizeof (sin2)) { + perror("getsockname"); + (void) close(s2); + goto bad; + } + port = ntohs((u_short)sin2.sin_port); + (void) sprintf(num, "%u", port); + (void) write(s, num, strlen(num)+1); + { int len = sizeof (from); + s3 = cygwin_accept(s2, (struct sockaddr *)&from, &len); + close(s2); + if (s3 < 0) { + perror("accept"); + port = 0; + goto bad; } - cygwin_listen(s2, 1); - sin2len = sizeof (sin2); - if (cygwin_getsockname(s2, (struct sockaddr *)&sin2, &sin2len) < 0 || - sin2len != sizeof (sin2)) { - perror("getsockname"); - (void) close(s2); - goto bad; - } - port = ntohs((u_short)sin2.sin_port); - (void) sprintf(num, "%u", port); - (void) write(s, num, strlen(num)+1); - { int len = sizeof (from); - s3 = cygwin_accept(s2, (struct sockaddr *)&from, &len); - close(s2); - if (s3 < 0) { - perror("accept"); - port = 0; - goto bad; - } - } - *fd2p = s3; - } - (void) write(s, name, strlen(name) + 1); - /* should public key encypt the password here */ - (void) write(s, pass, strlen(pass) + 1); - (void) write(s, cmd, strlen(cmd) + 1); - if (read(s, &c, 1) != 1) { - perror(*ahost); - goto bad; - } - if (c != 0) { - while (read(s, &c, 1) == 1) { - (void) write(2, &c, 1); - if (c == '\n') - break; - } - goto bad; - } - return (s); + } + *fd2p = s3; + } + (void) write(s, name, strlen(name) + 1); + /* should public key encypt the password here */ + (void) write(s, pass, strlen(pass) + 1); + (void) write(s, cmd, strlen(cmd) + 1); + if (read(s, &c, 1) != 1) { + perror(*ahost); + goto bad; + } + if (c != 0) { + while (read(s, &c, 1) == 1) { + (void) write(2, &c, 1); + if (c == '\n') + break; + } + goto bad; + } + return (s); bad: - if (port) - (void) close(*fd2p); - (void) close(s); - return (-1); + if (port) + (void) close(*fd2p); + (void) close(s); + } + __except (EFAULT) {} + __endtry + return (-1); } diff --git a/winsup/cygwin/malloc.cc b/winsup/cygwin/malloc.cc index f9c3d557a..b1d5f6c04 100644 --- a/winsup/cygwin/malloc.cc +++ b/winsup/cygwin/malloc.cc @@ -1,14 +1,13 @@ /* This is a version (aka dlmalloc) of malloc/free/realloc written by Doug Lea and released to the public domain, as explained at - http://creativecommons.org/licenses/publicdomain. Send questions, + http://creativecommons.org/publicdomain/zero/1.0/ Send questions, comments, complaints, performance data, etc to dl@cs.oswego.edu -* Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) - +* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea Note: There may be an updated version of this malloc obtainable at - ftp://gee.cs.oswego.edu/pub/misc/malloc.c - Check before installing! + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! * Quickstart @@ -19,7 +18,7 @@ compile-time and dynamic tuning options. For convenience, an include file for code using this malloc is at: - ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.3.h + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h You don't really need this .h file unless you call functions not defined in your system include files. The .h file contains only the excerpts from this file needed for using this malloc on ANSI C/C++ @@ -41,19 +40,19 @@ than pointers, you can use a previous release of this malloc (e.g. 2.7.2) supporting these.) - Alignment: 8 bytes (default) + Alignment: 8 bytes (minimum) This suffices for nearly all current machines and C compilers. However, you can define MALLOC_ALIGNMENT to be wider than this if necessary (up to 128bytes), at the expense of using more space. Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) - 8 or 16 bytes (if 8byte sizes) + 8 or 16 bytes (if 8byte sizes) Each malloced chunk has a hidden word of overhead holding size and status information, and additional cross-check word if FOOTERS is defined. Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) - 8-byte ptrs: 32 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) Even a request for zero bytes (i.e., malloc(0)) returns a pointer to something of the minimum allocatable size. @@ -104,15 +103,28 @@ no errors or vulnerabilities, you can define INSECURE to 1, which might (or might not) provide a small performance improvement. - Thread-safety: NOT thread-safe unless USE_LOCKS defined + It is also possible to limit the maximum total allocatable + space, using malloc_set_footprint_limit. This is not + designed as a security feature in itself (calls to set limits + are not screened or privileged), but may be useful as one + aspect of a secure implementation. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero When USE_LOCKS is defined, each public call to malloc, free, - etc is surrounded with either a pthread mutex or a win32 - spinlock (depending on WIN32). This is not especially fast, and - can be a major bottleneck. It is designed only to provide - minimal protection in concurrent environments, and to provide a - basis for extensions. If you are using malloc in a concurrent - program, consider instead using ptmalloc, which is derived from - a version of this malloc. (See http://www.malloc.de). + etc is surrounded with a lock. By default, this uses a plain + pthread mutex, win32 critical section, or a spin-lock if if + available for the platform and not disabled by setting + USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, + recursive versions are used instead (which are not required for + base functionality but may be needed in layered extensions). + Using a global lock is not especially fast, and can be a major + bottleneck. It is designed only to provide minimal protection + in concurrent environments, and to provide a basis for + extensions. If you are using malloc in a concurrent program, + consider instead using nedmalloc + (http://www.nedprod.com/programs/portable/nedmalloc/) or + ptmalloc (See http://www.malloc.de), which are derived from + versions of this malloc. System requirements: Any combination of MORECORE and/or MMAP/MUNMAP This malloc can use unix sbrk or any emulation (invoked using @@ -153,7 +165,11 @@ a size_t, not counting any clearing in calloc or copying in realloc, or actions surrounding MORECORE and MMAP that have times proportional to the number of non-contiguous regions returned by - system allocation routines, which is often just 1. + system allocation routines, which is often just 1. In real-time + applications, you can optionally suppress segment traversals using + NO_SEGMENT_TRAVERSAL, which assures bounded execution even when + system allocators return non-contiguous spaces, at the typical + expense of carrying around more memory and increased fragmentation. The implementation is not very modular and seriously overuses macros. Perhaps someday all C compilers will do as good a job @@ -197,19 +213,39 @@ conformance is only weakly checked, so usage errors are not always caught). If FOOTERS is defined, then each chunk carries around a tag indicating its originating mspace, and frees are directed to their - originating spaces. + originating spaces. Normally, this requires use of locks. ------------------------- Compile-time options --------------------------- Be careful in setting #define values for numerical constants of type size_t. On some systems, literal values are not automatically extended -to size_t precision unless they are explicitly casted. +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. WIN32 default: defined if _WIN32 defined Defining WIN32 sets up defaults for MS environment and compilers. - Otherwise defaults are for unix. + Otherwise defaults are for unix. Beware that there seem to be some + cases where this malloc might not be a pure drop-in replacement for + Win32 malloc: Random-looking failures from Win32 GDI API's (eg; + SetDIBits()) may be due to bugs in some video driver implementations + when pixel buffers are malloc()ed, and the region spans more than + one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) + default granularity, pixel buffers may straddle virtual allocation + regions more often than when using the Microsoft allocator. You can + avoid this by using VirtualAlloc() and VirtualFree() for all pixel + buffers rather than using malloc(). If this is not possible, + recompile this malloc with a larger DEFAULT_GRANULARITY. Note: + in cases where MSC and gcc (cygwin) are known to differ on WIN32, + conditions use _MSC_VER to distinguish them. -MALLOC_ALIGNMENT default: (size_t)8 +DLMALLOC_EXPORT default: extern + Defines how public APIs are declared. If you want to export via a + Windows DLL, you might define this as + #define DLMALLOC_EXPORT extern __declspec(dllexport) + If you want a POSIX ELF shared object, you might use + #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) + +MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *)) Controls the minimum alignment for malloc'ed chunks. It must be a power of two and at least 8, even on machines for which smaller alignments would suffice. It may be defined as larger than this @@ -226,7 +262,27 @@ ONLY_MSPACES default: 0 (false) USE_LOCKS default: 0 (false) Causes each call to each public routine to be surrounded with pthread or WIN32 mutex lock/unlock. (If set true, this can be - overridden on a per-mspace basis for mspace versions.) + overridden on a per-mspace basis for mspace versions.) If set to a + non-zero value other than 1, locks are used, but their + implementation is left out, so lock functions must be supplied manually, + as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available + If true, uses custom spin locks for locking. This is currently + supported only gcc >= 4.1, older gccs on x86 platforms, and recent + MS compilers. Otherwise, posix locks or win32 critical sections are + used. + +USE_RECURSIVE_LOCKS default: not defined + If defined nonzero, uses recursive (aka reentrant) locks, otherwise + uses plain mutexes. This is not required for malloc proper, but may + be needed for layered allocators such as nedmalloc. + +LOCK_AT_FORK default: not defined + If defined nonzero, performs pthread_atfork upon initialization + to initialize child lock while holding parent lock. The implementation + assumes that pthread locks (not custom locks) are being used. In other + cases, you may need to customize the implementation. FOOTERS default: 0 If true, provide extra checking and dispatching by placing @@ -241,6 +297,12 @@ USE_DL_PREFIX default: NOT defined This can be useful when you only want to use this malloc in one part of a program, using your regular system malloc elsewhere. +MALLOC_INSPECT_ALL default: NOT defined + If defined, compiles malloc_inspect_all and mspace_inspect_all, that + perform traversal of all heap space. Unless access to these + functions is otherwise restricted, you probably do not want to + include them in secure implementations. + ABORT default: defined as abort() Defines how to abort on failed checks. On most systems, a failed check cannot die with an "assert" or even print an informative @@ -297,11 +359,9 @@ MORECORE default: sbrk size_t (sometimes declared as "intptr_t"). It doesn't much matter though. Internally, we only call it with arguments less than half the max value of a size_t, which should work across all reasonable - possibilities, although sometimes generating compiler warnings. See - near the end of this file for guidelines for creating a custom - version of MORECORE. + possibilities, although sometimes generating compiler warnings. -MORECORE_CONTIGUOUS default: 1 (true) +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE If true, take advantage of fact that consecutive calls to MORECORE with positive arguments always return contiguous increasing addresses. This is true of unix sbrk. It does not hurt too much to @@ -315,6 +375,12 @@ MORECORE_CANNOT_TRIM default: NOT defined using a hand-crafted MORECORE function that cannot handle negative arguments. +NO_SEGMENT_TRAVERSAL default: 0 + If non-zero, suppresses traversals of memory segments + returned by either MORECORE or CALL_MMAP. This disables + merging of segments that are contiguous, and selectively + releasing them to the OS if unused, but bounds execution times. + HAVE_MMAP default: 1 (true) True if this system supports mmap or an emulation of it. If so, and HAVE_MORECORE is not true, MMAP is used for all system @@ -329,17 +395,17 @@ HAVE_MREMAP default: 1 on linux, else 0 If true realloc() uses mremap() to re-allocate large blocks and extend or shrink allocation spaces. -MMAP_CLEARS default: 1 on unix +MMAP_CLEARS default: 1 except on WINCE. True if mmap clears memory so calloc doesn't need to. This is true - for standard unix mmap using /dev/zero. + for standard unix mmap using /dev/zero and on WIN32 except for WINCE. USE_BUILTIN_FFS default: 0 (i.e., not used) Causes malloc to use the builtin ffs() function to compute indices. Some compilers may recognize and intrinsify ffs to be faster than the supplied C version. Also, the case of x86 using gcc is special-cased to an asm instruction, so is already as fast as it can be, and so - this setting has no effect. (On most x86s, the asm version is only - slightly faster than the C version.) + this setting has no effect. Similarly for Win32 under recent MS compilers. + (On most x86s, the asm version is only slightly faster than the C version.) malloc_getpagesize default: derive from system includes, or 4096. The system page size. To the extent possible, this malloc manages @@ -362,6 +428,10 @@ MALLINFO_FIELD_TYPE default: size_t defined as "int" in SVID etc, but is more usefully defined as size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set +NO_MALLOC_STATS default: 0 + If defined, don't compile "malloc_stats". This avoids calls to + fprintf and bringing in stdio dependencies you might not want. + REALLOC_ZERO_BYTES_FREES default: not defined This should be set if a call to realloc with zero bytes should be the same as a call to free. Some people think it should. Otherwise, @@ -370,13 +440,13 @@ REALLOC_ZERO_BYTES_FREES default: not defined LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H -LACKS_STDLIB_H default: NOT defined unless on WIN32 +LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32 Define these if your system does not have these header files. You might need to manually insert some of the declarations they provide. DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, - system_info.dwAllocationGranularity in WIN32, - otherwise 64K. + system_info.dwAllocationGranularity in WIN32, + otherwise 64K. Also settable using mallopt(M_GRANULARITY, x) The unit for allocating and deallocating memory from the system. On most systems with contiguous MORECORE, there is no reason to @@ -436,16 +506,43 @@ DEFAULT_MMAP_THRESHOLD default: 256K empirically derived value that works well in most systems. You can disable mmap by setting to MAX_SIZE_T. +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP + The number of consolidated frees between checks to release + unused segments when freeing. When using non-contiguous segments, + especially with multiple mspaces, checking only for topmost space + doesn't always suffice to trigger trimming. To compensate for this, + free() will, with a period of MAX_RELEASE_CHECK_RATE (or the + current number of segments, if greater) try to release unused + segments to the OS when freeing chunks that result in + consolidation. The best value for this parameter is a compromise + between slowing down frees with relatively costly checks that + rarely trigger versus holding on to unused memory. To effectively + disable, set to MAX_SIZE_T. This may lead to a very slight speed + improvement at the expense of carrying around more memory. */ +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20806 +#endif /* DLMALLOC_VERSION */ + +#ifndef DLMALLOC_EXPORT +#define DLMALLOC_EXPORT extern +#endif + #ifndef WIN32 #ifdef _WIN32 #define WIN32 1 #endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define WIN32 1 +#endif /* _WIN32_WCE */ #endif /* WIN32 */ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include +#include #define HAVE_MMAP 1 #define HAVE_MORECORE 0 #define LACKS_UNISTD_H @@ -455,8 +552,17 @@ DEFAULT_MMAP_THRESHOLD default: 256K #define LACKS_STRINGS_H #define LACKS_SYS_TYPES_H #define LACKS_ERRNO_H +#define LACKS_SCHED_H +#ifndef MALLOC_FAILURE_ACTION #define MALLOC_FAILURE_ACTION -#define MMAP_CLEARS 0 /* WINCE and some others apparently don't clear */ +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef MMAP_CLEARS +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /*MMAP_CLEARS */ #endif /* WIN32 */ #if defined(DARWIN) || defined(_DARWIN) @@ -464,6 +570,10 @@ DEFAULT_MMAP_THRESHOLD default: 256K #ifndef HAVE_MORECORE #define HAVE_MORECORE 0 #define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif #endif /* HAVE_MORECORE */ #endif /* DARWIN */ @@ -477,6 +587,26 @@ DEFAULT_MMAP_THRESHOLD default: 256K /* The maximum possible size_t value has all bits set */ #define MAX_SIZE_T (~(size_t)0) +#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ +#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#endif /* USE_LOCKS */ + +#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ +#if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER>=1310)) +#ifndef USE_SPIN_LOCKS +#define USE_SPIN_LOCKS 1 +#endif /* USE_SPIN_LOCKS */ +#elif USE_SPIN_LOCKS +#error "USE_SPIN_LOCKS defined without implementation" +#endif /* ... locks available... */ +#elif !defined(USE_SPIN_LOCKS) +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS */ + #ifndef ONLY_MSPACES #define ONLY_MSPACES 0 #endif /* ONLY_MSPACES */ @@ -488,7 +618,7 @@ DEFAULT_MMAP_THRESHOLD default: 256K #endif /* ONLY_MSPACES */ #endif /* MSPACES */ #ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)8U) +#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) #endif /* MALLOC_ALIGNMENT */ #ifndef FOOTERS #define FOOTERS 0 @@ -502,12 +632,13 @@ DEFAULT_MMAP_THRESHOLD default: 256K #ifndef PROCEED_ON_ERROR #define PROCEED_ON_ERROR 0 #endif /* PROCEED_ON_ERROR */ -#ifndef USE_LOCKS -#define USE_LOCKS 0 -#endif /* USE_LOCKS */ + #ifndef INSECURE #define INSECURE 0 #endif /* INSECURE */ +#ifndef MALLOC_INSPECT_ALL +#define MALLOC_INSPECT_ALL 0 +#endif /* MALLOC_INSPECT_ALL */ #ifndef HAVE_MMAP #define HAVE_MMAP 1 #endif /* HAVE_MMAP */ @@ -517,6 +648,7 @@ DEFAULT_MMAP_THRESHOLD default: 256K #ifndef HAVE_MREMAP #ifdef linux #define HAVE_MREMAP 1 +#define _GNU_SOURCE /* Turns on mremap() definition */ #else /* linux */ #define HAVE_MREMAP 0 #endif /* linux */ @@ -534,15 +666,13 @@ DEFAULT_MMAP_THRESHOLD default: 256K #if !HAVE_MORECORE #define MORECORE_CONTIGUOUS 0 #else /* !HAVE_MORECORE */ -#ifndef MORECORE -#define MORECORE sbrk -#endif /* MORECORE */ +#define MORECORE_DEFAULT sbrk #ifndef MORECORE_CONTIGUOUS #define MORECORE_CONTIGUOUS 1 #endif /* MORECORE_CONTIGUOUS */ #endif /* HAVE_MORECORE */ #ifndef DEFAULT_GRANULARITY -#if MORECORE_CONTIGUOUS +#if (MORECORE_CONTIGUOUS || defined(WIN32)) #define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ #else /* MORECORE_CONTIGUOUS */ #define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) @@ -562,6 +692,13 @@ DEFAULT_MMAP_THRESHOLD default: 256K #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T #endif /* HAVE_MMAP */ #endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ #ifndef USE_BUILTIN_FFS #define USE_BUILTIN_FFS 0 #endif /* USE_BUILTIN_FFS */ @@ -574,6 +711,12 @@ DEFAULT_MMAP_THRESHOLD default: 256K #ifndef MALLINFO_FIELD_TYPE #define MALLINFO_FIELD_TYPE size_t #endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_MALLOC_STATS +#define NO_MALLOC_STATS 0 +#endif /* NO_MALLOC_STATS */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ /* mallopt tuning options. SVID/XPG defines four standard parameter @@ -616,7 +759,10 @@ DEFAULT_MMAP_THRESHOLD default: 256K #ifdef HAVE_USR_INCLUDE_MALLOC_H #include "/usr/include/malloc.h" #else /* HAVE_USR_INCLUDE_MALLOC_H */ - +#ifndef STRUCT_MALLINFO_DECLARED +/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ +#define _STRUCT_MALLINFO +#define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ @@ -629,13 +775,41 @@ struct mallinfo { MALLINFO_FIELD_TYPE fordblks; /* total free space */ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; - +#endif /* STRUCT_MALLINFO_DECLARED */ #endif /* HAVE_USR_INCLUDE_MALLOC_H */ #endif /* NO_MALLINFO */ +/* + Try to persuade compilers to inline. The most critical functions for + inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE + #if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define FORCEINLINE __forceinline + #endif +#endif +#ifndef NOINLINE + #if defined(__GNUC__) + #define NOINLINE __attribute__ ((noinline)) + #elif defined(_MSC_VER) + #define NOINLINE __declspec(noinline) + #else + #define NOINLINE + #endif +#endif + #ifdef __cplusplus extern "C" { +#ifndef FORCEINLINE + #define FORCEINLINE inline +#endif #endif /* __cplusplus */ +#ifndef FORCEINLINE + #define FORCEINLINE +#endif #if !ONLY_MSPACES @@ -646,7 +820,9 @@ extern "C" { #define dlfree free #define dlmalloc malloc #define dlmemalign memalign +#define dlposix_memalign posix_memalign #define dlrealloc realloc +#define dlrealloc_in_place realloc_in_place #define dlvalloc valloc #define dlpvalloc pvalloc #define dlmallinfo mallinfo @@ -656,11 +832,14 @@ extern "C" { #define dlmalloc_usable_size malloc_usable_size #define dlmalloc_footprint malloc_footprint #define dlmalloc_max_footprint malloc_max_footprint +#define dlmalloc_footprint_limit malloc_footprint_limit +#define dlmalloc_set_footprint_limit malloc_set_footprint_limit +#define dlmalloc_inspect_all malloc_inspect_all #define dlindependent_calloc independent_calloc #define dlindependent_comalloc independent_comalloc +#define dlbulk_free bulk_free #endif /* USE_DL_PREFIX */ - /* malloc(size_t n) Returns a pointer to a newly allocated chunk of at least n bytes, or @@ -675,7 +854,7 @@ extern "C" { maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ -void* dlmalloc(size_t); +DLMALLOC_EXPORT void* dlmalloc(size_t); /* free(void* p) @@ -684,14 +863,14 @@ void* dlmalloc(size_t); It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cause the current program to abort. */ -void dlfree(void*); +DLMALLOC_EXPORT void dlfree(void*); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ -void* dlcalloc(size_t, size_t); +DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) @@ -715,8 +894,22 @@ void* dlcalloc(size_t, size_t); The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ +DLMALLOC_EXPORT void* dlrealloc(void*, size_t); -void* dlrealloc(void*, size_t); +/* + realloc_in_place(void* p, size_t n) + Resizes the space allocated for p to size n, only if this can be + done without moving p (i.e., only if there is adjacent space + available if n is greater than p's current allocated size, or n is + less than or equal to p's size). This may be used instead of plain + realloc if an alternative allocation strategy is needed upon failure + to expand space; for example, reallocation of a buffer that must be + memory-aligned or cleared. You can use realloc_in_place to trigger + these alternatives only when needed. + + Returns p if successful; otherwise null. +*/ +DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); /* memalign(size_t alignment, size_t n); @@ -730,14 +923,24 @@ void* dlrealloc(void*, size_t); Overreliance on memalign is a sure way to fragment space. */ -void* dlmemalign(size_t, size_t); +DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); + +/* + int posix_memalign(void** pp, size_t alignment, size_t n); + Allocates a chunk of n bytes, aligned in accord with the alignment + argument. Differs from memalign only in that it (1) assigns the + allocated memory to *pp rather than returning it, (2) fails and + returns EINVAL if the alignment is not a power of two (3) fails and + returns ENOMEM if memory cannot be allocated. +*/ +DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ -void* dlvalloc(size_t); +DLMALLOC_EXPORT void* dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) @@ -745,7 +948,11 @@ void* dlvalloc(size_t); (parameter-number, parameter-value) pair. mallopt then sets the corresponding parameter to the argument value if it can (i.e., so long as the value is meaningful), and returns 1 if successful else - 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, normally defined in malloc.h. None of these are use in this malloc, so setting them has no effect. But this malloc also supports other options in mallopt. See below for details. Briefly, supported @@ -753,11 +960,11 @@ void* dlvalloc(size_t); configurations). Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (MAX_SIZE_T disables) + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) M_GRANULARITY -2 page size any power of 2 >= page size M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) */ -int dlmallopt(int, int); +DLMALLOC_EXPORT int dlmallopt(int, int); /* malloc_footprint(); @@ -768,7 +975,7 @@ int dlmallopt(int, int); Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ -size_t dlmalloc_footprint(void); +DLMALLOC_EXPORT size_t dlmalloc_footprint(void); /* malloc_max_footprint(); @@ -781,7 +988,66 @@ size_t dlmalloc_footprint(void); otherwise defined, this function does not use them, so results might not be up to date. */ -size_t dlmalloc_max_footprint(void); +DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); + +/* + malloc_footprint_limit(); + Returns the number of bytes that the heap is allowed to obtain from + the system, returning the last value returned by + malloc_set_footprint_limit, or the maximum size_t value if + never set. The returned value reflects a permission. There is no + guarantee that this number of bytes can actually be obtained from + the system. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); + +/* + malloc_set_footprint_limit(); + Sets the maximum number of bytes to obtain from the system, causing + failure returns from malloc and related functions upon attempts to + exceed this value. The argument value may be subject to page + rounding to an enforceable limit; this actual value is returned. + Using an argument of the maximum possible size_t effectively + disables checks. If the argument is less than or equal to the + current malloc_footprint, then all future allocations that require + additional system memory will fail. However, invocation cannot + retroactively deallocate existing used memory. +*/ +DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); + +#if MALLOC_INSPECT_ALL +/* + malloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg); + Traverses the heap and calls the given handler for each managed + region, skipping all bytes that are (or may be) used for bookkeeping + purposes. Traversal does not include include chunks that have been + directly memory mapped. Each reported region begins at the start + address, and continues up to but not including the end address. The + first used_bytes of the region contain allocated data. If + used_bytes is zero, the region is unallocated. The handler is + invoked with the given callback argument. If locks are defined, they + are held during the entire traversal. It is a bad idea to invoke + other malloc functions from within the handler. + + For example, to count the number of in-use chunks with size greater + than 1000, you could write: + static int count = 0; + void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: + malloc_inspect_all(count_chunks, NULL); + + malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. +*/ +DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); + +#endif /* MALLOC_INSPECT_ALL */ #if !NO_MALLINFO /* @@ -794,19 +1060,19 @@ size_t dlmalloc_max_footprint(void); hblks: current number of mmapped regions hblkhd: total bytes held in mmapped regions usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. + than current total if trimming has occurred. fsmblks: always zero uordblks: current total allocated space (normal or mmapped) fordblks: total free space keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) Because these fields are ints, but internal bookkeeping may be kept as longs, the reported values may wrap around zero and thus be inaccurate. */ -struct mallinfo dlmallinfo(void); +DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); #endif /* NO_MALLINFO */ /* @@ -833,11 +1099,8 @@ struct mallinfo dlmallinfo(void); is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use regular calloc and assign pointers into this - space to represent elements. (In this case though, you cannot - independently free elements.) + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. independent_calloc simplifies and speeds up implementations of many kinds of pools. It may also be useful when constructing large data @@ -861,7 +1124,7 @@ struct mallinfo dlmallinfo(void); return first; } */ -void** dlindependent_calloc(size_t, size_t, void**); +DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); @@ -885,11 +1148,8 @@ void** dlindependent_calloc(size_t, size_t, void**); null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). - Each element must be individually freed when it is no longer - needed. If you'd like to instead be able to free all at once, you - should instead use a single regular malloc, and assign pointers at - particular offsets in the aggregate space. (In this case though, you - cannot independently free elements.) + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. independent_comallac differs from independent_calloc in that each element may have a different size, and also that it does not @@ -922,15 +1182,25 @@ void** dlindependent_calloc(size_t, size_t, void**); since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ -void** dlindependent_comalloc(size_t, size_t*, void**); +DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); +/* + bulk_free(void* array[], size_t n_elements) + Frees and clears (sets to null) each non-null pointer in the given + array. This is likely to be faster than freeing them one-by-one. + If footers are used, pointers that have been allocated in different + mspaces are not freed or cleared, and the count of all such pointers + is returned. For large arrays of pointers with poor locality, it + may be worthwhile to sort this array before calling bulk_free. +*/ +DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ -void* dlpvalloc(size_t); +DLMALLOC_EXPORT void* dlpvalloc(size_t); /* malloc_trim(size_t pad); @@ -953,23 +1223,7 @@ void* dlpvalloc(size_t); Malloc_trim returns 1 if it actually released any memory, else 0. */ -int dlmalloc_trim(size_t); - -/* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); -*/ -size_t dlmalloc_usable_size(void*); +DLMALLOC_EXPORT int dlmalloc_trim(size_t); /* malloc_stats(); @@ -990,7 +1244,23 @@ size_t dlmalloc_usable_size(void*); malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ -void dlmalloc_stats(void); +DLMALLOC_EXPORT void dlmalloc_stats(void); + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t dlmalloc_usable_size(void*); #endif /* ONLY_MSPACES */ @@ -1013,7 +1283,7 @@ typedef void* mspace; compiling with a different DEFAULT_GRANULARITY or dynamically setting with mallopt(M_GRANULARITY, value). */ -mspace create_mspace(size_t capacity, int locked); +DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); /* destroy_mspace destroys the given space, and attempts to return all @@ -1021,7 +1291,7 @@ mspace create_mspace(size_t capacity, int locked); bytes freed. After destruction, the results of access to all memory used by the space become undefined. */ -size_t destroy_mspace(mspace msp); +DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); /* create_mspace_with_base uses the memory supplied as the initial base @@ -1032,13 +1302,27 @@ size_t destroy_mspace(mspace msp); Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ -mspace create_mspace_with_base(void* base, size_t capacity, int locked); +DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. +*/ +DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); + /* mspace_malloc behaves as malloc, but operates within the given space. */ -void* mspace_malloc(mspace msp, size_t bytes); +DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within @@ -1048,7 +1332,7 @@ void* mspace_malloc(mspace msp, size_t bytes); free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ -void mspace_free(mspace msp, void* mem); +DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within @@ -1059,45 +1343,45 @@ void mspace_free(mspace msp, void* mem); realloced chunks from any space are handled by their originating spaces. */ -void* mspace_realloc(mspace msp, void* mem, size_t newsize); +DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ -void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); +DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ -void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); +DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ -void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); +DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ -void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); +DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the system for this space. */ -size_t mspace_footprint(mspace msp); +DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); /* mspace_max_footprint() returns the peak number of bytes obtained from the system for this space. */ -size_t mspace_max_footprint(mspace msp); +DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); #if !NO_MALLINFO @@ -1105,30 +1389,35 @@ size_t mspace_max_footprint(mspace msp); mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ -struct mallinfo mspace_mallinfo(mspace msp); +DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); #endif /* NO_MALLINFO */ +/* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; +*/ +DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); + /* mspace_malloc_stats behaves as malloc_stats, but reports properties of the given space. */ -void mspace_malloc_stats(mspace msp); +DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); /* mspace_trim behaves as malloc_trim, but operates within the given space. */ -int mspace_trim(mspace msp, size_t pad); +DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); /* An alias for mallopt. */ -int mspace_mallopt(int, int); +DLMALLOC_EXPORT int mspace_mallopt(int, int); #endif /* MSPACES */ #ifdef __cplusplus -}; /* end of extern "C" */ +} /* end of extern "C" */ #endif /* __cplusplus */ /* @@ -1143,30 +1432,34 @@ int mspace_mallopt(int, int); /*------------------------------ internal #includes ---------------------- */ -#ifdef WIN32 +#ifdef _MSC_VER #pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* WIN32 */ - +#endif /* _MSC_VER */ +#if !NO_MALLOC_STATS #include /* for printing in malloc_stats */ - +#endif /* NO_MALLOC_STATS */ #ifndef LACKS_ERRNO_H #include /* for MALLOC_FAILURE_ACTION */ #endif /* LACKS_ERRNO_H */ -#if FOOTERS -#include /* for magic initialization */ -#endif /* FOOTERS */ -#ifndef LACKS_STDLIB_H -#include /* for abort() */ -#endif /* LACKS_STDLIB_H */ #ifdef DEBUG #if ABORT_ON_ASSERT_FAILURE +#undef assert #define assert(x) if(!(x)) ABORT #else /* ABORT_ON_ASSERT_FAILURE */ #include #endif /* ABORT_ON_ASSERT_FAILURE */ #else /* DEBUG */ +#ifndef assert #define assert(x) +#endif +#define DEBUG 0 #endif /* DEBUG */ +#if !defined(WIN32) && !defined(LACKS_TIME_H) +#include /* for magic initialization */ +#endif /* WIN32 */ +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ #ifndef LACKS_STRING_H #include /* for memset etc */ #endif /* LACKS_STRING_H */ @@ -1177,21 +1470,83 @@ int mspace_mallopt(int, int); #endif /* USE_BUILTIN_FFS */ #if HAVE_MMAP #ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 #include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ #endif /* LACKS_SYS_MMAN_H */ #ifndef LACKS_FCNTL_H #include #endif /* LACKS_FCNTL_H */ #endif /* HAVE_MMAP */ -#if HAVE_MORECORE #ifndef LACKS_UNISTD_H -#include /* for sbrk */ +#include /* for sbrk, sysconf */ #else /* LACKS_UNISTD_H */ #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) extern void* sbrk(ptrdiff_t); #endif /* FreeBSD etc */ #endif /* LACKS_UNISTD_H */ -#endif /* HAVE_MMAP */ + +/* Declarations for locking */ +#if USE_LOCKS +#ifndef WIN32 +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#elif !defined(LACKS_SCHED_H) +#include +#endif /* solaris or LACKS_SCHED_H */ +#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS +#include +#endif /* USE_RECURSIVE_LOCKS ... */ +#elif defined(_MSC_VER) +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); +LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif defined(WIN32) && defined(__GNUC__) +#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) +#define interlockedexchange __sync_lock_test_and_set +#endif /* Win32 */ +#else /* USE_LOCKS */ +#endif /* USE_LOCKS */ + +#ifndef LOCK_AT_FORK +#define LOCK_AT_FORK 0 +#endif + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ #ifndef WIN32 #ifndef malloc_getpagesize @@ -1247,10 +1602,11 @@ extern void* sbrk(ptrdiff_t); #define SIZE_T_BITSIZE (sizeof(size_t) << 3) /* Some constants coerced to size_t */ -/* Annoying but necessary to avoid errors on some plaftorms */ +/* Annoying but necessary to avoid errors on some platforms */ #define SIZE_T_ZERO ((size_t)0) #define SIZE_T_ONE ((size_t)1) #define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) #define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) @@ -1280,26 +1636,17 @@ extern void* sbrk(ptrdiff_t); #define MFAIL ((void*)(MAX_SIZE_T)) #define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ -#if !HAVE_MMAP -#define IS_MMAPPED_BIT (SIZE_T_ZERO) -#define USE_MMAP_BIT (SIZE_T_ZERO) -#define CALL_MMAP(s) MFAIL -#define CALL_MUNMAP(a, s) (-1) -#define DIRECT_MMAP(s) MFAIL - -#else /* HAVE_MMAP */ -#define IS_MMAPPED_BIT (SIZE_T_ONE) -#define USE_MMAP_BIT (SIZE_T_ONE) +#if HAVE_MMAP #ifndef WIN32 -#define CALL_MUNMAP(a, s) munmap((a), (s)) +#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) #define MMAP_PROT (PROT_READ|PROT_WRITE) #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif /* MAP_ANON */ #ifdef MAP_ANONYMOUS #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) -#define CALL_MMAP(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) #else /* MAP_ANONYMOUS */ /* Nearly all versions of mmap support MAP_ANONYMOUS, so the following @@ -1307,37 +1654,38 @@ extern void* sbrk(ptrdiff_t); */ #define MMAP_FLAGS (MAP_PRIVATE) static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ -#define CALL_MMAP(s) ((dev_zero_fd < 0) ? \ - (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) #endif /* MAP_ANONYMOUS */ -#define DIRECT_MMAP(s) CALL_MMAP(s) +#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) + #else /* WIN32 */ /* Win32 MMAP via VirtualAlloc */ -static void* win32mmap(size_t size) { +static FORCEINLINE void* win32mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ -static void* win32direct_mmap(size_t size) { +static FORCEINLINE void* win32direct_mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); + PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* This function supports releasing coalesed segments */ -static int win32munmap(void* ptr, size_t size) { +static FORCEINLINE int win32munmap(void* ptr, size_t size) { MEMORY_BASIC_INFORMATION minfo; - char* cptr = ptr; + char* cptr = (char*)ptr; while (size) { if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) return -1; if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || - minfo.State != MEM_COMMIT || minfo.RegionSize > size) + minfo.State != MEM_COMMIT || minfo.RegionSize > size) return -1; if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) return -1; @@ -1347,24 +1695,76 @@ static int win32munmap(void* ptr, size_t size) { return 0; } -#define CALL_MMAP(s) win32mmap(s) -#define CALL_MUNMAP(a, s) win32munmap((a), (s)) -#define DIRECT_MMAP(s) win32direct_mmap(s) +#define MMAP_DEFAULT(s) win32mmap(s) +#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) #endif /* WIN32 */ #endif /* HAVE_MMAP */ -#if HAVE_MMAP && HAVE_MREMAP -#define CALL_MREMAP(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) -#else /* HAVE_MMAP && HAVE_MREMAP */ -#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ +#if HAVE_MREMAP +#ifndef WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* WIN32 */ +#endif /* HAVE_MREMAP */ +/** + * Define CALL_MORECORE + */ #if HAVE_MORECORE -#define CALL_MORECORE(S) MORECORE(S) + #ifdef MORECORE + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ #else /* HAVE_MORECORE */ -#define CALL_MORECORE(S) MFAIL + #define CALL_MORECORE(S) MFAIL #endif /* HAVE_MORECORE */ +/** + * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP + */ +#if HAVE_MMAP + #define USE_MMAP_BIT (SIZE_T_ONE) + + #ifdef MMAP + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ + #ifdef MUNMAP + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ + #ifdef DIRECT_MMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** + * Define CALL_MREMAP + */ +#if HAVE_MMAP && HAVE_MREMAP + #ifdef MREMAP + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + /* mstate bit set if continguous morecore disabled or failed */ #define USE_NONCONTIGUOUS_BIT (4U) @@ -1374,92 +1774,277 @@ static int win32munmap(void* ptr, size_t size) { /* --------------------------- Lock preliminaries ------------------------ */ -#if USE_LOCKS - /* - When locks are defined, there are up to two global locks: + When locks are defined, there is one global lock, plus + one per-mspace lock. - * If HAVE_MORECORE, morecore_mutex protects sequences of calls to - MORECORE. In many cases sys_alloc requires two calls, that should - not be interleaved with calls by other threads. This does not - protect against direct calls to MORECORE by other threads not - using this lock, so there is still code to cope the best we can on - interference. + The global lock_ensures that mparams.magic and other unique + mparams values are initialized only once. It also protects + sequences of calls to MORECORE. In many cases sys_alloc requires + two calls, that should not be interleaved with calls by other + threads. This does not protect against direct calls to MORECORE + by other threads not using this lock, so there is still code to + cope the best we can on interference. + + Per-mspace locks surround calls to malloc, free, etc. + By default, locks are simple non-reentrant mutexes. + + Because lock-protected regions generally have bounded times, it is + OK to use the supplied simple spinlocks. Spinlocks are likely to + improve performance for lightly contended applications, but worsen + performance under heavy contention. + + If USE_LOCKS is > 1, the definitions of lock routines here are + bypassed, in which case you will need to define the type MLOCK_T, + and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK + and TRY_LOCK. You must also declare a + static MLOCK_T malloc_global_mutex = { initialization values };. - * magic_init_mutex ensures that mparams.magic and other - unique mparams values are initialized only once. */ -#ifndef WIN32 -/* By default use posix locks */ -#include -#define MLOCK_T pthread_mutex_t -#define INITIAL_LOCK(l) pthread_mutex_init(l, NULL) -#define ACQUIRE_LOCK(l) pthread_mutex_lock(l) -#define RELEASE_LOCK(l) pthread_mutex_unlock(l) +#if !USE_LOCKS +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) (0) +#define DESTROY_LOCK(l) (0) +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() -#if HAVE_MORECORE -static MLOCK_T morecore_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif /* HAVE_MORECORE */ +#else +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ -static MLOCK_T magic_init_mutex = PTHREAD_MUTEX_INITIALIZER; +#elif USE_SPIN_LOCKS -#else /* WIN32 */ +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +/* Custom spin locks for older gcc on x86 */ +static FORCEINLINE int x86_cas_lock(int *sl) { + int ret; + int val = 1; + int cmp = 0; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); + return ret; +} + +static FORCEINLINE void x86_clear_lock(int* sl) { + assert(*sl != 0); + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); +} + +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) + +#endif /* ... gcc spins locks ... */ + +/* How to yield for a spin lock */ +#define SPINS_PER_YIELD 63 +#if defined(_MSC_VER) +#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ +#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) +#elif defined (__SVR4) && defined (__sun) /* solaris */ +#define SPIN_LOCK_YIELD thr_yield(); +#elif !defined(LACKS_SCHED_H) +#define SPIN_LOCK_YIELD sched_yield(); +#else +#define SPIN_LOCK_YIELD +#endif /* ... yield ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +/* Plain spin locks use single word (embedded in malloc_states) */ +static int spin_acquire_lock(int *sl) { + int spins = 0; + while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } + return 0; +} + +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) +#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) +static MLOCK_T malloc_global_mutex = 0; + +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else /* - Because lock-protected regions have bounded times, and there - are no recursive lock calls, we can use simple spinlocks. + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. */ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) +#endif -#define MLOCK_T long -static int win32_acquire_lock (MLOCK_T *sl) { - for (;;) { -#ifdef InterlockedCompareExchangePointer - if (!InterlockedCompareExchange(sl, 1, 0)) - return 0; -#else /* Use older void* version */ - if (!InterlockedCompareExchange((void**)sl, (void*)1, (void*)0)) - return 0; -#endif /* InterlockedCompareExchangePointer */ - Sleep (0); +struct malloc_recursive_lock { + int sl; + unsigned int c; + THREAD_ID_T threadid; +}; + +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + +static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); } } -static void win32_release_lock (MLOCK_T *sl) { - InterlockedExchange (sl, 0); +static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + int spins = 0; + for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 0; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 0; + } + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } } -#define INITIAL_LOCK(l) *(l)=0 -#define ACQUIRE_LOCK(l) win32_acquire_lock(l) -#define RELEASE_LOCK(l) win32_release_lock(l) -#if HAVE_MORECORE -static MLOCK_T morecore_mutex; -#endif /* HAVE_MORECORE */ -static MLOCK_T magic_init_mutex; -#endif /* WIN32 */ +static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 1; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 1; + } + return 0; +} +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile LONG malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status, (LONG)1); + return; + } + SleepEx(0, FALSE); + } +} + +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int pthread_init_lock (MLOCK_T *lk) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; +#endif + if (pthread_mutex_init(lk, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#endif /* ... lock types ... */ + +/* Common code for all lock types */ #define USE_LOCK_BIT (2U) -#else /* USE_LOCKS */ -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) + +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif + +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif + #endif /* USE_LOCKS */ -#if USE_LOCKS && HAVE_MORECORE -#define ACQUIRE_MORECORE_LOCK() ACQUIRE_LOCK(&morecore_mutex); -#define RELEASE_MORECORE_LOCK() RELEASE_LOCK(&morecore_mutex); -#else /* USE_LOCKS && HAVE_MORECORE */ -#define ACQUIRE_MORECORE_LOCK() -#define RELEASE_MORECORE_LOCK() -#endif /* USE_LOCKS && HAVE_MORECORE */ - -#if USE_LOCKS -#define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex); -#define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex); -#else /* USE_LOCKS */ -#define ACQUIRE_MAGIC_INIT_LOCK() -#define RELEASE_MAGIC_INIT_LOCK() -#endif /* USE_LOCKS */ - - /* ----------------------- Chunk representations ------------------------ */ /* @@ -1485,21 +2070,21 @@ static MLOCK_T magic_init_mutex; A chunk that's in use looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk (if P = 1) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| - | Size of this chunk 1| +-+ + | Size of previous chunk (if P = 0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - +- -+ - | | - +- -+ - | : - +- size - sizeof(size_t) available payload bytes -+ - : | + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | chunk-> +- -+ - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| | Size of next chunk (may or may not be in use) | +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -1507,21 +2092,21 @@ static MLOCK_T magic_init_mutex; And if it's free, it looks like this: chunk-> +- -+ - | User payload (must be in use, or we would have merged!) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| - | Size of this chunk 0| +-+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Next pointer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Prev pointer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | : - +- size - sizeof(struct chunk) unused bytes -+ - : | + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of this chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| | Size of next chunk (must be in use, or we would have merged)| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -1529,8 +2114,8 @@ static MLOCK_T magic_init_mutex; +- User payload -+ : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |0| - +-+ + |0| + +-+ Note that since we always merge adjacent free chunks, the chunks adjacent to a free chunk must be in use. @@ -1556,13 +2141,14 @@ static MLOCK_T magic_init_mutex; The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of the chunk size redundantly records whether the current chunk is - inuse. This redundancy enables usage checks within free and realloc, - and reduces indirection when freeing and consolidating chunks. + inuse (unless the chunk is mmapped). This redundancy enables usage + checks within free and realloc, and reduces indirection when freeing + and consolidating chunks. Each freshly allocated chunk must have both cinuse and pinuse set. That is, each allocated chunk borders either a previously allocated and still in-use chunk, or the base of its memory arena. This is - ensured by making all allocations from the the `lowest' part of any + ensured by making all allocations from the `lowest' part of any found chunk. Further, no free chunk physically borders another one, so each free chunk is known to be preceded and followed by either inuse chunks or the ends of memory. @@ -1575,25 +2161,24 @@ static MLOCK_T magic_init_mutex; The exceptions to all this are 1. The special chunk `top' is the top-most available chunk (i.e., - the one bordering the end of available memory). It is treated - specially. Top is never included in any bin, is used only if - no other chunk is available, and is released back to the - system if it is very large (see M_TRIM_THRESHOLD). In effect, - the top chunk is treated as larger (and thus less well - fitting) than any other available chunk. The top chunk - doesn't update its trailing size field since there is no next - contiguous chunk that would have to index off it. However, - space is still allocated for it (TOP_FOOT_SIZE) to enable - separation or merging when space is extended. + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. - 3. Chunks allocated via mmap, which have the lowest-order bit - (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set - PINUSE_BIT in their head fields. Because they are allocated - one-by-one, each must carry its own prev_foot field, which is - also used to hold the offset this chunk has within its mmapped - region, which is needed to preserve alignment. Each mmapped - chunk is trailed by the first two fields of a fake next-chunk - for sake of usage checks. + 3. Chunks allocated via mmap, have both cinuse and pinuse bits + cleared in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. */ @@ -1654,14 +2239,16 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ /* The head field of a chunk is or'ed with PINUSE_BIT when previous adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in - use. If the chunk was obtained with mmap, the prev_foot field has - IS_MMAPPED_BIT set, otherwise holding the offset of the base of the - mmapped region to the base of the chunk. + use, unless mmapped, in which case both bits are cleared. + + FLAG4_BIT is not used by this malloc, but might be useful in extensions. */ #define PINUSE_BIT (SIZE_T_ONE) #define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) #define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) /* Head value for fenceposts */ #define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) @@ -1669,17 +2256,22 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ /* extraction of fields from head words */ #define cinuse(p) ((p)->head & CINUSE_BIT) #define pinuse(p) ((p)->head & PINUSE_BIT) -#define chunksize(p) ((p)->head & ~(INUSE_BITS)) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) #define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) -#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) /* Treat space at ptr +/- offset as a chunk */ #define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) #define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) /* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS))) +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) #define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) /* extract next chunk's pinuse bit */ @@ -1697,9 +2289,6 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ #define set_free_with_pinuse(p, s, n)\ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) -#define is_mmapped(p)\ - (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT)) - /* Get the internal overhead associated with chunk p */ #define overhead_for(p)\ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) @@ -1721,20 +2310,20 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ like this: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Forward pointer to next chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Back pointer to previous chunk in list | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Unused space (may be 0 bytes long) . - . . - . | + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Larger chunks are kept in a form of bitwise digital trees (aka tries) keyed on chunksizes. Because malloc_tree_chunks are only for @@ -1742,27 +2331,27 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ constraints on user chunk sizes. Each node looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Size of previous chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Forward pointer to next chunk of same size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Back pointer to previous chunk of same size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to left child (child[0]) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to right child (child[1]) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer to parent | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | bin index of this chunk | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Unused space . - . | + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks of the same size are arranged in a circularly-linked list, with only @@ -1870,7 +2459,7 @@ typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ and so should not try to deallocate or merge with others. (This currently holds only for the initial segment passed into create_mspace_with_base.) - * If IS_MMAPPED_BIT set, the segment may be merged with + * If USE_MMAP_BIT set, the segment may be merged with other surrounding mmapped segments and trimmed/de-allocated using munmap. * If neither bit is set, then the segment was obtained using @@ -1885,7 +2474,7 @@ struct malloc_segment { flag_t sflags; /* mmap and extern flag */ }; -#define is_mmapped_segment(S) ((S)->sflags & IS_MMAPPED_BIT) +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) #define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) typedef struct malloc_segment msegment; @@ -1954,6 +2543,9 @@ typedef struct malloc_segment* msegmentptr; Magic tag A cross-check field that should always hold same value as mparams.magic. + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + Flags Bits recording whether to use MMAP, locks, or contiguous MORECORE @@ -1961,9 +2553,18 @@ typedef struct malloc_segment* msegmentptr; Each space keeps track of current and maximum system memory obtained via MORECORE or MMAP. + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + Locking If USE_LOCKS is defined, the "mutex" lock is acquired and released around every public call using this mspace. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. */ /* Bin types, widths and sizes */ @@ -1985,16 +2586,20 @@ struct malloc_state { mchunkptr dv; mchunkptr top; size_t trim_check; + size_t release_checks; size_t magic; mchunkptr smallbins[(NSMALLBINS+1)*2]; tbinptr treebins[NTREEBINS]; size_t footprint; size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ flag_t mflags; #if USE_LOCKS MLOCK_T mutex; /* locate lock among fields that rarely change */ #endif /* USE_LOCKS */ msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; }; typedef struct malloc_state* mstate; @@ -2004,7 +2609,8 @@ typedef struct malloc_state* mstate; /* malloc_params holds global properties, including those that can be dynamically set using mallopt. There is a single instance, mparams, - initialized in init_mparams. + initialized in init_mparams. Note that the non-zeroness of "magic" + also serves as an initialization flag. */ struct malloc_params { @@ -2018,10 +2624,18 @@ struct malloc_params { static struct malloc_params mparams; +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + /* The global malloc_state used for all non-"mspace" calls */ static struct malloc_state _gm_; #define gm (&_gm_) #define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + #define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ @@ -2030,11 +2644,19 @@ static struct malloc_state _gm_; #define use_lock(M) ((M)->mflags & USE_LOCK_BIT) #define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#if USE_LOCKS #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) +#else +#define disable_lock(M) +#endif #define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) #define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#if HAVE_MMAP #define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) +#else +#define disable_mmap(M) +#endif #define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) #define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) @@ -2046,11 +2668,23 @@ static struct malloc_state _gm_; /* page-align a size */ #define page_align(S)\ - (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE)) + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) /* granularity-align a size */ #define granularity_align(S)\ - (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE)) + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) #define is_page_aligned(S)\ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) @@ -2107,11 +2741,7 @@ static int has_segment_link(mstate m, msegmentptr ss) { */ #if USE_LOCKS - -/* Ensure locks are initialized */ -#define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams()) - -#define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) #define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } #else /* USE_LOCKS */ @@ -2156,6 +2786,7 @@ static void reset_on_error(mstate m); #endif /* PROCEED_ON_ERROR */ + /* -------------------------- Debugging setup ---------------------------- */ #if ! DEBUG @@ -2192,7 +2823,7 @@ static size_t traverse_and_check(mstate m); /* ---------------------------- Indexing Bins ---------------------------- */ #define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) #define small_index2size(i) ((i) << SMALLBIN_SHIFT) #define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) @@ -2200,8 +2831,36 @@ static size_t traverse_and_check(mstate m); #define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) #define treebin_at(M,i) (&((M)->treebins[i])) -/* assign tree index for size S to variable I */ -#if defined(__GNUC__) && defined(i386) +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ @@ -2211,10 +2870,11 @@ static size_t traverse_and_check(mstate m); I = NTREEBINS-1;\ else {\ unsigned int K;\ - __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ + _BitScanReverse((DWORD *) &K, (DWORD) X);\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } + #else /* GNUC */ #define compute_tree_index(S, I)\ {\ @@ -2264,21 +2924,45 @@ static size_t traverse_and_check(mstate m); #define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) #define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) -/* index corresponding to given bit */ +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) -#if defined(__GNUC__) && defined(i386) +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ - __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\ + J = __builtin_ctz(X); \ I = (bindex_t)J;\ } -#else /* GNUC */ -#if USE_BUILTIN_FFS +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS #define compute_bit2idx(X, I) I = ffs(X)-1 -#else /* USE_BUILTIN_FFS */ +#else #define compute_bit2idx(X, I)\ {\ unsigned int Y = X - 1;\ @@ -2290,18 +2974,8 @@ static size_t traverse_and_check(mstate m); N += K = Y >> (1-0) & 1; Y >>= K;\ I = (bindex_t)(N + Y);\ } -#endif /* USE_BUILTIN_FFS */ #endif /* GNUC */ -/* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) - -/* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x<<1) | -(x<<1)) - -/* mask with all bits to left of or equal to least bit of x on */ -#define same_or_left_bits(x) ((x) | -(x)) - /* ----------------------- Runtime Check Support ------------------------- */ @@ -2323,7 +2997,7 @@ static size_t traverse_and_check(mstate m); http://www.usenix.org/events/lisa03/tech/robertson.html The footer of an inuse chunk holds the xor of its mstate and a random seed, that is checked upon calls to free() and realloc(). This is - (probablistically) unguessable from outside the program, but can be + (probabalistically) unguessable from outside the program, but can be computed by any code successfully malloc'ing any chunk, so does not itself provide protection against code that has already broken security through some other means. Unlike Robertson et al, we @@ -2336,15 +3010,15 @@ static size_t traverse_and_check(mstate m); #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) /* Check if address of next chunk n is higher than base chunk p */ #define ok_next(p, n) ((char*)(p) < (char*)(n)) -/* Check if p has its cinuse bit on */ -#define ok_cinuse(p) cinuse(p) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) /* Check if p has its pinuse bit on */ #define ok_pinuse(p) pinuse(p) #else /* !INSECURE */ #define ok_address(M, a) (1) #define ok_next(b, n) (1) -#define ok_cinuse(p) (1) +#define ok_inuse(p) (1) #define ok_pinuse(p) (1) #endif /* !INSECURE */ @@ -2355,7 +3029,6 @@ static size_t traverse_and_check(mstate m); #define ok_magic(M) (1) #endif /* (FOOTERS && !INSECURE) */ - /* In gcc, use __builtin_expect to minimize impact of checks */ #if !INSECURE #if defined(__GNUC__) && __GNUC__ >= 3 @@ -2373,6 +3046,8 @@ static size_t traverse_and_check(mstate m); #define mark_inuse_foot(M,p,s) +/* Macros for setting head/foot of non-mmapped chunks */ + /* Set cinuse bit and pinuse bit of next chunk */ #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ @@ -2415,60 +3090,35 @@ static size_t traverse_and_check(mstate m); /* ---------------------------- setting mparams -------------------------- */ +#if LOCK_AT_FORK +static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } +static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } +static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } +#endif /* LOCK_AT_FORK */ + /* Initialize mparams */ static int init_mparams(void) { - if (mparams.page_size == 0) { - size_t s; +#ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); +#endif - mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; - mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; -#if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ - -#if (FOOTERS && !INSECURE) - { -#if USE_DEV_RANDOM - int fd; - unsigned char buf[sizeof(size_t)]; - /* Try to use /dev/urandom, else fall back on using time */ - if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && - read(fd, buf, sizeof(buf)) == sizeof(buf)) { - s = *((size_t *) buf); - close(fd); - } - else -#endif /* USE_DEV_RANDOM */ - s = (size_t)(time(0) ^ (size_t)0x55555555U); - - s |= (size_t)8U; /* ensure nonzero */ - s &= ~(size_t)7U; /* improve chances of fault for bad values */ - - } -#else /* (FOOTERS && !INSECURE) */ - s = (size_t)0x58585858U; -#endif /* (FOOTERS && !INSECURE) */ - ACQUIRE_MAGIC_INIT_LOCK(); - if (mparams.magic == 0) { - mparams.magic = s; - /* Set up lock for main malloc area */ - INITIAL_LOCK(&gm->mutex); - gm->mflags = mparams.default_mflags; - } - RELEASE_MAGIC_INIT_LOCK(); + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; #ifndef WIN32 - mparams.page_size = malloc_getpagesize; - mparams.granularity = ((DEFAULT_GRANULARITY != 0)? - DEFAULT_GRANULARITY : mparams.page_size); + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); #else /* WIN32 */ { SYSTEM_INFO system_info; GetSystemInfo(&system_info); - mparams.page_size = system_info.dwPageSize; - mparams.granularity = system_info.dwAllocationGranularity; + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); } #endif /* WIN32 */ @@ -2479,22 +3129,68 @@ static int init_mparams(void) { Alignment, min chunk size, and page size must all be powers of 2. */ if ((sizeof(size_t) != sizeof(char*)) || - (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || - (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || - ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) || - ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0)) + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) ABORT; + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + (void)INITIAL_LOCK(&gm->mutex); +#endif +#if LOCK_AT_FORK + pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ +#ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#elif defined(LACKS_TIME_H) + magic = (size_t)&magic ^ (size_t)0x55555555U; +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + /* Until memory modes commonly available, use volatile-write */ + (*(volatile size_t *)(&(mparams.magic))) = magic; + } } - return 0; + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; } /* support for mallopt */ static int change_mparam(int param_number, int value) { - size_t val = (size_t)value; - init_mparams(); + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; switch(param_number) { case M_TRIM_THRESHOLD: mparams.trim_threshold = val; @@ -2526,7 +3222,7 @@ static void do_check_any_chunk(mstate m, mchunkptr p) { /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { msegmentptr sp = segment_holding(m, (char*)p); - size_t sz = chunksize(p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ assert(sp != 0); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); @@ -2534,13 +3230,13 @@ static void do_check_top_chunk(mstate m, mchunkptr p) { assert(sz > 0); assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); assert(pinuse(p)); - assert(!next_pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); } /* Check properties of (inuse) mmapped chunks */ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { size_t sz = chunksize(p); - size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); assert(is_mmapped(p)); assert(use_mmap(m)); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); @@ -2554,7 +3250,7 @@ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { do_check_any_chunk(m, p); - assert(cinuse(p)); + assert(is_inuse(p)); assert(next_pinuse(p)); /* If not pinuse and not mmapped, previous chunk has OK offset */ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); @@ -2564,10 +3260,10 @@ static void do_check_inuse_chunk(mstate m, mchunkptr p) { /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { - size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + size_t sz = chunksize(p); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); - assert(!cinuse(p)); + assert(!is_inuse(p)); assert(!next_pinuse(p)); assert (!is_mmapped(p)); if (p != m->dv && p != m->top) { @@ -2576,7 +3272,7 @@ static void do_check_free_chunk(mstate m, mchunkptr p) { assert(is_aligned(chunk2mem(p))); assert(next->prev_foot == sz); assert(pinuse(p)); - assert (next == m->top || cinuse(next)); + assert (next == m->top || is_inuse(next)); assert(p->fd->bk == p); assert(p->bk->fd == p); } @@ -2589,7 +3285,7 @@ static void do_check_free_chunk(mstate m, mchunkptr p) { static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { if (mem != 0) { mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + size_t sz = p->head & ~INUSE_BITS; do_check_inuse_chunk(m, p); assert((sz & CHUNK_ALIGN_MASK) == 0); assert(sz >= MIN_CHUNK_SIZE); @@ -2616,7 +3312,7 @@ static void do_check_tree(mstate m, tchunkptr t) { do_check_any_chunk(m, ((mchunkptr)u)); assert(u->index == tindex); assert(chunksize(u) == tsize); - assert(!cinuse(u)); + assert(!is_inuse(u)); assert(!next_pinuse(u)); assert(u->fd->bk == u); assert(u->bk->fd == u); @@ -2629,20 +3325,20 @@ static void do_check_tree(mstate m, tchunkptr t) { head = u; assert(u->parent != u); assert (u->parent->child[0] == u || - u->parent->child[1] == u || - *((tbinptr*)(u->parent)) == u); + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); if (u->child[0] != 0) { - assert(u->child[0]->parent == u); - assert(u->child[0] != u); - do_check_tree(m, u->child[0]); + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); } if (u->child[1] != 0) { - assert(u->child[1]->parent == u); - assert(u->child[1] != u); - do_check_tree(m, u->child[1]); + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); } if (u->child[0] != 0 && u->child[1] != 0) { - assert(chunksize(u->child[0]) < chunksize(u->child[1])); + assert(chunksize(u->child[0]) < chunksize(u->child[1])); } } u = u->fd; @@ -2680,7 +3376,7 @@ static void do_check_smallbin(mstate m, bindex_t i) { /* chunk is followed by an inuse chunk */ q = next_chunk(p); if (q->head != FENCEPOST_HEAD) - do_check_inuse_chunk(m, q); + do_check_inuse_chunk(m, q); } } } @@ -2694,8 +3390,8 @@ static int bin_find(mstate m, mchunkptr x) { if (smallmap_is_marked(m, sidx)) { mchunkptr p = b; do { - if (p == x) - return 1; + if (p == x) + return 1; } while ((p = p->fd) != b); } } @@ -2706,15 +3402,15 @@ static int bin_find(mstate m, mchunkptr x) { tchunkptr t = *treebin_at(m, tidx); size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - sizebits <<= 1; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; } if (t != 0) { - tchunkptr u = t; - do { - if (u == (tchunkptr)x) - return 1; - } while ((u = u->fd) != t); + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); } } } @@ -2732,19 +3428,19 @@ static size_t traverse_and_check(mstate m) { mchunkptr lastq = 0; assert(pinuse(q)); while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - sum += chunksize(q); - if (cinuse(q)) { - assert(!bin_find(m, q)); - do_check_inuse_chunk(m, q); - } - else { - assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */ - do_check_free_chunk(m, q); - } - lastq = q; - q = next_chunk(q); + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); } s = s->next; } @@ -2752,6 +3448,7 @@ static size_t traverse_and_check(mstate m) { return sum; } + /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { bindex_t i; @@ -2771,7 +3468,7 @@ static void do_check_malloc_state(mstate m) { if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); - assert(m->topsize == chunksize(m->top)); + /*assert(m->topsize == chunksize(m->top)); redundant */ assert(m->topsize > 0); assert(bin_find(m, m->top) == 0); } @@ -2787,6 +3484,7 @@ static void do_check_malloc_state(mstate m) { #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); if (!PREACTION(m)) { check_malloc_state(m); if (is_initialized(m)) { @@ -2795,18 +3493,18 @@ static struct mallinfo internal_mallinfo(mstate m) { size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - size_t sz = chunksize(q); - sum += sz; - if (!cinuse(q)) { - mfree += sz; - ++nfree; - } - q = next_chunk(q); - } - s = s->next; + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; } nm.arena = sum; @@ -2824,7 +3522,9 @@ static struct mallinfo internal_mallinfo(mstate m) { } #endif /* !NO_MALLINFO */ +#if !NO_MALLOC_STATS static void internal_malloc_stats(mstate m) { + ensure_initialization(); if (!PREACTION(m)) { size_t maxfp = 0; size_t fp = 0; @@ -2837,24 +3537,23 @@ static void internal_malloc_stats(mstate m) { used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - if (!cinuse(q)) - used -= chunksize(q); - q = next_chunk(q); - } - s = s->next; + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; } } - + POSTACTION(m); /* drop lock */ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); - - POSTACTION(m); } } +#endif /* NO_MALLOC_STATS */ /* ----------------------- Operations on smallbins ----------------------- */ @@ -2892,12 +3591,18 @@ static void internal_malloc_stats(mstate m) { assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ - if (F == B)\ - clear_smallmap(M, I);\ - else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ - (B == smallbin_at(M,I) || ok_address(M, B)))) {\ - F->bk = B;\ - B->fd = F;\ + if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(B == smallbin_at(M,I) ||\ + (ok_address(M, B) && B->fd == P))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ @@ -2910,11 +3615,12 @@ static void internal_malloc_stats(mstate m) { assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ - if (B == F)\ + if (B == F) {\ clear_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, F))) {\ - B->fd = F;\ + }\ + else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ F->bk = B;\ + B->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ @@ -2925,9 +3631,9 @@ static void internal_malloc_stats(mstate m) { /* Used only when dvsize known to be small */ #define replace_dv(M, P, S) {\ size_t DVS = M->dvsize;\ + assert(is_small(DVS));\ if (DVS != 0) {\ mchunkptr DV = M->dv;\ - assert(is_small(DVS));\ insert_small_chunk(M, DV, DVS);\ }\ M->dvsize = S;\ @@ -2955,34 +3661,34 @@ static void internal_malloc_stats(mstate m) { size_t K = S << leftshift_for_tree_index(I);\ for (;;) {\ if (chunksize(T) != S) {\ - tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ - K <<= 1;\ - if (*C != 0)\ - T = *C;\ - else if (RTCHECK(ok_address(M, C))) {\ - *C = X;\ - X->parent = T;\ - X->fd = X->bk = X;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ }\ else {\ - tchunkptr F = T->fd;\ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ - T->fd = F->bk = X;\ - X->fd = F;\ - X->bk = T;\ - X->parent = 0;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ }\ }\ }\ @@ -3011,7 +3717,7 @@ static void internal_malloc_stats(mstate m) { if (X->bk != X) {\ tchunkptr F = X->fd;\ R = X->bk;\ - if (RTCHECK(ok_address(M, F))) {\ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ F->bk = R;\ R->fd = F;\ }\ @@ -3022,16 +3728,16 @@ static void internal_malloc_stats(mstate m) { else {\ tchunkptr* RP;\ if (((R = *(RP = &(X->child[1]))) != 0) ||\ - ((R = *(RP = &(X->child[0]))) != 0)) {\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ tchunkptr* CP;\ while ((*(CP = &(R->child[1])) != 0) ||\ - (*(CP = &(R->child[0])) != 0)) {\ - R = *(RP = CP);\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ }\ if (RTCHECK(ok_address(M, RP)))\ - *RP = 0;\ + *RP = 0;\ else {\ - CORRUPTION_ERROR_ACTION(M);\ + CORRUPTION_ERROR_ACTION(M);\ }\ }\ }\ @@ -3039,39 +3745,39 @@ static void internal_malloc_stats(mstate m) { tbinptr* H = treebin_at(M, X->index);\ if (X == *H) {\ if ((*H = R) == 0) \ - clear_treemap(M, X->index);\ + clear_treemap(M, X->index);\ }\ else if (RTCHECK(ok_address(M, XP))) {\ if (XP->child[0] == X) \ - XP->child[0] = R;\ + XP->child[0] = R;\ else \ - XP->child[1] = R;\ + XP->child[1] = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ if (R != 0) {\ if (RTCHECK(ok_address(M, R))) {\ - tchunkptr C0, C1;\ - R->parent = XP;\ - if ((C0 = X->child[0]) != 0) {\ - if (RTCHECK(ok_address(M, C0))) {\ - R->child[0] = C0;\ - C0->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - if ((C1 = X->child[1]) != 0) {\ - if (RTCHECK(ok_address(M, C1))) {\ - R->child[1] = C1;\ - C1->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ }\ else\ - CORRUPTION_ERROR_ACTION(M);\ + CORRUPTION_ERROR_ACTION(M);\ }\ }\ } @@ -3095,7 +3801,7 @@ static void internal_malloc_stats(mstate m) { #else /* ONLY_MSPACES */ #if MSPACES #define internal_malloc(m, b)\ - (m == gm)? dlmalloc(b) : mspace_malloc(m, b) + ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) #define internal_free(m, mem)\ if (m == gm) dlfree(mem); else mspace_free(m,mem); #else /* MSPACES */ @@ -3111,30 +3817,33 @@ static void internal_malloc_stats(mstate m) { the mmapped region stored in the prev_foot field of the chunk. This allows reconstruction of the required argument to MUNMAP when freed, and also allows adjustment of the returned chunk to meet alignment - requirements (especially in memalign). There is also enough space - allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain - the PINUSE bit so frees can be checked. + requirements (especially in memalign). */ /* Malloc using mmap */ static void* mmap_alloc(mstate m, size_t nb) { - size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (m->footprint_limit != 0) { + size_t fp = m->footprint + mmsize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } if (mmsize > nb) { /* Check for wrap around 0 */ - char* mm = (char*)(DIRECT_MMAP(mmsize)); + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); if (mm != CMFAIL) { size_t offset = align_offset(chunk2mem(mm)); size_t psize = mmsize - offset - MMAP_FOOT_PAD; mchunkptr p = (mchunkptr)(mm + offset); - p->prev_foot = offset | IS_MMAPPED_BIT; - (p)->head = (psize|CINUSE_BIT); + p->prev_foot = offset; + p->head = psize; mark_inuse_foot(m, p, psize); chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; - if (mm < m->least_addr) - m->least_addr = mm; + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; if ((m->footprint += mmsize) > m->max_footprint) - m->max_footprint = m->footprint; + m->max_footprint = m->footprint; assert(is_aligned(chunk2mem(p))); check_mmapped_chunk(m, p); return chunk2mem(p); @@ -3144,8 +3853,9 @@ static void* mmap_alloc(mstate m, size_t nb) { } /* Realloc using mmap */ -static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { size_t oldsize = chunksize(oldp); + (void)flags; /* placate people compiling -Wunused */ if (is_small(nb)) /* Can't shrink mmap regions below small size */ return 0; /* Keep old chunk if big enough but not too big */ @@ -3153,24 +3863,23 @@ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { (oldsize - nb) <= (mparams.granularity << 1)) return oldp; else { - size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT; + size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; - size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES + - CHUNK_ALIGN_MASK); + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); char* cp = (char*)CALL_MREMAP((char*)oldp - offset, - oldmmsize, newmmsize, 1); + oldmmsize, newmmsize, flags); if (cp != CMFAIL) { mchunkptr newp = (mchunkptr)(cp + offset); size_t psize = newmmsize - offset - MMAP_FOOT_PAD; - newp->head = (psize|CINUSE_BIT); + newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; if (cp < m->least_addr) - m->least_addr = cp; + m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) - m->max_footprint = m->footprint; + m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); return newp; } @@ -3178,6 +3887,7 @@ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { return 0; } + /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ @@ -3212,7 +3922,7 @@ static void reset_on_error(mstate m) { int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ - m->smallbins = m->treebins = 0; + m->smallmap = m->treemap = 0; m->dvsize = m->topsize = 0; m->seg.base = 0; m->seg.size = 0; @@ -3226,7 +3936,7 @@ static void reset_on_error(mstate m) { /* Allocate chunk and prepend remainder with chunk in successor base. */ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, - size_t nb) { + size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); size_t psize = (char*)oldfirst - (char*)p; @@ -3251,7 +3961,7 @@ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, set_size_and_pinuse_of_free_chunk(q, dsize); } else { - if (!cinuse(oldfirst)) { + if (!is_inuse(oldfirst)) { size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); @@ -3266,7 +3976,6 @@ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, return chunk2mem(p); } - /* Add a segment to hold a new noncontiguous region */ static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Determine locations and sizes of segment, fenceposts, old top */ @@ -3327,16 +4036,26 @@ static void* sys_alloc(mstate m, size_t nb) { char* tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; + size_t asize; /* allocation size */ - init_mparams(); + ensure_initialization(); - /* Directly map large chunks */ - if (use_mmap(m) && nb >= mparams.mmap_threshold) { + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { void* mem = mmap_alloc(m, nb); if (mem != 0) return mem; } + asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize <= nb) + return 0; /* wraparound */ + if (m->footprint_limit != 0) { + size_t fp = m->footprint + asize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + /* Try getting memory in any of three ways (in most-preferred to least-preferred order): @@ -3352,95 +4071,97 @@ static void* sys_alloc(mstate m, size_t nb) { find space. 3. A call to MORECORE that cannot usually contiguously extend memory. (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. */ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { char* br = CMFAIL; + size_t ssize = asize; /* sbrk call size */ msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); - size_t asize = 0; - ACQUIRE_MORECORE_LOCK(); + ACQUIRE_MALLOC_GLOBAL_LOCK(); if (ss == 0) { /* First time through or recovery */ char* base = (char*)CALL_MORECORE(0); if (base != CMFAIL) { - asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); - /* Adjust to end on a page boundary */ - if (!is_page_aligned(base)) - asize += (page_align((size_t)base) - (size_t)base); - /* Can't call MORECORE if size is negative when treated as signed */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == base) { - tbase = base; - tsize = asize; - } + size_t fp; + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + ssize += (page_align((size_t)base) - (size_t)base); + fp = m->footprint + ssize; /* recheck limits */ + if (ssize > nb && ssize < HALF_MAX_SIZE_T && + (m->footprint_limit == 0 || + (fp > m->footprint && fp <= m->footprint_limit)) && + (br = (char*)(CALL_MORECORE(ssize))) == base) { + tbase = base; + tsize = ssize; + } } } else { /* Subtract out existing available top space from MORECORE request. */ - asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE); + ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); /* Use mem here only if it did continuously extend old space */ - if (asize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { - tbase = br; - tsize = asize; + if (ssize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { + tbase = br; + tsize = ssize; } } if (tbase == CMFAIL) { /* Cope with partial failure */ if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (asize < HALF_MAX_SIZE_T && - asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) { - size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize); - if (esize < HALF_MAX_SIZE_T) { - char* end = (char*)CALL_MORECORE(esize); - if (end != CMFAIL) - asize += esize; - else { /* Can't use; try to release */ - CALL_MORECORE(-asize); - br = CMFAIL; - } - } - } + if (ssize < HALF_MAX_SIZE_T && + ssize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + ssize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-ssize); + br = CMFAIL; + } + } + } } if (br != CMFAIL) { /* Use the space we did get */ - tbase = br; - tsize = asize; + tbase = br; + tsize = ssize; } else - disable_contiguous(m); /* Don't try contiguous path in the future */ + disable_contiguous(m); /* Don't try contiguous path in the future */ } - RELEASE_MORECORE_LOCK(); + RELEASE_MALLOC_GLOBAL_LOCK(); } if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; - size_t rsize = granularity_align(req); - if (rsize > nb) { /* Fail if wraps around zero */ - char* mp = (char*)(CALL_MMAP(rsize)); - if (mp != CMFAIL) { - tbase = mp; - tsize = rsize; - mmap_flag = IS_MMAPPED_BIT; - } + char* mp = (char*)(CALL_MMAP(asize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = asize; + mmap_flag = USE_MMAP_BIT; } } if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ - size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); if (asize < HALF_MAX_SIZE_T) { char* br = CMFAIL; char* end = CMFAIL; - ACQUIRE_MORECORE_LOCK(); + ACQUIRE_MALLOC_GLOBAL_LOCK(); br = (char*)(CALL_MORECORE(asize)); end = (char*)(CALL_MORECORE(0)); - RELEASE_MORECORE_LOCK(); + RELEASE_MALLOC_GLOBAL_LOCK(); if (br != CMFAIL && end != CMFAIL && br < end) { - size_t ssize = end - br; - if (ssize > nb + TOP_FOOT_SIZE) { - tbase = br; - tsize = ssize; - } + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } } } } @@ -3451,48 +4172,55 @@ static void* sys_alloc(mstate m, size_t nb) { m->max_footprint = m->footprint; if (!is_initialized(m)) { /* first-time initialization */ - m->seg.base = m->least_addr = tbase; + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmap_flag; m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; init_bins(m); +#if !ONLY_MSPACES if (is_global(m)) - init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); - else { - /* Offset top by embedded malloc_state */ - mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); } } else { /* Try to merge with an existing segment */ msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ while (sp != 0 && tbase != sp->base + sp->size) - sp = sp->next; + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & IS_MMAPPED_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ - sp->size += tsize; - init_top(m, m->top, m->topsize + tsize); + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); } else { - if (tbase < m->least_addr) - m->least_addr = tbase; - sp = &m->seg; - while (sp != 0 && sp->base != tbase + tsize) - sp = sp->next; - if (sp != 0 && - !is_extern_segment(sp) && - (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) { - char* oldbase = sp->base; - sp->base = tbase; - sp->size += tsize; - return prepend_alloc(m, tbase, oldbase, nb); - } - else - add_segment(m, tbase, tsize, mmap_flag); + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); } } @@ -3517,46 +4245,54 @@ static void* sys_alloc(mstate m, size_t nb) { /* Unmap and unlink any mmapped segments that don't contain used chunks */ static size_t release_unused_segments(mstate m) { size_t released = 0; + int nsegs = 0; msegmentptr pred = &m->seg; msegmentptr sp = pred->next; while (sp != 0) { char* base = sp->base; size_t size = sp->size; msegmentptr next = sp->next; + ++nsegs; if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { mchunkptr p = align_as_chunk(base); size_t psize = chunksize(p); /* Can unmap if first chunk holds entire segment and not pinned */ - if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { - tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char*)sp)); - if (p == m->dv) { - m->dv = 0; - m->dvsize = 0; - } - else { - unlink_large_chunk(m, tp); - } - if (CALL_MUNMAP(base, size) == 0) { - released += size; - m->footprint -= size; - /* unlink obsoleted record */ - sp = pred; - sp->next = next; - } - else { /* back out if cannot unmap */ - insert_large_chunk(m, tp, psize); - } + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } } } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; pred = sp; sp = next; } + /* Reset check counter */ + m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? + (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); return released; } static int sys_trim(mstate m, size_t pad) { size_t released = 0; + ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ @@ -3564,45 +4300,46 @@ static int sys_trim(mstate m, size_t pad) { /* Shrink top space in granularity-size units, keeping at least one */ size_t unit = mparams.granularity; size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - - SIZE_T_ONE) * unit; + SIZE_T_ONE) * unit; msegmentptr sp = segment_holding(m, (char*)m->top); if (!is_extern_segment(sp)) { - if (is_mmapped_segment(sp)) { - if (HAVE_MMAP && - sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ - size_t newsize = sp->size - extra; - /* Prefer mremap, fall back to munmap */ - if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || - (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { - released = extra; - } - } - } - else if (HAVE_MORECORE) { - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ - extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; - ACQUIRE_MORECORE_LOCK(); - { - /* Make sure end of memory is where we last set it. */ - char* old_br = (char*)(CALL_MORECORE(0)); - if (old_br == sp->base + sp->size) { - char* rel_br = (char*)(CALL_MORECORE(-extra)); - char* new_br = (char*)(CALL_MORECORE(0)); - if (rel_br != CMFAIL && new_br < old_br) - released = old_br - new_br; - } - } - RELEASE_MORECORE_LOCK(); - } + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + (void)newsize; /* placate people compiling -Wunused-variable */ + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } } if (released != 0) { - sp->size -= released; - m->footprint -= released; - init_top(m, m->top, m->topsize - released); - check_top_chunk(m, m->top); + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); } } @@ -3611,14 +4348,85 @@ static int sys_trim(mstate m, size_t pad) { released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0) + if (released == 0 && m->topsize > m->trim_check) m->trim_check = MAX_SIZE_T; } return (released != 0)? 1 : 0; } -/* ---------------------------- malloc support --------------------------- */ +/* Consolidate and bin a chunk. Differs from exported versions + of free mainly in that the chunk need not be marked as inuse. +*/ +static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + mchunkptr prev; + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + m->footprint -= psize; + return; + } + prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (p != m->dv) { + unlink_chunk(m, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + m->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return; + } + } + else { + CORRUPTION_ERROR_ACTION(m); + return; + } + } + if (RTCHECK(ok_address(m, next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == m->top) { + size_t tsize = m->topsize += psize; + m->top = p; + p->head = tsize | PINUSE_BIT; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + return; + } + else if (next == m->dv) { + size_t dsize = m->dvsize += psize; + m->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(m, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == m->dv) { + m->dvsize = psize; + return; + } + } + } + else { + set_free_with_pinuse(p, psize, next); + } + insert_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + } +} + +/* ---------------------------- malloc --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ static void* tmalloc_large(mstate m, size_t nb) { @@ -3627,7 +4435,6 @@ static void* tmalloc_large(mstate m, size_t nb) { tchunkptr t; bindex_t idx; compute_tree_index(nb, idx); - if ((t = *treebin_at(m, idx)) != 0) { /* Traverse tree for this bin looking for node with size == nb */ size_t sizebits = nb << leftshift_for_tree_index(idx); @@ -3636,22 +4443,21 @@ static void* tmalloc_large(mstate m, size_t nb) { tchunkptr rt; size_t trem = chunksize(t) - nb; if (trem < rsize) { - v = t; - if ((rsize = trem) == 0) - break; + v = t; + if ((rsize = trem) == 0) + break; } rt = t->child[1]; t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; if (rt != 0 && rt != t) - rst = rt; + rst = rt; if (t == 0) { - t = rst; /* set t to least subtree holding sizes > nb */ - break; + t = rst; /* set t to least subtree holding sizes > nb */ + break; } sizebits <<= 1; } } - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { @@ -3677,15 +4483,15 @@ static void* tmalloc_large(mstate m, size_t nb) { mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); - if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); - else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - insert_chunk(m, r, rsize); - } - return chunk2mem(v); + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); @@ -3700,7 +4506,6 @@ static void* tmalloc_small(mstate m, size_t nb) { bindex_t i; binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); - v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; @@ -3718,11 +4523,11 @@ static void* tmalloc_small(mstate m, size_t nb) { if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(m, v, (rsize + nb)); + set_inuse_and_pinuse(m, v, (rsize + nb)); else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(m, r, rsize); + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); } return chunk2mem(v); } @@ -3732,82 +4537,356 @@ static void* tmalloc_small(mstate m, size_t nb) { return 0; } -/* --------------------------- realloc support --------------------------- */ +#if !ONLY_MSPACES -static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - return 0; - } - if (!PREACTION(m)) { - mchunkptr oldp = mem2chunk(oldmem); - size_t oldsize = chunksize(oldp); - mchunkptr next = chunk_plus_offset(oldp, oldsize); - mchunkptr newp = 0; - void* extra = 0; +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it - /* Try to either shrink or extend into top. Else malloc-copy-free */ + The ugly goto's here ensure that postaction occurs along all paths. + */ - if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && - ok_next(oldp, next) && ok_pinuse(next))) { - size_t nb = request2size(bytes); - if (is_mmapped(oldp)) - newp = mmap_resize(m, oldp, nb); - else if (oldsize >= nb) { /* already big enough */ - size_t rsize = oldsize - nb; - newp = oldp; - if (rsize >= MIN_CHUNK_SIZE) { - mchunkptr remainder = chunk_plus_offset(newp, nb); - set_inuse(m, newp, nb); - set_inuse(m, remainder, rsize); - extra = chunk2mem(remainder); - } +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; } - else if (next == m->top && oldsize + m->topsize > nb) { - /* Expand into top */ - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; - mchunkptr newtop = chunk_plus_offset(oldp, nb); - set_inuse(m, oldp, nb); - newtop->head = newtopsize |PINUSE_BIT; - m->top = newtop; - m->topsize = newtopsize; - newp = oldp; + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } } } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { - USAGE_ERROR_ACTION(m, oldmem); - POSTACTION(m); - return 0; + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } } - POSTACTION(m); + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } - if (newp != 0) { - if (extra != 0) { - internal_free(m, extra); - } - check_inuse_chunk(m, newp); - return chunk2mem(newp); - } - else { - void* newmem = internal_malloc(m, bytes); - if (newmem != 0) { - size_t oc = oldsize - overhead_for(oldp); - memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); - internal_free(m, oldmem); - } - return newmem; + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; } + return 0; } -/* --------------------------- memalign support -------------------------- */ +/* ---------------------------- free --------------------------- */ + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +#endif /* !ONLY_MSPACES */ + +/* ------------ Internal support for realloc, memalign, etc -------------- */ + +/* Try to realloc; only in-place unless can_move true */ +static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, + int can_move) { + mchunkptr newp = 0; + size_t oldsize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, oldsize); + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) { + if (is_mmapped(p)) { + newp = mmap_resize(m, p, nb, can_move); + } + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + else if (next == m->top) { /* extend into top */ + if (oldsize + m->topsize > nb) { + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = p; + } + } + else if (next == m->dv) { /* extend into dv */ + size_t dvs = m->dvsize; + if (oldsize + dvs >= nb) { + size_t dsize = oldsize + dvs - nb; + if (dsize >= MIN_CHUNK_SIZE) { + mchunkptr r = chunk_plus_offset(p, nb); + mchunkptr n = chunk_plus_offset(r, dsize); + set_inuse(m, p, nb); + set_size_and_pinuse_of_free_chunk(r, dsize); + clear_pinuse(n); + m->dvsize = dsize; + m->dv = r; + } + else { /* exhaust dv */ + size_t newsize = oldsize + dvs; + set_inuse(m, p, newsize); + m->dvsize = 0; + m->dv = 0; + } + newp = p; + } + } + else if (!cinuse(next)) { /* extend into next free chunk */ + size_t nextsize = chunksize(next); + if (oldsize + nextsize >= nb) { + size_t rsize = oldsize + nextsize - nb; + unlink_chunk(m, next, nextsize); + if (rsize < MIN_CHUNK_SIZE) { + size_t newsize = oldsize + nextsize; + set_inuse(m, p, newsize); + } + else { + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + } + } + else { + USAGE_ERROR_ACTION(m, chunk2mem(p)); + } + return newp; +} static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ - return internal_malloc(m, bytes); + void* mem = 0; if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ @@ -3815,7 +4894,6 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { while (a < alignment) a <<= 1; alignment = a; } - if (bytes >= MAX_REQUEST - alignment) { if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; @@ -3824,87 +4902,75 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { else { size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; - char* mem = (char*)internal_malloc(m, req); + mem = internal_malloc(m, req); if (mem != 0) { - void* leader = 0; - void* trailer = 0; mchunkptr p = mem2chunk(mem); + if (PREACTION(m)) + return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; - if (PREACTION(m)) return 0; - if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ - /* - Find an aligned spot inside chunk. Since we need to give - back leading space in a chunk of at least MIN_CHUNK_SIZE, if - the first calculation places us at a spot with less than - MIN_CHUNK_SIZE leader, we can move to the next aligned spot. - We've allocated enough total room so that this is always - possible. - */ - char* br = (char*)mem2chunk((size_t)(((size_t)(mem + - alignment - - SIZE_T_ONE)) & - -alignment)); - char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? - br : br+alignment; - mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char*)(p); - size_t newsize = chunksize(p) - leadsize; - - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ - newp->prev_foot = p->prev_foot + leadsize; - newp->head = (newsize|CINUSE_BIT); - } - else { /* Otherwise, give back leader, use the rest */ - set_inuse(m, newp, newsize); - set_inuse(m, p, leadsize); - leader = chunk2mem(p); - } - p = newp; + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + dispose_chunk(m, p, leadsize); + } + p = newp; } /* Give back spare room at the end */ if (!is_mmapped(p)) { - size_t size = chunksize(p); - if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; - mchunkptr remainder = chunk_plus_offset(p, nb); - set_inuse(m, p, nb); - set_inuse(m, remainder, remainder_size); - trailer = chunk2mem(remainder); - } + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + dispose_chunk(m, remainder, remainder_size); + } } + mem = chunk2mem(p); assert (chunksize(p) >= nb); - assert((((size_t)(chunk2mem(p))) % alignment) == 0); + assert(((size_t)mem & (alignment - 1)) == 0); check_inuse_chunk(m, p); POSTACTION(m); - if (leader != 0) { - internal_free(m, leader); - } - if (trailer != 0) { - internal_free(m, trailer); - } - return chunk2mem(p); } } - return 0; + return mem; } -/* ------------------------ comalloc/coalloc support --------------------- */ - -static void** ialloc(mstate m, - size_t n_elements, - size_t* sizes, - int opts, - void* chunks[]) { - /* - This provides common support for independent_X routines, handling +/* + Common support for independent_X routines, handling all of the combinations that can result. - - The opts arg has: + The opts arg has: bit 0 set if all elements are same size (using sizes[0]) bit 1 set if elements should be zeroed - */ +*/ +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { size_t element_size; /* chunksize of each element, if all same */ size_t contents_size; /* total size of elements */ @@ -3918,6 +4984,7 @@ static void** ialloc(mstate m, size_t size; size_t i; + ensure_initialization(); /* compute array length, if needed */ if (chunks != 0) { if (n_elements == 0) @@ -3985,9 +5052,9 @@ static void** ialloc(mstate m, marray[i] = chunk2mem(p); if (i != n_elements-1) { if (element_size != 0) - size = element_size; + size = element_size; else - size = request2size(sizes[i]); + size = request2size(sizes[i]); remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); @@ -4018,312 +5085,253 @@ static void** ialloc(mstate m, return marray; } +/* Try to free all pointers in the given array. + Note: this could be made faster, by delaying consolidation, + at the price of disabling some user integrity checks, We + still optimize some consolidations by combining adjacent + chunks before freeing, which will occur often if allocated + with ialloc or the array is sorted. +*/ +static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { + size_t unfreed = 0; + if (!PREACTION(m)) { + void** a; + void** fence = &(array[nelem]); + for (a = array; a != fence; ++a) { + void* mem = *a; + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t psize = chunksize(p); +#if FOOTERS + if (get_mstate_for(p) != m) { + ++unfreed; + continue; + } +#endif + check_inuse_chunk(m, p); + *a = 0; + if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { + void ** b = a + 1; /* try to merge with next chunk */ + mchunkptr next = next_chunk(p); + if (b != fence && *b == chunk2mem(next)) { + size_t newsize = chunksize(next) + psize; + set_inuse(m, p, newsize); + *b = chunk2mem(p); + } + else + dispose_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + break; + } + } + } + if (should_trim(m, m->topsize)) + sys_trim(m, 0); + POSTACTION(m); + } + return unfreed; +} -/* -------------------------- public routines ---------------------------- */ +/* Traversal */ +#if MALLOC_INSPECT_ALL +static void internal_inspect_all(mstate m, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + if (is_initialized(m)) { + mchunkptr top = m->top; + msegmentptr s; + for (s = &m->seg; s != 0; s = s->next) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { + mchunkptr next = next_chunk(q); + size_t sz = chunksize(q); + size_t used; + void* start; + if (is_inuse(q)) { + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + start = chunk2mem(q); + } + else { + used = 0; + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void*)((char*)q + sizeof(struct malloc_chunk)); + } + else { + start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + } + } + if (start < (void*)next) /* skip if all space is bookkeeping */ + handler(start, next, used, arg); + if (q == top) + break; + q = next; + } + } + } +} +#endif /* MALLOC_INSPECT_ALL */ + +/* ------------------ Exported realloc, memalign, etc -------------------- */ #if !ONLY_MSPACES -void* dlmalloc(size_t bytes) { - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the dv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in dv. - 4. If it is big enough, use the top chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than dv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the dv chunk. - 3. If it is big enough, use the top chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it - - The ugly goto's here ensure that postaction occurs along all paths. - */ - - if (!PREACTION(gm)) { - void* mem; - size_t nb; - if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; - binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); - idx = small_index(nb); - smallbits = gm->smallmap >> idx; - - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(gm, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(gm, b, p, idx); - set_inuse_and_pinuse(gm, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb > gm->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(gm, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(gm, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(gm, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(gm, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - } - else if (bytes >= MAX_REQUEST) - nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ - else { - nb = pad_request(bytes); - if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - } - - if (nb <= gm->dvsize) { - size_t rsize = gm->dvsize - nb; - mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = gm->dv = chunk_plus_offset(p, nb); - gm->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - } - else { /* exhaust dv */ - size_t dvs = gm->dvsize; - gm->dvsize = 0; - gm->dv = 0; - set_inuse_and_pinuse(gm, p, dvs); - } - mem = chunk2mem(p); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; - mchunkptr p = gm->top; - mchunkptr r = gm->top = chunk_plus_offset(p, nb); - r->head = rsize | PINUSE_BIT; - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - mem = chunk2mem(p); - check_top_chunk(gm, gm->top); - check_malloced_chunk(gm, mem, nb); - goto postaction; - } - - mem = sys_alloc(gm, nb); - - postaction: - POSTACTION(gm); - return mem; - } - - return 0; -} - -void dlfree(void* mem) { - /* - Consolidate freed chunks with preceeding or succeeding bordering - free chunks, if they exist, and then place in a bin. Intermixed - with special cases for top, dv, mmapped chunks, and usage errors. - */ - - if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS - mstate fm = get_mstate_for(p); - if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); - return; - } -#else /* FOOTERS */ -#define fm gm -#endif /* FOOTERS */ - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if ((prevsize & IS_MMAPPED_BIT) != 0) { - prevsize &= ~IS_MMAPPED_BIT; - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } - - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - insert_chunk(fm, p, psize); - check_free_chunk(fm, p); - goto postaction; - } - } - erroraction: - USAGE_ERROR_ACTION(fm, p); - postaction: - POSTACTION(fm); - } - } -#if !FOOTERS -#undef fm -#endif /* FOOTERS */ -} - -void* dlcalloc(size_t n_elements, size_t elem_size) { - void* mem; - size_t req = 0; - if (n_elements != 0) { - req = n_elements * elem_size; - if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - } - mem = dlmalloc(req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) - memset(mem, 0, req); - return mem; -} - void* dlrealloc(void* oldmem, size_t bytes) { - if (oldmem == 0) - return dlmalloc(bytes); + void* mem = 0; + if (oldmem == 0) { + mem = dlmalloc(bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } #ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { + else if (bytes == 0) { dlfree(oldmem); - return 0; } #endif /* REALLOC_ZERO_BYTES_FREES */ else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); #if ! FOOTERS mstate m = gm; #else /* FOOTERS */ - mstate m = get_mstate_for(mem2chunk(oldmem)); + mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ - return internal_realloc(m, oldmem, bytes); + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = internal_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + } + } } + return mem; +} + +void* dlrealloc_in_place(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; } void* dlmemalign(size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc(bytes); + } return internal_memalign(gm, alignment, bytes); } -void** dlindependent_calloc(size_t n_elements, size_t elem_size, - void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ - return ialloc(gm, n_elements, &sz, 3, chunks); -} - -void** dlindependent_comalloc(size_t n_elements, size_t sizes[], - void* chunks[]) { - return ialloc(gm, n_elements, sizes, 0, chunks); +int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment == MALLOC_ALIGNMENT) + mem = dlmalloc(bytes); + else { + size_t d = alignment / sizeof(void*); + size_t r = alignment % sizeof(void*); + if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) + return EINVAL; + else if (bytes <= MAX_REQUEST - alignment) { + if (alignment < MIN_CHUNK_SIZE) + alignment = MIN_CHUNK_SIZE; + mem = internal_memalign(gm, alignment, bytes); + } + } + if (mem == 0) + return ENOMEM; + else { + *pp = mem; + return 0; + } } void* dlvalloc(size_t bytes) { size_t pagesz; - init_mparams(); + ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, bytes); } void* dlpvalloc(size_t bytes) { size_t pagesz; - init_mparams(); + ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); } +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +size_t dlbulk_free(void* array[], size_t nelem) { + return internal_bulk_free(gm, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + ensure_initialization(); + if (!PREACTION(gm)) { + internal_inspect_all(gm, handler, arg); + POSTACTION(gm); + } +} +#endif /* MALLOC_INSPECT_ALL */ + int dlmalloc_trim(size_t pad) { int result = 0; + ensure_initialization(); if (!PREACTION(gm)) { result = sys_trim(gm, pad); POSTACTION(gm); @@ -4339,29 +5347,47 @@ size_t dlmalloc_max_footprint(void) { return gm->max_footprint; } +size_t dlmalloc_footprint_limit(void) { + size_t maf = gm->footprint_limit; + return maf == 0 ? MAX_SIZE_T : maf; +} + +size_t dlmalloc_set_footprint_limit(size_t bytes) { + size_t result; /* invert sense of 0 */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + return gm->footprint_limit = result; +} + #if !NO_MALLINFO struct mallinfo dlmallinfo(void) { return internal_mallinfo(gm); } #endif /* NO_MALLINFO */ +#if !NO_MALLOC_STATS void dlmalloc_stats() { internal_malloc_stats(gm); } +#endif /* NO_MALLOC_STATS */ + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} size_t dlmalloc_usable_size(void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); - if (cinuse(p)) + if (is_inuse(p)) return chunksize(p) - overhead_for(p); } return 0; } -int dlmallopt(int param_number, int value) { - return change_mparam(param_number, value); -} - #endif /* !ONLY_MSPACES */ /* ----------------------------- user mspaces ---------------------------- */ @@ -4374,12 +5400,15 @@ static mstate init_user_mstate(char* tbase, size_t tsize) { mchunkptr msp = align_as_chunk(tbase); mstate m = (mstate)(chunk2mem(msp)); memset(m, 0, msize); - INITIAL_LOCK(&m->mutex); - msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + (void)INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; disable_contiguous(m); init_bins(m); mn = next_chunk(mem2chunk(m)); @@ -4390,17 +5419,17 @@ static mstate init_user_mstate(char* tbase, size_t tsize) { mspace create_mspace(size_t capacity, int locked) { mstate m = 0; - size_t msize = pad_request(sizeof(struct malloc_state)); - init_mparams(); /* Ensure pagesize etc initialized */ - + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { size_t rs = ((capacity == 0)? mparams.granularity : - (capacity + TOP_FOOT_SIZE + msize)); + (capacity + TOP_FOOT_SIZE + msize)); size_t tsize = granularity_align(rs); char* tbase = (char*)(CALL_MMAP(tsize)); if (tbase != CMFAIL) { m = init_user_mstate(tbase, tsize); - m->seg.sflags = IS_MMAPPED_BIT; + m->seg.sflags = USE_MMAP_BIT; set_lock(m, locked); } } @@ -4409,9 +5438,9 @@ mspace create_mspace(size_t capacity, int locked) { mspace create_mspace_with_base(void* base, size_t capacity, int locked) { mstate m = 0; - size_t msize = pad_request(sizeof(struct malloc_state)); - init_mparams(); /* Ensure pagesize etc initialized */ - + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); if (capacity > msize + TOP_FOOT_SIZE && capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { m = init_user_mstate((char*)base, capacity); @@ -4421,19 +5450,38 @@ mspace create_mspace_with_base(void* base, size_t capacity, int locked) { return (mspace)m; } +int mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) { + ret = 1; + } + if (!enable) { + enable_mmap(ms); + } else { + disable_mmap(ms); + } + POSTACTION(ms); + } + return ret; +} + size_t destroy_mspace(mspace msp) { size_t freed = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { msegmentptr sp = &ms->seg; + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ while (sp != 0) { char* base = sp->base; size_t size = sp->size; flag_t flag = sp->sflags; + (void)base; /* placate people compiling -Wunused-variable */ sp = sp->next; - if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) && - CALL_MUNMAP(base, size) == 0) - freed += size; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; } } else { @@ -4447,7 +5495,6 @@ size_t destroy_mspace(mspace msp) { versions. This is not so nice but better than the alternatives. */ - void* mspace_malloc(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { @@ -4465,49 +5512,49 @@ void* mspace_malloc(mspace msp, size_t bytes) { smallbits = ms->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ - mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ - b = smallbin_at(ms, idx); - p = b->fd; - assert(chunksize(p) == small_index2size(idx)); - unlink_first_small_chunk(ms, b, p, idx); - set_inuse_and_pinuse(ms, p, small_index2size(idx)); - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; } else if (nb > ms->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ - mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); - compute_bit2idx(leastbit, i); - b = smallbin_at(ms, i); - p = b->fd; - assert(chunksize(p) == small_index2size(i)); - unlink_first_small_chunk(ms, b, p, i); - rsize = small_index2size(i) - nb; - /* Fit here cannot be remainderless if 4byte sizes */ - if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) - set_inuse_and_pinuse(ms, p, small_index2size(i)); - else { - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - r = chunk_plus_offset(p, nb); - set_size_and_pinuse_of_free_chunk(r, rsize); - replace_dv(ms, r, rsize); - } - mem = chunk2mem(p); - check_malloced_chunk(ms, mem, nb); - goto postaction; - } + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } - else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; - } + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } } } else if (bytes >= MAX_REQUEST) @@ -4515,8 +5562,8 @@ void* mspace_malloc(mspace msp, size_t bytes) { else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); - goto postaction; + check_malloced_chunk(ms, mem, nb); + goto postaction; } } @@ -4524,16 +5571,16 @@ void* mspace_malloc(mspace msp, size_t bytes) { size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ - mchunkptr r = ms->dv = chunk_plus_offset(p, nb); - ms->dvsize = rsize; - set_size_and_pinuse_of_free_chunk(r, rsize); - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); } else { /* exhaust dv */ - size_t dvs = ms->dvsize; - ms->dvsize = 0; - ms->dv = 0; - set_inuse_and_pinuse(ms, p, dvs); + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); @@ -4567,6 +5614,7 @@ void mspace_free(mspace msp, void* mem) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); + (void)msp; /* placate people compiling -Wunused */ #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ @@ -4576,74 +5624,83 @@ void mspace_free(mspace msp, void* mem) { } if (!PREACTION(fm)) { check_inuse_chunk(fm, p); - if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { - size_t psize = chunksize(p); - mchunkptr next = chunk_plus_offset(p, psize); - if (!pinuse(p)) { - size_t prevsize = p->prev_foot; - if ((prevsize & IS_MMAPPED_BIT) != 0) { - prevsize &= ~IS_MMAPPED_BIT; - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - fm->footprint -= psize; - goto postaction; - } - else { - mchunkptr prev = chunk_minus_offset(p, prevsize); - psize += prevsize; - p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ - if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { - fm->dvsize = psize; - set_free_with_pinuse(p, psize, next); - goto postaction; - } - } - else - goto erroraction; - } - } + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } - if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ - if (next == fm->top) { - size_t tsize = fm->topsize += psize; - fm->top = p; - p->head = tsize | PINUSE_BIT; - if (p == fm->dv) { - fm->dv = 0; - fm->dvsize = 0; - } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); - goto postaction; - } - else if (next == fm->dv) { - size_t dsize = fm->dvsize += psize; - fm->dv = p; - set_size_and_pinuse_of_free_chunk(p, dsize); - goto postaction; - } - else { - size_t nsize = chunksize(next); - psize += nsize; - unlink_chunk(fm, next, nsize); - set_size_and_pinuse_of_free_chunk(p, psize); - if (p == fm->dv) { - fm->dvsize = psize; - goto postaction; - } - } - } - else - set_free_with_pinuse(p, psize, next); - insert_chunk(fm, p, psize); - check_free_chunk(fm, p); - goto postaction; - } + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } } erroraction: USAGE_ERROR_ACTION(fm, p); @@ -4664,7 +5721,7 @@ void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && - (req / n_elements != elem_size)) + (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = internal_malloc(ms, req); @@ -4674,27 +5731,80 @@ void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { } void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { - if (oldmem == 0) - return mspace_malloc(msp, bytes); + void* mem = 0; + if (oldmem == 0) { + mem = mspace_malloc(msp, bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } #ifdef REALLOC_ZERO_BYTES_FREES - if (bytes == 0) { + else if (bytes == 0) { mspace_free(msp, oldmem); - return 0; } #endif /* REALLOC_ZERO_BYTES_FREES */ else { -#if FOOTERS - mchunkptr p = mem2chunk(oldmem); - mstate ms = get_mstate_for(p); + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; #else /* FOOTERS */ - mstate ms = (mstate)msp; -#endif /* FOOTERS */ - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); return 0; } - return internal_realloc(ms, oldmem, bytes); +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = mspace_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + mspace_free(m, oldmem); + } + } + } } + return mem; +} + +void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + (void)msp; /* placate people compiling -Wunused */ + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; } void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { @@ -4703,11 +5813,13 @@ void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { USAGE_ERROR_ACTION(ms,ms); return 0; } + if (alignment <= MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); return internal_memalign(ms, alignment, bytes); } void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]) { + size_t elem_size, void* chunks[]) { size_t sz = elem_size; /* serves as 1-element array */ mstate ms = (mstate)msp; if (!ok_magic(ms)) { @@ -4718,7 +5830,7 @@ void** mspace_independent_calloc(mspace msp, size_t n_elements, } void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]) { + size_t sizes[], void* chunks[]) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); @@ -4727,6 +5839,30 @@ void** mspace_independent_comalloc(mspace msp, size_t n_elements, return ialloc(ms, n_elements, sizes, 0, chunks); } +size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { + return internal_bulk_free((mstate)msp, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void mspace_inspect_all(mspace msp, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + internal_inspect_all(ms, handler, arg); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* MALLOC_INSPECT_ALL */ + int mspace_trim(mspace msp, size_t pad) { int result = 0; mstate ms = (mstate)msp; @@ -4742,6 +5878,7 @@ int mspace_trim(mspace msp, size_t pad) { return result; } +#if !NO_MALLOC_STATS void mspace_malloc_stats(mspace msp) { mstate ms = (mstate)msp; if (ok_magic(ms)) { @@ -4751,28 +5888,62 @@ void mspace_malloc_stats(mspace msp) { USAGE_ERROR_ACTION(ms,ms); } } +#endif /* NO_MALLOC_STATS */ size_t mspace_footprint(mspace msp) { - size_t result; + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->footprint; } - USAGE_ERROR_ACTION(ms,ms); + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; } - size_t mspace_max_footprint(mspace msp) { - size_t result; + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->max_footprint; } - USAGE_ERROR_ACTION(ms,ms); + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; } +size_t mspace_footprint_limit(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + size_t maf = ms->footprint_limit; + result = (maf == 0) ? MAX_SIZE_T : maf; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + ms->footprint_limit = result; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} #if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { @@ -4784,12 +5955,22 @@ struct mallinfo mspace_mallinfo(mspace msp) { } #endif /* NO_MALLINFO */ +size_t mspace_usable_size(const void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + int mspace_mallopt(int param_number, int value) { return change_mparam(param_number, value); } #endif /* MSPACES */ + /* -------------------- Alternative MORECORE functions ------------------- */ /* @@ -4839,12 +6020,12 @@ int mspace_mallopt(int param_number, int value) { if (size > 0) { if (size < MINIMUM_MORECORE_SIZE) - size = MINIMUM_MORECORE_SIZE; + size = MINIMUM_MORECORE_SIZE; if (CurrentExecutionLevel() == kTaskLevel) - ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); if (ptr == 0) { - return (void *) MFAIL; + return (void *) MFAIL; } // save ptrs so they can be freed during cleanup our_os_pools[next_os_pool] = ptr; @@ -4874,8 +6055,8 @@ int mspace_mallopt(int param_number, int value) { for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) if (*ptr) { - PoolDeallocate(*ptr); - *ptr = 0; + PoolDeallocate(*ptr); + *ptr = 0; } } @@ -4884,12 +6065,50 @@ int mspace_mallopt(int param_number, int value) { /* ----------------------------------------------------------------------- History: + v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + * fix bad comparison in dlposix_memalign + * don't reuse adjusted asize in sys_alloc + * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion + * reduce compiler warnings -- thanks to all who reported/suggested these + + v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) + * Always perform unlink checks unless INSECURE + * Add posix_memalign. + * Improve realloc to expand in more cases; expose realloc_in_place. + Thanks to Peter Buhr for the suggestion. + * Add footprint_limit, inspect_all, bulk_free. Thanks + to Barry Hayes and others for the suggestions. + * Internal refactorings to avoid calls while holding locks + * Use non-reentrant locks by default. Thanks to Roland McGrath + for the suggestion. + * Small fixes to mspace_destroy, reset_on_error. + * Various configuration extensions/changes. Thanks + to all who contributed these. + + V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) + * Update Creative Commons URL + + V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + * Use zeros instead of prev foot for is_mmapped + * Add mspace_track_large_chunks; thanks to Jean Brouwers + * Fix set_inuse in internal_realloc; thanks to Jean Brouwers + * Fix insufficient sys_alloc padding when using 16byte alignment + * Fix bad error check in mspace_footprint + * Adaptations for ptmalloc; thanks to Wolfram Gloger. + * Reentrant spin locks; thanks to Earl Chew and others + * Win32 improvements; thanks to Niall Douglas and Earl Chew + * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options + * Extension hook in malloc_state + * Various small adjustments to reduce warnings on some compilers + * Various configuration extensions/changes for more platforms. Thanks + to all who contributed these. + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) * Add max_footprint functions * Ensure all appropriate literals are size_t * Fix conditional compilation problem for some #define settings * Avoid concatenating segments with the one provided - in create_mspace_with_base + in create_mspace_with_base * Rename some variables to avoid compiler shadowing warnings * Use explicit lock initialization. * Better handling of sbrk interference. @@ -4908,13 +6127,13 @@ History: * Use trees for large bins * Support mspaces * Use segments to unify sbrk-based and mmap-based system allocation, - removing need for emulation on most platforms without sbrk. + removing need for emulation on most platforms without sbrk. * Default safety checks * Optional footer checks. Thanks to William Robertson for the idea. * Internal code refactoring * Incorporate suggestions and platform-specific changes. - Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, - Aaron Bachmann, Emery Berger, and others. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. * Speed up non-fastbin processing enough to remove fastbins. * Remove useless cfree() to avoid conflicts with other apps. * Remove internal memcpy, memset. Compilers handle builtins better. @@ -4927,7 +6146,7 @@ History: * Allow tuning of FIRST_SORTED_BIN_SIZE * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. * Better detection and support for non-contiguousness of MORECORE. - Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger * Bypass most of malloc if no frees. Thanks To Emery Berger. * Fix freeing of old top non-contiguous chunk im sysmalloc. * Raised default trim and map thresholds to 256K. @@ -4938,18 +6157,18 @@ History: V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) * Introduce independent_comalloc and independent_calloc. - Thanks to Michael Pachos for motivation and help. + Thanks to Michael Pachos for motivation and help. * Make optional .h file available * Allow > 2GB requests on 32bit systems. * new WIN32 sbrk, mmap, munmap, lock code from . - Thanks also to Andreas Mueller , - and Anonymous. + Thanks also to Andreas Mueller , + and Anonymous. * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for - helping test this.) + helping test this.) * memalign: check alignment arg * realloc: don't try to shift chunks backwards, since this - leads to more fragmentation in some programs and doesn't - seem to help in any others. + leads to more fragmentation in some programs and doesn't + seem to help in any others. * Collect all cases in malloc requiring system memory into sysmalloc * Use mmap as backup to sbrk * Place all internal state in malloc_state @@ -4957,23 +6176,23 @@ History: * Many minor tunings and cosmetic improvements * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS - Thanks to Tony E. Bennett and others. + Thanks to Tony E. Bennett and others. * Include errno.h to support default failure action. V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) * return null for negative arguments * Added Several WIN32 cleanups from Martin C. Fong - * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' - (e.g. WIN32 platforms) - * Cleanup header file inclusion for WIN32 platforms - * Cleanup code to avoid Microsoft Visual C++ compiler complaints - * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing - memory allocation routines - * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) - * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to - usage of 'assert' in non-WIN32 code - * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to - avoid infinite loop + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop * Always call 'fREe()' rather than 'free()' V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) @@ -4985,13 +6204,13 @@ History: * Added anonymously donated WIN32 sbrk emulation * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen * malloc_extend_top: fix mask error that caused wastage after - foreign sbrks + foreign sbrks * Add linux mremap support code from HJ Liu V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) * Integrated most documentation with the code. * Add support for mmap, with help from - Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Use last_remainder in more cases. * Pack bins using idea from colin@nyx10.cs.du.edu * Use ordered bins instead of best-fit threshhold @@ -4999,34 +6218,34 @@ History: * Support another case of realloc via move into top * Fix error occuring when initial sbrk_base not word-aligned. * Rely on page size for units instead of SBRK_UNIT to - avoid surprises about sbrk alignment conventions. + avoid surprises about sbrk alignment conventions. * Add mallinfo, mallopt. Thanks to Raymond Nijssen - (raymond@es.ele.tue.nl) for the suggestion. + (raymond@es.ele.tue.nl) for the suggestion. * Add `pad' argument to malloc_trim and top_pad mallopt parameter. * More precautions for cases where other routines call sbrk, - courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Added macros etc., allowing use in linux libc from - H.J. Lu (hjl@gnu.ai.mit.edu) + H.J. Lu (hjl@gnu.ai.mit.edu) * Inverted this history list V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) * Re-tuned and fixed to behave more nicely with V2.6.0 changes. * Removed all preallocation code since under current scheme - the work required to undo bad preallocations exceeds - the work saved in good cases for most test programs. + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. * No longer use return list or unconsolidated bins since - no scheme using them consistently outperforms those that don't - given above changes. + no scheme using them consistently outperforms those that don't + given above changes. * Use best fit for very large chunks to prevent some worst-cases. * Added some support for debugging V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) * Removed footers when chunks are in use. Thanks to - Paul Wilson (wilson@cs.texas.edu) for the suggestion. + Paul Wilson (wilson@cs.texas.edu) for the suggestion. V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) * Added malloc_trim, with help from Wolfram Gloger - (wmglo@Dent.MED.Uni-Muenchen.DE). + (wmglo@Dent.MED.Uni-Muenchen.DE). V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) @@ -5042,11 +6261,11 @@ History: V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) * faster bin computation & slightly different binning * merged all consolidations to one part of malloc proper - (eliminating old malloc_find_space & malloc_clean_bin) + (eliminating old malloc_find_space & malloc_clean_bin) * Scan 2 returns chunks (not just 1) * Propagate failure in realloc if malloc returns 0 * Add stuff to allow compilation on non-ANSI compilers - from kpv@research.att.com + from kpv@research.att.com V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) * removed potential for odd address access in prev_chunk @@ -5054,11 +6273,11 @@ History: * misc cosmetics and a bit more internal documentation * anticosmetics: mangled names in macros to evade debugger strangeness * tested on sparc, hp-700, dec-mips, rs6000 - with gcc & native cc (hp, dec only) allowing - Detlefs & Zorn comparison study (in SIGPLAN Notices.) + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) * Based loosely on libg++-1.2X malloc. (It retains some of the overall - structure of old version, but most details differ.) + structure of old version, but most details differ.) */ diff --git a/winsup/cygwin/miscfuncs.cc b/winsup/cygwin/miscfuncs.cc index 0a3651cb1..3787853d5 100644 --- a/winsup/cygwin/miscfuncs.cc +++ b/winsup/cygwin/miscfuncs.cc @@ -146,8 +146,8 @@ cygwin_strncasecmp (const char *cs, const char *ct, size_t n) return RtlCompareUnicodeString (&us, &ut, TRUE); } -extern "C" char * __stdcall -cygwin_strlwr (char *string) +extern "C" char * +strlwr (char *string) { UNICODE_STRING us; size_t len = (strlen (string) + 1) * sizeof (WCHAR); @@ -160,8 +160,8 @@ cygwin_strlwr (char *string) return string; } -extern "C" char * __stdcall -cygwin_strupr (char *string) +extern "C" char * +strupr (char *string) { UNICODE_STRING us; size_t len = (strlen (string) + 1) * sizeof (WCHAR); @@ -202,35 +202,38 @@ check_iovec (const struct iovec *iov, int iovcnt, bool forwrite) return -1; } - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - size_t tot = 0; - - while (iovcnt != 0) + __try { - if (iov->iov_len > SSIZE_MAX || (tot += iov->iov_len) > SSIZE_MAX) + + size_t tot = 0; + + while (iovcnt != 0) { - set_errno (EINVAL); - return -1; + if (iov->iov_len > SSIZE_MAX || (tot += iov->iov_len) > SSIZE_MAX) + { + set_errno (EINVAL); + __leave; + } + + volatile char *p = ((char *) iov->iov_base) + iov->iov_len - 1; + if (!iov->iov_len) + /* nothing to do */; + else if (!forwrite) + *p = dummytest (p); + else + dummytest (p); + + iov++; + iovcnt--; } - volatile char *p = ((char *) iov->iov_base) + iov->iov_len - 1; - if (!iov->iov_len) - /* nothing to do */; - else if (!forwrite) - *p = dummytest (p); - else - dummytest (p); + assert (tot <= SSIZE_MAX); - iov++; - iovcnt--; + return (ssize_t) tot; } - - assert (tot <= SSIZE_MAX); - - return (ssize_t) tot; + __except (EFAULT) + __endtry + return -1; } /* Try hard to schedule another thread. @@ -436,18 +439,23 @@ slashify (const char *src, char *dst, bool trailing_slash_p) void * __reg1 __import_address (void *imp) { - if (*((uint16_t *) imp) != 0x25ff) - return NULL; - myfault efault; - if (efault.faulted ()) - return NULL; - const char *ptr = (const char *) imp; + __try + { + if (*((uint16_t *) imp) == 0x25ff) + { + const char *ptr = (const char *) imp; #ifdef __x86_64__ - const uintptr_t *jmpto = (uintptr_t *) (ptr + 6 + *(int32_t *)(ptr + 2)); + const uintptr_t *jmpto = (uintptr_t *) + (ptr + 6 + *(int32_t *)(ptr + 2)); #else - const uintptr_t *jmpto = (uintptr_t *) *((uintptr_t *) (ptr + 2)); + const uintptr_t *jmpto = (uintptr_t *) *((uintptr_t *) (ptr + 2)); #endif - return (void *) *jmpto; + return (void *) *jmpto; + } + } + __except (NO_ERROR) {} + __endtry + return NULL; } /* CygwinCreateThread. diff --git a/winsup/cygwin/mount.cc b/winsup/cygwin/mount.cc index 43f462686..2d8d38a15 100644 --- a/winsup/cygwin/mount.cc +++ b/winsup/cygwin/mount.cc @@ -1772,50 +1772,54 @@ mount (const char *win32_path, const char *posix_path, unsigned flags) isn't really supported except from fstab? */ int res = -1; - myfault efault; - if (efault.faulted (EFAULT)) - /* errno set */; - else if (!*posix_path) - set_errno (EINVAL); - else if (strpbrk (posix_path, "\\:")) - set_errno (EINVAL); - else if (flags & MOUNT_CYGDRIVE) /* normal mount */ + __try { - /* When flags include MOUNT_CYGDRIVE, take this to mean that - we actually want to change the cygdrive prefix and flags - without actually mounting anything. */ - res = mount_table->write_cygdrive_info (posix_path, flags); - win32_path = NULL; - } - else if (!*win32_path) - set_errno (EINVAL); - else - { - char *w32_path = (char *) win32_path; - if (flags & MOUNT_BIND) + if (!*posix_path) + set_errno (EINVAL); + else if (strpbrk (posix_path, "\\:")) + set_errno (EINVAL); + else if (flags & MOUNT_CYGDRIVE) /* normal mount */ { - /* Prepend root path to bound path. */ - tmp_pathbuf tp; - device dev; - - unsigned conv_flags = 0; - const char *bound_path = w32_path; - - w32_path = tp.c_get (); - int error = mount_table->conv_to_win32_path (bound_path, w32_path, - dev, &conv_flags); - if (error || strlen (w32_path) >= MAX_PATH) - return true; - if ((flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY)) - flags = (MOUNT_BIND | conv_flags) - & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC); + /* When flags include MOUNT_CYGDRIVE, take this to mean that + we actually want to change the cygdrive prefix and flags + without actually mounting anything. */ + res = mount_table->write_cygdrive_info (posix_path, flags); + win32_path = NULL; } - /* Make sure all mounts are user mounts, even those added via mount -a. */ - flags &= ~MOUNT_SYSTEM; - res = mount_table->add_item (w32_path, posix_path, flags); - } + else if (!*win32_path) + set_errno (EINVAL); + else + { + char *w32_path = (char *) win32_path; + if (flags & MOUNT_BIND) + { + /* Prepend root path to bound path. */ + tmp_pathbuf tp; + device dev; - syscall_printf ("%R = mount(%s, %s, %y)", res, win32_path, posix_path, flags); + unsigned conv_flags = 0; + const char *bound_path = w32_path; + + w32_path = tp.c_get (); + int error = mount_table->conv_to_win32_path (bound_path, w32_path, + dev, &conv_flags); + if (error || strlen (w32_path) >= MAX_PATH) + return true; + if ((flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY)) + flags = (MOUNT_BIND | conv_flags) + & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC); + } + /* Make sure all mounts are user mounts, even those added via + mount -a. */ + flags &= ~MOUNT_SYSTEM; + res = mount_table->add_item (w32_path, posix_path, flags); + } + + syscall_printf ("%R = mount(%s, %s, %y)", + res, win32_path, posix_path, flags); + } + __except (EFAULT) {} + __endtry return res; } @@ -1827,15 +1831,18 @@ mount (const char *win32_path, const char *posix_path, unsigned flags) extern "C" int umount (const char *path) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!*path) + __try { - set_errno (EINVAL); - return -1; + if (!*path) + { + set_errno (EINVAL); + __leave; + } + return cygwin_umount (path, 0); } - return cygwin_umount (path, 0); + __except (EFAULT) {} + __endtry + return -1; } /* cygwin_umount: This is like umount but takes an additional flags diff --git a/winsup/cygwin/msg.cc b/winsup/cygwin/msg.cc index f970121c9..5505dc453 100644 --- a/winsup/cygwin/msg.cc +++ b/winsup/cygwin/msg.cc @@ -1,6 +1,6 @@ /* msg.cc: XSI IPC interface for Cygwin. - Copyright 2002, 2003, 2004, 2005, 2008, 2009 Red Hat, Inc. + Copyright 2002, 2003, 2004, 2005, 2008, 2009, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -91,38 +91,40 @@ client_request_msg::client_request_msg (int msqid, extern "C" int msgctl (int msqid, int cmd, struct msqid_ds *buf) { - syscall_printf ("msgctl (msqid = %d, cmd = %y, buf = %p)", - msqid, cmd, buf); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - switch (cmd) + syscall_printf ("msgctl (msqid = %d, cmd = %y, buf = %p)", msqid, cmd, buf); + __try { - case IPC_STAT: - break; - case IPC_SET: - break; - case IPC_RMID: - break; - case IPC_INFO: - break; - case MSG_INFO: - break; - default: - syscall_printf ("-1 [%d] = msgctl ()", EINVAL); - set_errno (EINVAL); - return -1; + switch (cmd) + { + case IPC_STAT: + break; + case IPC_SET: + break; + case IPC_RMID: + break; + case IPC_INFO: + break; + case MSG_INFO: + break; + default: + syscall_printf ("-1 [%d] = msgctl ()", EINVAL); + set_errno (EINVAL); + __leave; + } + client_request_msg request (msqid, cmd, buf); + if (request.make_request () == -1 || request.retval () == -1) + { + syscall_printf ("-1 [%d] = msgctl ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; + } + return request.retval (); } - client_request_msg request (msqid, cmd, buf); - if (request.make_request () == -1 || request.retval () == -1) - { - syscall_printf ("-1 [%d] = msgctl ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; - } - return request.retval (); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -147,19 +149,22 @@ msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) syscall_printf ("msgrcv (msqid = %d, msgp = %p, msgsz = %ld, " "msgtyp = %d, msgflg = %y)", msqid, msgp, msgsz, msgtyp, msgflg); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - client_request_msg request (msqid, msgp, msgsz, msgtyp, msgflg); - if (request.make_request () == -1 || request.rcvval () == -1) + __try { - syscall_printf ("-1 [%d] = msgrcv ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; + client_request_msg request (msqid, msgp, msgsz, msgtyp, msgflg); + if (request.make_request () == -1 || request.rcvval () == -1) + { + syscall_printf ("-1 [%d] = msgrcv ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; + } + return request.rcvval (); } - return request.rcvval (); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -167,17 +172,20 @@ msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg) { syscall_printf ("msgsnd (msqid = %d, msgp = %p, msgsz = %ld, msgflg = %y)", msqid, msgp, msgsz, msgflg); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - client_request_msg request (msqid, msgp, msgsz, msgflg); - if (request.make_request () == -1 || request.retval () == -1) + __try { - syscall_printf ("-1 [%d] = msgsnd ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; + client_request_msg request (msqid, msgp, msgsz, msgflg); + if (request.make_request () == -1 || request.retval () == -1) + { + syscall_printf ("-1 [%d] = msgsnd ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; + } + return request.retval (); } - return request.retval (); + __except (EFAULT) {} + __endtry + return -1; } diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 67c68804a..8ff88ba11 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -500,10 +500,13 @@ dup_ent (servent *src) extern "C" struct protoent * cygwin_getprotobyname (const char *p) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - return dup_ent (getprotobyname (p)); + __try + { + return dup_ent (getprotobyname (p)); + } + __except (EFAULT) {} + __endtry + return NULL; } /* exported as getprotobynumber: standards? */ @@ -731,18 +734,18 @@ extern "C" ssize_t cygwin_sendto (int fd, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->sendto (buf, len, flags, to, tolen); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->sendto (buf, len, flags, to, tolen); + } + __except (EFAULT) {} + __endtry syscall_printf ("%lR = sendto(%d, %p, %ld, %y, %p, %d)", res, fd, buf, len, flags, to, tolen); return res; @@ -753,22 +756,22 @@ extern "C" ssize_t cygwin_recvfrom (int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - /* Originally we shortcircuited here if res == 0. - Allow 0 bytes buffer. This is valid in POSIX and handled in - fhandler_socket::recv_internal. If we shortcircuit, we fail - to deliver valid error conditions and peer address. */ - res = fh->recvfrom (buf, len, flags, from, fromlen); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + /* Originally we shortcircuited here if res == 0. + Allow 0 bytes buffer. This is valid in POSIX and handled in + fhandler_socket::recv_internal. If we shortcircuit, we fail + to deliver valid error conditions and peer address. */ + res = fh->recvfrom (buf, len, flags, from, fromlen); + } + __except (EFAULT) {} + __endtry syscall_printf ("%lR = recvfrom(%d, %p, %ld, %y, %p, %p)", res, fd, buf, len, flags, from, fromlen); return res; @@ -800,14 +803,30 @@ extern "C" int cygwin_setsockopt (int fd, int level, int optname, const void *optval, socklen_t optlen) { - int res; - fhandler_socket *fh = get (fd); + int res = -1; - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else + __try { + fhandler_socket *fh = get (fd); + if (!fh) + __leave; + + /* Switch off the AF_LOCAL handshake and thus SO_PEERCRED handling + for AF_LOCAL/SOCK_STREAM sockets. This allows to handle special + situations in which connect is called before a listening socket + accepts connections. + FIXME: In the long run we should find a more generic solution which + doesn't require a blocking handshake in accept/connect to exchange + SO_PEERCRED credentials. */ + if (level == SOL_SOCKET && optname == SO_PEERCRED) + { + if (optval || optlen) + set_errno (EINVAL); + else + res = fh->af_local_set_no_getpeereid (); + __leave; + } + /* Old applications still use the old WinSock1 IPPROTO_IP values. */ if (level == IPPROTO_IP && CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES) optname = convert_ws1_ip_optname (optname); @@ -892,7 +911,11 @@ cygwin_setsockopt (int fd, int level, int optname, const void *optval, break; } } - + __except (EFAULT) + { + res = -1; + } + __endtry syscall_printf ("%R = setsockopt(%d, %d, %y, %p, %d)", res, fd, level, optname, optval, optlen); return res; @@ -903,19 +926,19 @@ extern "C" int cygwin_getsockopt (int fd, int level, int optname, void *optval, socklen_t *optlen) { - int res; - fhandler_socket *fh = get (fd); + int res = -1; - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else if (optname == SO_PEERCRED && level == SOL_SOCKET) - { - struct ucred *cred = (struct ucred *) optval; - res = fh->getpeereid (&cred->pid, &cred->uid, &cred->gid); - } - else + __try { + fhandler_socket *fh = get (fd); + if (!fh) + __leave; + if (optname == SO_PEERCRED && level == SOL_SOCKET) + { + struct ucred *cred = (struct ucred *) optval; + res = fh->getpeereid (&cred->pid, &cred->uid, &cred->gid); + __leave; + } /* Old applications still use the old WinSock1 IPPROTO_IP values. */ if (level == IPPROTO_IP && CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES) optname = convert_ws1_ip_optname (optname); @@ -930,7 +953,7 @@ cygwin_getsockopt (int fd, int level, int optname, void *optval, *e = find_winsock_errno (*e); } else if (*optlen == 1) - { + { /* Regression in Vista and later: instead of a 4 byte BOOL value, a 1 byte BOOLEAN value is returned, in contrast to older systems and the documentation. Since an int type is expected by the @@ -947,7 +970,11 @@ cygwin_getsockopt (int fd, int level, int optname, void *optval, } } } - + __except (EFAULT) + { + res = -1; + } + __endtry syscall_printf ("%R = getsockopt(%d, %d, %y, %p, %p)", res, fd, level, optname, optval, optlen); return res; @@ -966,20 +993,19 @@ getpeereid (int fd, uid_t *euid, gid_t *egid) extern "C" int cygwin_connect (int fd, const struct sockaddr *name, socklen_t namelen) { - int res; + int res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->connect (name, namelen); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->connect (name, namelen); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = connect(%d, %p, %d)", res, fd, name, namelen); - return res; } @@ -987,11 +1013,14 @@ cygwin_connect (int fd, const struct sockaddr *name, socklen_t namelen) extern "C" struct servent * cygwin_getservbyname (const char *name, const char *proto) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; + servent *res = NULL; - servent *res = dup_ent (getservbyname (name, proto)); + __try + { + res = dup_ent (getservbyname (name, proto)); + } + __except (EFAULT) {} + __endtry syscall_printf ("%p = getservbyname (%s, %s)", res, name, proto); return res; } @@ -1000,11 +1029,14 @@ cygwin_getservbyname (const char *name, const char *proto) extern "C" struct servent * cygwin_getservbyport (int port, const char *proto) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; + servent *res = NULL; - servent *res = dup_ent (getservbyport (port, proto)); + __try + { + res = dup_ent (getservbyport (port, proto)); + } + __except (EFAULT) {} + __endtry syscall_printf ("%p = getservbyport (%d, %s)", res, port, proto); return res; } @@ -1012,67 +1044,77 @@ cygwin_getservbyport (int port, const char *proto) extern "C" int cygwin_gethostname (char *name, size_t len) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; + int res = -1; - if (gethostname (name, len)) + __try { - DWORD local_len = len; - - if (!GetComputerNameExA (ComputerNameDnsFullyQualified, name, &local_len)) + if (gethostname (name, len)) { - set_winsock_errno (); - return -1; + DWORD local_len = len; + + if (!GetComputerNameExA (ComputerNameDnsFullyQualified, name, + &local_len)) + { + set_winsock_errno (); + __leave; + } } + debug_printf ("name %s", name); + res = 0; } - debug_printf ("name %s", name); - return 0; + __except (EFAULT) {} + __endtry + return res; } /* exported as gethostbyname: standards? */ extern "C" struct hostent * cygwin_gethostbyname (const char *name) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - unsigned char tmp_addr[4]; struct hostent tmp, *h; char *tmp_aliases[1] = {0}; char *tmp_addr_list[2] = {0,0}; unsigned int a, b, c, d; char dummy; + hostent *res = NULL; - if (sscanf (name, "%u.%u.%u.%u%c", &a, &b, &c, &d, &dummy) != 4 - || a >= 256 || b >= 256 || c >= 256 || d >= 256) - h = gethostbyname (name); - else + __try { - /* In case you don't have DNS, at least x.x.x.x still works */ - memset (&tmp, 0, sizeof (tmp)); - tmp_addr[0] = a; - tmp_addr[1] = b; - tmp_addr[2] = c; - tmp_addr[3] = d; - tmp_addr_list[0] = (char *) tmp_addr; - tmp.h_name = name; - tmp.h_aliases = tmp_aliases; - tmp.h_addrtype = 2; - tmp.h_length = 4; - tmp.h_addr_list = tmp_addr_list; - h = &tmp; - } + if (sscanf (name, "%u.%u.%u.%u%c", &a, &b, &c, &d, &dummy) != 4 + || a >= 256 || b >= 256 || c >= 256 || d >= 256) + h = gethostbyname (name); + else + { + /* In case you don't have DNS, at least x.x.x.x still works */ + memset (&tmp, 0, sizeof (tmp)); + tmp_addr[0] = a; + tmp_addr[1] = b; + tmp_addr[2] = c; + tmp_addr[3] = d; + tmp_addr_list[0] = (char *) tmp_addr; + tmp.h_name = name; + tmp.h_aliases = tmp_aliases; + tmp.h_addrtype = 2; + tmp.h_length = 4; + tmp.h_addr_list = tmp_addr_list; + h = &tmp; + } - hostent *res = dup_ent (h); - if (res) - debug_printf ("h_name %s", res->h_name); - else - { - debug_printf ("dup_ent returned NULL for name %s, h %p", name, h); - set_host_errno (); + res = dup_ent (h); + if (res) + debug_printf ("h_name %s", res->h_name); + else + { + debug_printf ("dup_ent returned NULL for name %s, h %p", name, h); + set_host_errno (); + } } + __except (EFAULT) + { + res = NULL; + } + __endtry return res; } @@ -1080,15 +1122,21 @@ cygwin_gethostbyname (const char *name) extern "C" struct hostent * cygwin_gethostbyaddr (const char *addr, int len, int type) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; + hostent *res = NULL; - hostent *res = dup_ent (gethostbyaddr (addr, len, type)); - if (res) - debug_printf ("h_name %s", res->h_name); - else - set_host_errno (); + __try + { + res = dup_ent (gethostbyaddr (addr, len, type)); + if (res) + debug_printf ("h_name %s", res->h_name); + else + set_host_errno (); + } + __except (EFAULT) + { + res = NULL; + } + __endtry return res; } @@ -1311,51 +1359,56 @@ gethostby_helper (const char *name, const int af, const int type, extern "C" struct hostent * gethostbyname2 (const char *name, int af) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; + hostent *res = NULL; - if (!(_res.options & RES_INIT)) - res_init(); - bool v4to6 = _res.options & RES_USE_INET6; - - int type, addrsize_in, addrsize_out; - switch (af) + __try { - case AF_INET: - addrsize_in = NS_INADDRSZ; - addrsize_out = (v4to6) ? NS_IN6ADDRSZ : NS_INADDRSZ; - type = ns_t_a; - break; - case AF_INET6: - addrsize_in = addrsize_out = NS_IN6ADDRSZ; - type = ns_t_aaaa; - break; - default: - set_errno (EAFNOSUPPORT); - h_errno = NETDB_INTERNAL; - return NULL; - } + if (!(_res.options & RES_INIT)) + res_init(); + bool v4to6 = _res.options & RES_USE_INET6; - return gethostby_helper (name, af, type, addrsize_in, addrsize_out); + int type, addrsize_in, addrsize_out; + switch (af) + { + case AF_INET: + addrsize_in = NS_INADDRSZ; + addrsize_out = (v4to6) ? NS_IN6ADDRSZ : NS_INADDRSZ; + type = ns_t_a; + break; + case AF_INET6: + addrsize_in = addrsize_out = NS_IN6ADDRSZ; + type = ns_t_aaaa; + break; + default: + set_errno (EAFNOSUPPORT); + h_errno = NETDB_INTERNAL; + __leave; + } + + res = gethostby_helper (name, af, type, addrsize_in, addrsize_out); + } + __except (EFAULT) {} + __endtry + return res; } /* exported as accept: standards? */ extern "C" int cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len) { - int res; + int res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->accept4 (peer, len, fh->is_nonblocking () ? SOCK_NONBLOCK : 0); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->accept4 (peer, len, + fh->is_nonblocking () ? SOCK_NONBLOCK : 0); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = accept(%d, %p, %p)", res, fd, peer, len); return res; } @@ -1363,23 +1416,22 @@ cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len) extern "C" int accept4 (int fd, struct sockaddr *peer, socklen_t *len, int flags) { - int res; + int res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) + __try { - set_errno (EINVAL); - res = -1; + fhandler_socket *fh = get (fd); + if (!fh) + __leave; + if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) + set_errno (EINVAL); + else + res = fh->accept4 (peer, len, flags); } - else - res = fh->accept4 (peer, len, flags); - + __except (EFAULT) {} + __endtry syscall_printf ("%R = accept4(%d, %p, %p, %y)", res, fd, peer, len, flags); return res; } @@ -1388,15 +1440,16 @@ accept4 (int fd, struct sockaddr *peer, socklen_t *len, int flags) extern "C" int cygwin_bind (int fd, const struct sockaddr *my_addr, socklen_t addrlen) { - int res; - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->bind (my_addr, addrlen); + int res = -1; + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->bind (my_addr, addrlen); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = bind(%d, %p, %d)", res, fd, my_addr, addrlen); return res; } @@ -1405,16 +1458,16 @@ cygwin_bind (int fd, const struct sockaddr *my_addr, socklen_t addrlen) extern "C" int cygwin_getsockname (int fd, struct sockaddr *addr, socklen_t *namelen) { - int res; - - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->getsockname (addr, namelen); + int res = -1; + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->getsockname (addr, namelen); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R =getsockname (%d, %p, %p)", res, fd, addr, namelen); return res; } @@ -1423,14 +1476,16 @@ cygwin_getsockname (int fd, struct sockaddr *addr, socklen_t *namelen) extern "C" int cygwin_listen (int fd, int backlog) { - int res; - fhandler_socket *fh = get (fd); - - if (!fh) - res = -1; - else - res = fh->listen (backlog); + int res = -1; + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->listen (backlog); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = listen(%d, %d)", res, fd, backlog); return res; } @@ -1439,15 +1494,11 @@ cygwin_listen (int fd, int backlog) extern "C" int cygwin_shutdown (int fd, int how) { - int res; + int res = -1; fhandler_socket *fh = get (fd); - - if (!fh) - res = -1; - else + if (fh) res = fh->shutdown (how); - syscall_printf ("%R = shutdown(%d, %d)", res, fd, how); return res; } @@ -1469,51 +1520,54 @@ cygwin_hstrerror (int err) extern "C" void cygwin_herror (const char *s) { - myfault efault; - if (efault.faulted ()) - return; - if (cygheap->fdtab.not_open (2)) - return; - - if (s) + __try { - write (2, s, strlen (s)); - write (2, ": ", 2); + if (cygheap->fdtab.not_open (2)) + return; + + if (s) + { + write (2, s, strlen (s)); + write (2, ": ", 2); + } + + const char *h_errstr = cygwin_hstrerror (h_errno); + + if (!h_errstr) + switch (h_errno) + { + case NETDB_INTERNAL: + h_errstr = "Resolver internal error"; + break; + case NETDB_SUCCESS: + h_errstr = "Resolver error 0 (no error)"; + break; + default: + h_errstr = "Unknown resolver error"; + break; + } + write (2, h_errstr, strlen (h_errstr)); + write (2, "\n", 1); } - - const char *h_errstr = cygwin_hstrerror (h_errno); - - if (!h_errstr) - switch (h_errno) - { - case NETDB_INTERNAL: - h_errstr = "Resolver internal error"; - break; - case NETDB_SUCCESS: - h_errstr = "Resolver error 0 (no error)"; - break; - default: - h_errstr = "Unknown resolver error"; - break; - } - write (2, h_errstr, strlen (h_errstr)); - write (2, "\n", 1); + __except (NO_ERROR) {} + __endtry } /* exported as getpeername: standards? */ extern "C" int cygwin_getpeername (int fd, struct sockaddr *name, socklen_t *len) { - int res; - - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->getpeername (name, len); + int res = -1; + fhandler_socket *fh = NULL; + __try + { + fh = get (fd); + if (fh) + res = fh->getpeername (name, len); + } + __except (EFAULT) {} + __endtry syscall_printf ("%R = getpeername(%d) %p", res, fd, (fh ? fh->get_socket () : (SOCKET) -1)); return res; @@ -1523,22 +1577,22 @@ cygwin_getpeername (int fd, struct sockaddr *name, socklen_t *len) extern "C" ssize_t cygwin_recv (int fd, void *buf, size_t len, int flags) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - /* Originally we shortcircuited here if res == 0. - Allow 0 bytes buffer. This is valid in POSIX and handled in - fhandler_socket::recv_internal. If we shortcircuit, we fail - to deliver valid error conditions. */ - res = fh->recvfrom (buf, len, flags, NULL, NULL); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + /* Originally we shortcircuited here if res == 0. + Allow 0 bytes buffer. This is valid in POSIX and handled in + fhandler_socket::recv_internal. If we shortcircuit, we fail + to deliver valid error conditions. */ + res = fh->recvfrom (buf, len, flags, NULL, NULL); + } + __except (EFAULT) {} + __endtry syscall_printf ("%lR = recv(%d, %p, %ld, %y)", res, fd, buf, len, flags); return res; } @@ -1547,18 +1601,18 @@ cygwin_recv (int fd, void *buf, size_t len, int flags) extern "C" ssize_t cygwin_send (int fd, const void *buf, size_t len, int flags) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else - res = fh->sendto (buf, len, flags, NULL, 0); - + __try + { + fhandler_socket *fh = get (fd); + if (fh) + res = fh->sendto (buf, len, flags, NULL, 0); + } + __except (EFAULT) + __endtry syscall_printf ("%lR = send(%d, %p, %ld, %y)", res, fd, buf, len, flags); return res; } @@ -1567,21 +1621,22 @@ cygwin_send (int fd, const void *buf, size_t len, int flags) extern "C" int getdomainname (char *domain, size_t len) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - PFIXED_INFO info = NULL; - ULONG size = 0; - - if (GetNetworkParams(info, &size) == ERROR_BUFFER_OVERFLOW - && (info = (PFIXED_INFO) alloca(size)) - && GetNetworkParams(info, &size) == ERROR_SUCCESS) + __try { - strncpy(domain, info->DomainName, len); - return 0; + PFIXED_INFO info = NULL; + ULONG size = 0; + + if (GetNetworkParams(info, &size) == ERROR_BUFFER_OVERFLOW + && (info = (PFIXED_INFO) alloca(size)) + && GetNetworkParams(info, &size) == ERROR_SUCCESS) + { + strncpy(domain, info->DomainName, len); + return 0; + } + __seterrno (); } - __seterrno (); + __except (EFAULT) + __endtry return -1; } @@ -2176,85 +2231,88 @@ freeifaddrs (struct ifaddrs *ifp) int get_ifconf (struct ifconf *ifc, int what) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - /* Ensure we have space for at least one struct ifreqs, fail if not. */ - if (ifc->ifc_len < (int) sizeof (struct ifreq)) + __try { - set_errno (EINVAL); - return -1; - } - - struct ifall *ifret, *ifp; - ifret = get_ifs (AF_INET); - if (!ifret) - return -1; - - struct sockaddr_in *sin; - struct ifreq *ifr = ifc->ifc_req; - int cnt = 0; - for (ifp = ifret; ifp; ifp = (struct ifall *) ifp->ifa_ifa.ifa_next) - { - ++cnt; - strcpy (ifr->ifr_name, ifp->ifa_name); - switch (what) + /* Ensure we have space for at least one struct ifreqs, fail if not. */ + if (ifc->ifc_len < (int) sizeof (struct ifreq)) { - case SIOCGIFFLAGS: - ifr->ifr_flags = ifp->ifa_ifa.ifa_flags; - break; - case SIOCGIFCONF: - case SIOCGIFADDR: - sin = (struct sockaddr_in *) &ifr->ifr_addr; - memcpy (sin, &ifp->ifa_addr, sizeof *sin); - break; - case SIOCGIFNETMASK: - sin = (struct sockaddr_in *) &ifr->ifr_netmask; - memcpy (sin, &ifp->ifa_netmask, sizeof *sin); - break; - case SIOCGIFDSTADDR: - sin = (struct sockaddr_in *) &ifr->ifr_dstaddr; - if (ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT) - memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); - else /* Return addr as on Linux. */ - memcpy (sin, &ifp->ifa_addr, sizeof *sin); - break; - case SIOCGIFBRDADDR: - sin = (struct sockaddr_in *) &ifr->ifr_broadaddr; - if (!(ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT)) - memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); - else - { - sin->sin_addr.s_addr = INADDR_ANY; - sin->sin_family = AF_INET; - sin->sin_port = 0; - } - break; - case SIOCGIFHWADDR: - memcpy (&ifr->ifr_hwaddr, &ifp->ifa_hwaddr, sizeof ifr->ifr_hwaddr); - break; - case SIOCGIFMETRIC: - ifr->ifr_metric = ifp->ifa_metric; - break; - case SIOCGIFMTU: - ifr->ifr_mtu = ifp->ifa_mtu; - break; - case SIOCGIFINDEX: - ifr->ifr_ifindex = ifp->ifa_ifindex; - break; - case SIOCGIFFRNDLYNAM: - memcpy (ifr->ifr_frndlyname, &ifp->ifa_frndlyname, - sizeof (struct ifreq_frndlyname)); + set_errno (EINVAL); + __leave; } - if ((caddr_t) ++ifr > - ifc->ifc_buf + ifc->ifc_len - sizeof (struct ifreq)) - break; + + struct ifall *ifret, *ifp; + ifret = get_ifs (AF_INET); + if (!ifret) + __leave; + + struct sockaddr_in *sin; + struct ifreq *ifr = ifc->ifc_req; + int cnt = 0; + for (ifp = ifret; ifp; ifp = (struct ifall *) ifp->ifa_ifa.ifa_next) + { + ++cnt; + strcpy (ifr->ifr_name, ifp->ifa_name); + switch (what) + { + case SIOCGIFFLAGS: + ifr->ifr_flags = ifp->ifa_ifa.ifa_flags; + break; + case SIOCGIFCONF: + case SIOCGIFADDR: + sin = (struct sockaddr_in *) &ifr->ifr_addr; + memcpy (sin, &ifp->ifa_addr, sizeof *sin); + break; + case SIOCGIFNETMASK: + sin = (struct sockaddr_in *) &ifr->ifr_netmask; + memcpy (sin, &ifp->ifa_netmask, sizeof *sin); + break; + case SIOCGIFDSTADDR: + sin = (struct sockaddr_in *) &ifr->ifr_dstaddr; + if (ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT) + memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); + else /* Return addr as on Linux. */ + memcpy (sin, &ifp->ifa_addr, sizeof *sin); + break; + case SIOCGIFBRDADDR: + sin = (struct sockaddr_in *) &ifr->ifr_broadaddr; + if (!(ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT)) + memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); + else + { + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_family = AF_INET; + sin->sin_port = 0; + } + break; + case SIOCGIFHWADDR: + memcpy (&ifr->ifr_hwaddr, &ifp->ifa_hwaddr, + sizeof ifr->ifr_hwaddr); + break; + case SIOCGIFMETRIC: + ifr->ifr_metric = ifp->ifa_metric; + break; + case SIOCGIFMTU: + ifr->ifr_mtu = ifp->ifa_mtu; + break; + case SIOCGIFINDEX: + ifr->ifr_ifindex = ifp->ifa_ifindex; + break; + case SIOCGIFFRNDLYNAM: + memcpy (ifr->ifr_frndlyname, &ifp->ifa_frndlyname, + sizeof (struct ifreq_frndlyname)); + } + if ((caddr_t) ++ifr > + ifc->ifc_buf + ifc->ifc_len - sizeof (struct ifreq)) + break; + } + /* Set the correct length */ + ifc->ifc_len = cnt * sizeof (struct ifreq); + free (ifret); + return 0; } - /* Set the correct length */ - ifc->ifc_len = cnt * sizeof (struct ifreq); - free (ifret); - return 0; + __except (EFAULT) {} + __endtry + return -1; } extern "C" unsigned @@ -2263,26 +2321,30 @@ if_nametoindex (const char *name) PIP_ADAPTER_ADDRESSES pa0 = NULL, pap; unsigned index = 0; - myfault efault; - if (efault.faulted (EFAULT)) - return 0; - - if (get_adapters_addresses (&pa0, AF_UNSPEC)) + __try { - char lname[IF_NAMESIZE], *c; + if (get_adapters_addresses (&pa0, AF_UNSPEC)) + { + char lname[IF_NAMESIZE], *c; - lname[0] = '\0'; - strncat (lname, name, IF_NAMESIZE - 1); - if (lname[0] == '{' && (c = strchr (lname, ':'))) - *c = '\0'; - for (pap = pa0; pap; pap = pap->Next) - if (strcasematch (lname, pap->AdapterName)) - { - index = pap->Ipv6IfIndex ?: pap->IfIndex; - break; - } - free (pa0); + lname[0] = '\0'; + strncat (lname, name, IF_NAMESIZE - 1); + if (lname[0] == '{' && (c = strchr (lname, ':'))) + *c = '\0'; + for (pap = pa0; pap; pap = pap->Next) + if (strcasematch (lname, pap->AdapterName)) + { + index = pap->Ipv6IfIndex ?: pap->IfIndex; + break; + } + free (pa0); + } } + __except (EFAULT) + { + index = 0; + } + __endtry return index; } @@ -2292,35 +2354,38 @@ if_indextoname (unsigned ifindex, char *ifname) PIP_ADAPTER_ADDRESSES pa0 = NULL, pap; char *name = NULL; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - - if (get_adapters_addresses (&pa0, AF_UNSPEC)) + __try { - for (pap = pa0; pap; pap = pap->Next) - if (ifindex == (pap->Ipv6IfIndex ?: pap->IfIndex)) - { - /* Unfortunately the pre-Vista IPv6 stack has a distinct loopback - device with the same Ipv6IfIndex as the IfIndex of the IPv4 - loopback device, but with a different adapter name. - For consistency with /proc/net/if_inet6, try to find the - IPv6 loopback device and use that adapter name instead. - We identify the loopback device by its IfIndex of 1. */ - if (pap->IfIndex == 1 && pap->Ipv6IfIndex == 0) - for (PIP_ADAPTER_ADDRESSES pap2 = pa0; pap2; pap2 = pap2->Next) - if (pap2->Ipv6IfIndex == 1) - { - pap = pap2; - break; - } - name = strcpy (ifname, pap->AdapterName); - break; - } - free (pa0); + if (get_adapters_addresses (&pa0, AF_UNSPEC)) + { + for (pap = pa0; pap; pap = pap->Next) + if (ifindex == (pap->Ipv6IfIndex ?: pap->IfIndex)) + { + /* Unfortunately the pre-Vista IPv6 stack has a distinct + loopback device with the same Ipv6IfIndex as the IfIndex + of the IPv4 loopback device, but with a different adapter + name. For consistency with /proc/net/if_inet6, try to find + the IPv6 loopback device and use that adapter name instead. + We identify the loopback device by its IfIndex of 1. */ + if (pap->IfIndex == 1 && pap->Ipv6IfIndex == 0) + for (PIP_ADAPTER_ADDRESSES pap2 = pa0; + pap2; + pap2 = pap2->Next) + if (pap2->Ipv6IfIndex == 1) + { + pap = pap2; + break; + } + name = strcpy (ifname, pap->AdapterName); + break; + } + free (pa0); + } + else + set_errno (ENXIO); } - else - set_errno (ENXIO); + __except (EFAULT) {} + __endtry return name; } @@ -2331,58 +2396,60 @@ if_nameindex (void) struct if_nameindex *iflist = NULL; char (*ifnamelist)[IF_NAMESIZE]; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - - if (get_adapters_addresses (&pa0, AF_UNSPEC)) + __try { - int cnt = 0; - for (pap = pa0; pap; pap = pap->Next) - ++cnt; - iflist = (struct if_nameindex *) - malloc ((cnt + 1) * sizeof (struct if_nameindex) - + cnt * IF_NAMESIZE); - if (!iflist) - set_errno (ENOBUFS); - else + if (get_adapters_addresses (&pa0, AF_UNSPEC)) { - ifnamelist = (char (*)[IF_NAMESIZE]) (iflist + cnt + 1); - for (pap = pa0, cnt = 0; pap; pap = pap->Next) + int cnt = 0; + for (pap = pa0; pap; pap = pap->Next) + ++cnt; + iflist = (struct if_nameindex *) + malloc ((cnt + 1) * sizeof (struct if_nameindex) + + cnt * IF_NAMESIZE); + if (!iflist) + set_errno (ENOBUFS); + else { - for (int i = 0; i < cnt; ++i) - if (iflist[i].if_index == (pap->Ipv6IfIndex ?: pap->IfIndex)) - goto outer_loop; - iflist[cnt].if_index = pap->Ipv6IfIndex ?: pap->IfIndex; - strcpy (iflist[cnt].if_name = ifnamelist[cnt], pap->AdapterName); - /* See comment in if_indextoname. */ - if (pap->IfIndex == 1 && pap->Ipv6IfIndex == 0) - for (PIP_ADAPTER_ADDRESSES pap2 = pa0; pap2; pap2 = pap2->Next) - if (pap2->Ipv6IfIndex == 1) - { - strcpy (ifnamelist[cnt], pap2->AdapterName); - break; - } - ++cnt; - outer_loop: - ; + ifnamelist = (char (*)[IF_NAMESIZE]) (iflist + cnt + 1); + for (pap = pa0, cnt = 0; pap; pap = pap->Next) + { + for (int i = 0; i < cnt; ++i) + if (iflist[i].if_index + == (pap->Ipv6IfIndex ?: pap->IfIndex)) + goto outer_loop; + iflist[cnt].if_index = pap->Ipv6IfIndex ?: pap->IfIndex; + strcpy (iflist[cnt].if_name = ifnamelist[cnt], + pap->AdapterName); + /* See comment in if_indextoname. */ + if (pap->IfIndex == 1 && pap->Ipv6IfIndex == 0) + for (PIP_ADAPTER_ADDRESSES pap2 = pa0; + pap2; + pap2 = pap2->Next) + if (pap2->Ipv6IfIndex == 1) + { + strcpy (ifnamelist[cnt], pap2->AdapterName); + break; + } + ++cnt; + outer_loop: + ; + } + iflist[cnt].if_index = 0; + iflist[cnt].if_name = NULL; } - iflist[cnt].if_index = 0; - iflist[cnt].if_name = NULL; + free (pa0); } - free (pa0); + else + set_errno (ENXIO); } - else - set_errno (ENXIO); + __except (EFAULT) {} + __endtry return iflist; } extern "C" void if_freenameindex (struct if_nameindex *ptr) { - myfault efault; - if (efault.faulted (EFAULT)) - return; free (ptr); } @@ -2398,72 +2465,75 @@ cygwin_bindresvport_sa (int fd, struct sockaddr *sa) struct sockaddr_in6 *sin6 = NULL; in_port_t port; socklen_t salen; - int ret; + int ret = -1; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - fhandler_socket *fh = get (fd); - if (!fh) - return -1; - - if (!sa) + __try { - sa = (struct sockaddr *) &sst; - memset (&sst, 0, sizeof sst); - sa->sa_family = fh->get_addr_family (); + fhandler_socket *fh = get (fd); + if (!fh) + __leave; + + if (!sa) + { + sa = (struct sockaddr *) &sst; + memset (&sst, 0, sizeof sst); + sa->sa_family = fh->get_addr_family (); + } + + switch (sa->sa_family) + { + case AF_INET: + salen = sizeof (struct sockaddr_in); + sin = (struct sockaddr_in *) sa; + port = sin->sin_port; + break; + case AF_INET6: + salen = sizeof (struct sockaddr_in6); + sin6 = (struct sockaddr_in6 *) sa; + port = sin6->sin6_port; + break; + default: + set_errno (EPFNOSUPPORT); + __leave; + } + + /* If a non-zero port number is given, try this first. If that succeeds, + or if the error message is serious, return. */ + if (port) + { + ret = fh->bind (sa, salen); + if (!ret || (get_errno () != EADDRINUSE && get_errno () != EINVAL)) + __leave; + } + + LONG myport; + + for (int i = 0; i < NUM_PORTS; i++) + { + while ((myport = InterlockedExchange ( + &cygwin_shared->last_used_bindresvport, -1)) == -1) + yield (); + if (myport == 0 || --myport < PORT_LOW) + myport = PORT_HIGH; + InterlockedExchange (&cygwin_shared->last_used_bindresvport, myport); + + if (sa->sa_family == AF_INET6) + sin6->sin6_port = htons (myport); + else + sin->sin_port = htons (myport); + if (!(ret = fh->bind (sa, salen))) + break; + if (get_errno () != EADDRINUSE && get_errno () != EINVAL) + break; + } + } - - switch (sa->sa_family) - { - case AF_INET: - salen = sizeof (struct sockaddr_in); - sin = (struct sockaddr_in *) sa; - port = sin->sin_port; - break; - case AF_INET6: - salen = sizeof (struct sockaddr_in6); - sin6 = (struct sockaddr_in6 *) sa; - port = sin6->sin6_port; - break; - default: - set_errno (EPFNOSUPPORT); - return -1; - } - - /* If a non-zero port number is given, try this first. If that succeeds, - or if the error message is serious, return. */ - if (port) - { - ret = fh->bind (sa, salen); - if (!ret || (get_errno () != EADDRINUSE && get_errno () != EINVAL)) - return ret; - } - - LONG myport; - - for (int i = 0; i < NUM_PORTS; i++) - { - while ((myport = InterlockedExchange (&cygwin_shared->last_used_bindresvport, -1)) == -1) - yield (); - if (myport == 0 || --myport < PORT_LOW) - myport = PORT_HIGH; - InterlockedExchange (&cygwin_shared->last_used_bindresvport, myport); - - if (sa->sa_family == AF_INET6) - sin6->sin6_port = htons (myport); - else - sin->sin_port = htons (myport); - if (!(ret = fh->bind (sa, salen))) - break; - if (get_errno () != EADDRINUSE && get_errno () != EINVAL) - break; - } - + __except (EFAULT) {} + __endtry return ret; } + extern "C" int cygwin_bindresvport (int fd, struct sockaddr_in *sin) { @@ -2476,204 +2546,195 @@ extern "C" int socketpair (int family, int type, int protocol, int *sb) { int res = -1; - SOCKET insock, outsock, newsock; + SOCKET insock = INVALID_SOCKET; + SOCKET outsock = INVALID_SOCKET; + SOCKET newsock = INVALID_SOCKET; struct sockaddr_in sock_in, sock_out; int len; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; + __try + { + int flags = type & _SOCK_FLAG_MASK; + type &= ~_SOCK_FLAG_MASK; - int flags = type & _SOCK_FLAG_MASK; - type &= ~_SOCK_FLAG_MASK; + if (family != AF_LOCAL && family != AF_INET) + { + set_errno (EAFNOSUPPORT); + __leave; + } + if (type != SOCK_STREAM && type != SOCK_DGRAM) + { + set_errno (EPROTOTYPE); + __leave; + } + if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) + { + set_errno (EINVAL); + __leave; + } + if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL) + || (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET)) + { + set_errno (EPROTONOSUPPORT); + __leave; + } - if (family != AF_LOCAL && family != AF_INET) - { - set_errno (EAFNOSUPPORT); - goto done; - } - if (type != SOCK_STREAM && type != SOCK_DGRAM) - { - set_errno (EPROTOTYPE); - goto done; - } - if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) - { - set_errno (EINVAL); - goto done; - } - if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL) - || (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET)) - { - set_errno (EPROTONOSUPPORT); - goto done; - } + /* create the first socket */ + newsock = socket (AF_INET, type, 0); + if (newsock == INVALID_SOCKET) + { + debug_printf ("first socket call failed"); + set_winsock_errno (); + __leave; + } - /* create the first socket */ - newsock = socket (AF_INET, type, 0); - if (newsock == INVALID_SOCKET) - { - debug_printf ("first socket call failed"); - set_winsock_errno (); - goto done; - } - - /* bind the socket to any unused port */ - sock_in.sin_family = AF_INET; - sock_in.sin_port = 0; - sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) - { - debug_printf ("bind failed"); - set_winsock_errno (); - closesocket (newsock); - goto done; - } - len = sizeof (sock_in); - if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0) - { - debug_printf ("getsockname error"); - set_winsock_errno (); - closesocket (newsock); - goto done; - } - - /* For stream sockets, create a listener */ - if (type == SOCK_STREAM) - listen (newsock, 2); - - /* create a connecting socket */ - outsock = socket (AF_INET, type, 0); - if (outsock == INVALID_SOCKET) - { - debug_printf ("second socket call failed"); - set_winsock_errno (); - closesocket (newsock); - goto done; - } - - /* For datagram sockets, bind the 2nd socket to an unused address, too */ - if (type == SOCK_DGRAM) - { - sock_out.sin_family = AF_INET; - sock_out.sin_port = 0; - sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0) + /* bind the socket to any unused port */ + sock_in.sin_family = AF_INET; + sock_in.sin_port = 0; + sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) { debug_printf ("bind failed"); set_winsock_errno (); - closesocket (newsock); - closesocket (outsock); - goto done; + __leave; } - len = sizeof (sock_out); - if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0) + len = sizeof (sock_in); + if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0) { debug_printf ("getsockname error"); set_winsock_errno (); - closesocket (newsock); - closesocket (outsock); - goto done; + __leave; } - } - /* Force IP address to loopback */ - sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (type == SOCK_DGRAM) - sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + /* For stream sockets, create a listener */ + if (type == SOCK_STREAM) + listen (newsock, 2); - /* Do a connect */ - if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) - { - debug_printf ("connect error"); - set_winsock_errno (); - closesocket (newsock); - closesocket (outsock); - goto done; - } - - if (type == SOCK_STREAM) - { - /* For stream sockets, accept the connection and close the listener */ - len = sizeof (sock_in); - insock = accept (newsock, (struct sockaddr *) &sock_in, &len); - if (insock == INVALID_SOCKET) + /* create a connecting socket */ + outsock = socket (AF_INET, type, 0); + if (outsock == INVALID_SOCKET) { - debug_printf ("accept error"); + debug_printf ("second socket call failed"); set_winsock_errno (); - closesocket (newsock); - closesocket (outsock); - goto done; + __leave; } - closesocket (newsock); - } - else - { - /* For datagram sockets, connect the 2nd socket */ - if (connect (newsock, (struct sockaddr *) &sock_out, - sizeof (sock_out)) < 0) + + /* For datagram sockets, bind the 2nd socket to an unused address, too */ + if (type == SOCK_DGRAM) + { + sock_out.sin_family = AF_INET; + sock_out.sin_port = 0; + sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0) + { + debug_printf ("bind failed"); + set_winsock_errno (); + __leave; + } + len = sizeof (sock_out); + if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0) + { + debug_printf ("getsockname error"); + set_winsock_errno (); + __leave; + } + } + + /* Force IP address to loopback */ + sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (type == SOCK_DGRAM) + sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* Do a connect */ + if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) { debug_printf ("connect error"); set_winsock_errno (); - closesocket (newsock); - closesocket (outsock); - goto done; + __leave; + } + + if (type == SOCK_STREAM) + { + /* For stream sockets, accept the connection and close the listener */ + len = sizeof (sock_in); + insock = accept (newsock, (struct sockaddr *) &sock_in, &len); + if (insock == INVALID_SOCKET) + { + debug_printf ("accept error"); + set_winsock_errno (); + __leave; + } + closesocket (newsock); + newsock = INVALID_SOCKET; + } + else + { + /* For datagram sockets, connect the 2nd socket */ + if (connect (newsock, (struct sockaddr *) &sock_out, + sizeof (sock_out)) < 0) + { + debug_printf ("connect error"); + set_winsock_errno (); + __leave; + } + insock = newsock; + newsock = INVALID_SOCKET; + } + + cygheap_fdnew sb0; + const device *dev; + + if (family == AF_INET) + dev = (type == SOCK_STREAM ? tcp_dev : udp_dev); + else + dev = (type == SOCK_STREAM ? stream_dev : dgram_dev); + + if (sb0 >= 0 && fdsock (sb0, dev, insock)) + { + ((fhandler_socket *) sb0)->set_addr_family (family); + ((fhandler_socket *) sb0)->set_socket_type (type); + ((fhandler_socket *) sb0)->connect_state (connected); + if (flags & SOCK_NONBLOCK) + ((fhandler_socket *) sb0)->set_nonblocking (true); + if (flags & SOCK_CLOEXEC) + ((fhandler_socket *) sb0)->set_close_on_exec (true); + if (family == AF_LOCAL && type == SOCK_STREAM) + ((fhandler_socket *) sb0)->af_local_set_sockpair_cred (); + + cygheap_fdnew sb1 (sb0, false); + + if (sb1 >= 0 && fdsock (sb1, dev, outsock)) + { + ((fhandler_socket *) sb1)->set_addr_family (family); + ((fhandler_socket *) sb1)->set_socket_type (type); + ((fhandler_socket *) sb1)->connect_state (connected); + if (flags & SOCK_NONBLOCK) + ((fhandler_socket *) sb1)->set_nonblocking (true); + if (flags & SOCK_CLOEXEC) + ((fhandler_socket *) sb1)->set_close_on_exec (true); + if (family == AF_LOCAL && type == SOCK_STREAM) + ((fhandler_socket *) sb1)->af_local_set_sockpair_cred (); + + sb[0] = sb0; + sb[1] = sb1; + res = 0; + } + else + sb0.release (); } - insock = newsock; } - - { - cygheap_fdnew sb0; - const device *dev; - - if (family == AF_INET) - dev = (type == SOCK_STREAM ? tcp_dev : udp_dev); - else - dev = (type == SOCK_STREAM ? stream_dev : dgram_dev); - - if (sb0 >= 0 && fdsock (sb0, dev, insock)) - { - ((fhandler_socket *) sb0)->set_addr_family (family); - ((fhandler_socket *) sb0)->set_socket_type (type); - ((fhandler_socket *) sb0)->connect_state (connected); - if (flags & SOCK_NONBLOCK) - ((fhandler_socket *) sb0)->set_nonblocking (true); - if (flags & SOCK_CLOEXEC) - ((fhandler_socket *) sb0)->set_close_on_exec (true); - if (family == AF_LOCAL && type == SOCK_STREAM) - ((fhandler_socket *) sb0)->af_local_set_sockpair_cred (); - - cygheap_fdnew sb1 (sb0, false); - - if (sb1 >= 0 && fdsock (sb1, dev, outsock)) - { - ((fhandler_socket *) sb1)->set_addr_family (family); - ((fhandler_socket *) sb1)->set_socket_type (type); - ((fhandler_socket *) sb1)->connect_state (connected); - if (flags & SOCK_NONBLOCK) - ((fhandler_socket *) sb1)->set_nonblocking (true); - if (flags & SOCK_CLOEXEC) - ((fhandler_socket *) sb1)->set_close_on_exec (true); - if (family == AF_LOCAL && type == SOCK_STREAM) - ((fhandler_socket *) sb1)->af_local_set_sockpair_cred (); - - sb[0] = sb0; - sb[1] = sb1; - res = 0; - } - else - sb0.release (); - } - - if (res == -1) - { - closesocket (insock); - closesocket (outsock); - } - } - -done: + __except (EFAULT) {} + __endtry syscall_printf ("%R = socketpair(...)", res); + if (res == -1) + { + if (insock != INVALID_SOCKET) + closesocket (insock); + if (outsock != INVALID_SOCKET) + closesocket (outsock); + if (newsock != INVALID_SOCKET) + closesocket (newsock); + } return res; } @@ -2693,26 +2754,29 @@ endhostent (void) extern "C" ssize_t cygwin_recvmsg (int fd, struct msghdr *msg, int flags) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else + __try { - res = check_iovec_for_read (msg->msg_iov, msg->msg_iovlen); - /* Originally we shortcircuited here if res == 0. - Allow 0 bytes buffer. This is valid in POSIX and handled in - fhandler_socket::recv_internal. If we shortcircuit, we fail - to deliver valid error conditions and peer address. */ - if (res >= 0) - res = fh->recvmsg (msg, flags); + fhandler_socket *fh = get (fd); + if (fh) + { + res = check_iovec_for_read (msg->msg_iov, msg->msg_iovlen); + /* Originally we shortcircuited here if res == 0. + Allow 0 bytes buffer. This is valid in POSIX and handled in + fhandler_socket::recv_internal. If we shortcircuit, we fail + to deliver valid error conditions and peer address. */ + if (res >= 0) + res = fh->recvmsg (msg, flags); + } } - + __except (EFAULT) + { + res = -1; + } + __endtry syscall_printf ("%lR = recvmsg(%d, %p, %y)", res, fd, msg, flags); return res; } @@ -2721,22 +2785,25 @@ cygwin_recvmsg (int fd, struct msghdr *msg, int flags) extern "C" ssize_t cygwin_sendmsg (int fd, const struct msghdr *msg, int flags) { - ssize_t res; + ssize_t res = -1; pthread_testcancel (); - fhandler_socket *fh = get (fd); - - myfault efault; - if (efault.faulted (EFAULT) || !fh) - res = -1; - else + __try { - res = check_iovec_for_write (msg->msg_iov, msg->msg_iovlen); - if (res >= 0) - res = fh->sendmsg (msg, flags); + fhandler_socket *fh = get (fd); + if (fh) + { + res = check_iovec_for_write (msg->msg_iov, msg->msg_iovlen); + if (res >= 0) + res = fh->sendmsg (msg, flags); + } } - + __except (EFAULT) + { + res = -1; + } + __endtry syscall_printf ("%lR = sendmsg(%d, %p, %y)", res, fd, msg, flags); return res; } @@ -3111,10 +3178,6 @@ cygwin_freeaddrinfo (struct addrinfo *addr) { struct addrinfo *ai, *ainext; - myfault efault; - if (efault.faulted (EFAULT)) - return; - for (ai = addr; ai != NULL; ai = ainext) { if (ai->ai_addr != NULL) @@ -3290,9 +3353,8 @@ extern "C" int cygwin_getaddrinfo (const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { - myfault efault; - if (efault.faulted (EFAULT)) - return EAI_SYSTEM; + int ret = 0; + /* Windows' getaddrinfo implementations lets all possible values in ai_flags slip through and just ignores unknown values. So we check manually here. */ @@ -3303,140 +3365,148 @@ cygwin_getaddrinfo (const char *hostname, const char *servname, #ifndef AI_DISABLE_IDN_ENCODING #define AI_DISABLE_IDN_ENCODING 0x80000 #endif - if (hints && (hints->ai_flags - & ~(AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_ALL - | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED - | AI_IDN_MASK))) - return EAI_BADFLAGS; - /* AI_NUMERICSERV is not supported prior to Windows Vista. We just check - the servname parameter by ourselves here. */ - if (hints && (hints->ai_flags & AI_NUMERICSERV)) + __try { - char *p; - if (servname && *servname && (strtoul (servname, &p, 10), *p)) - return EAI_NONAME; - } - - int idn_flags = hints ? (hints->ai_flags & AI_IDN_MASK) : 0; - const char *src; - mbstate_t ps; - tmp_pathbuf tp; - wchar_t *whost = NULL, *wserv = NULL; - struct addrinfoW whints, *wres; - - if (hostname) - { - memset (&ps, 0, sizeof ps); - src = hostname; - whost = tp.w_get (); - if (mbsrtowcs (whost, &src, NT_MAX_PATH, &ps) == (size_t) -1) - return EAI_IDN_ENCODE; - if (src) - return EAI_MEMORY; - if (idn_flags & AI_IDN) + if (hints && (hints->ai_flags + & ~(AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_ALL + | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED + | AI_IDN_MASK))) + return EAI_BADFLAGS; + /* AI_NUMERICSERV is not supported prior to Windows Vista. We just check + the servname parameter by ourselves here. */ + if (hints && (hints->ai_flags & AI_NUMERICSERV)) { - /* Map flags to equivalent IDN_* flags. */ - wchar_t *ascbuf = tp.w_get (); - if (IdnToAscii (idn_flags >> 16, whost, -1, ascbuf, NT_MAX_PATH)) - whost = ascbuf; - else if (GetLastError () != ERROR_PROC_NOT_FOUND) + char *p; + if (servname && *servname && (strtoul (servname, &p, 10), *p)) + return EAI_NONAME; + } + + int idn_flags = hints ? (hints->ai_flags & AI_IDN_MASK) : 0; + const char *src; + mbstate_t ps; + tmp_pathbuf tp; + wchar_t *whost = NULL, *wserv = NULL; + struct addrinfoW whints, *wres; + + if (hostname) + { + memset (&ps, 0, sizeof ps); + src = hostname; + whost = tp.w_get (); + if (mbsrtowcs (whost, &src, NT_MAX_PATH, &ps) == (size_t) -1) return EAI_IDN_ENCODE; + if (src) + return EAI_MEMORY; + if (idn_flags & AI_IDN) + { + /* Map flags to equivalent IDN_* flags. */ + wchar_t *ascbuf = tp.w_get (); + if (IdnToAscii (idn_flags >> 16, whost, -1, ascbuf, NT_MAX_PATH)) + whost = ascbuf; + else if (GetLastError () != ERROR_PROC_NOT_FOUND) + return EAI_IDN_ENCODE; + } } - } - if (servname) - { - memset (&ps, 0, sizeof ps); - src = servname; - wserv = tp.w_get (); - if (mbsrtowcs (wserv, &src, NT_MAX_PATH, &ps) == (size_t) -1) - return EAI_IDN_ENCODE; - if (src) - return EAI_MEMORY; - } - - if (!hints) - { - /* Default settings per glibc man page. */ - memset (&whints, 0, sizeof whints); - whints.ai_family = PF_UNSPEC; - whints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; - } - else - { - /* sizeof addrinfo == sizeof addrinfoW */ - memcpy (&whints, hints, sizeof whints); - whints.ai_flags &= ~AI_IDN_MASK; -#ifdef __x86_64__ - /* ai_addrlen is socklen_t (4 bytes) in POSIX but size_t (8 bytes) in - Winsock. Sert upper 4 bytes explicitely to 0 to avoid EAI_FAIL. */ - whints.ai_addrlen &= UINT32_MAX; -#endif - /* AI_ADDRCONFIG is not supported prior to Vista. Rather it's - the default and only possible setting. - On Vista, the default behaviour is as if AI_ADDRCONFIG is set, - apparently for performance reasons. To get the POSIX default - behaviour, the AI_ALL flag has to be set. */ - if (wincap.supports_all_posix_ai_flags () - && whints.ai_family == PF_UNSPEC - && !(whints.ai_flags & AI_ADDRCONFIG)) - whints.ai_flags |= AI_ALL; - } - /* Disable automatic IDN conversion on W8 and later. */ - whints.ai_flags |= AI_DISABLE_IDN_ENCODING; - int ret = w32_to_gai_err (GetAddrInfoW (whost, wserv, &whints, &wres)); - /* Always copy over to self-allocated memory. */ - if (!ret) - { - *res = ga_duplist (wres, false, idn_flags, ret); - FreeAddrInfoW (wres); - if (!*res) - return ret; - } - /* AI_V4MAPPED and AI_ALL are not supported prior to Vista. So, what - we do here is to emulate AI_V4MAPPED. If no IPv6 addresses are - returned, or the AI_ALL flag is set, we try with AF_INET again, and - convert the returned IPv4 addresses into v4-in-v6 entries. This - is done in ga_dup if the v4mapped flag is set. */ - if (!wincap.supports_all_posix_ai_flags () - && hints - && hints->ai_family == AF_INET6 - && (hints->ai_flags & AI_V4MAPPED) - && (ret == EAI_NODATA || ret == EAI_NONAME - || (hints->ai_flags & AI_ALL))) - { - /* sizeof addrinfo == sizeof addrinfoW */ - memcpy (&whints, hints, sizeof whints); - whints.ai_family = AF_INET; -#ifdef __x86_64__ - /* ai_addrlen is socklen_t (4 bytes) in POSIX but size_t (8 bytes) in - Winsock. Sert upper 4 bytes explicitely to 0 to avoid EAI_FAIL. */ - whints.ai_addrlen &= UINT32_MAX; -#endif - int ret2 = w32_to_gai_err (GetAddrInfoW (whost, wserv, &whints, &wres)); - if (!ret2) + if (servname) { - struct addrinfo *v4res = ga_duplist (wres, true, idn_flags, ret); + memset (&ps, 0, sizeof ps); + src = servname; + wserv = tp.w_get (); + if (mbsrtowcs (wserv, &src, NT_MAX_PATH, &ps) == (size_t) -1) + return EAI_IDN_ENCODE; + if (src) + return EAI_MEMORY; + } + + if (!hints) + { + /* Default settings per glibc man page. */ + memset (&whints, 0, sizeof whints); + whints.ai_family = PF_UNSPEC; + whints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + } + else + { + /* sizeof addrinfo == sizeof addrinfoW */ + memcpy (&whints, hints, sizeof whints); + whints.ai_flags &= ~AI_IDN_MASK; +#ifdef __x86_64__ + /* ai_addrlen is socklen_t (4 bytes) in POSIX but size_t (8 bytes) in + Winsock. Sert upper 4 bytes explicitely to 0 to avoid EAI_FAIL. */ + whints.ai_addrlen &= UINT32_MAX; +#endif + /* AI_ADDRCONFIG is not supported prior to Vista. Rather it's + the default and only possible setting. + On Vista, the default behaviour is as if AI_ADDRCONFIG is set, + apparently for performance reasons. To get the POSIX default + behaviour, the AI_ALL flag has to be set. */ + if (wincap.supports_all_posix_ai_flags () + && whints.ai_family == PF_UNSPEC + && !(whints.ai_flags & AI_ADDRCONFIG)) + whints.ai_flags |= AI_ALL; + } + /* Disable automatic IDN conversion on W8 and later. */ + whints.ai_flags |= AI_DISABLE_IDN_ENCODING; + ret = w32_to_gai_err (GetAddrInfoW (whost, wserv, &whints, &wres)); + /* Always copy over to self-allocated memory. */ + if (!ret) + { + *res = ga_duplist (wres, false, idn_flags, ret); FreeAddrInfoW (wres); - if (!v4res) + if (!*res) + __leave; + } + /* AI_V4MAPPED and AI_ALL are not supported prior to Vista. So, what + we do here is to emulate AI_V4MAPPED. If no IPv6 addresses are + returned, or the AI_ALL flag is set, we try with AF_INET again, and + convert the returned IPv4 addresses into v4-in-v6 entries. This + is done in ga_dup if the v4mapped flag is set. */ + if (!wincap.supports_all_posix_ai_flags () + && hints + && hints->ai_family == AF_INET6 + && (hints->ai_flags & AI_V4MAPPED) + && (ret == EAI_NODATA || ret == EAI_NONAME + || (hints->ai_flags & AI_ALL))) + { + /* sizeof addrinfo == sizeof addrinfoW */ + memcpy (&whints, hints, sizeof whints); + whints.ai_family = AF_INET; +#ifdef __x86_64__ + /* ai_addrlen is socklen_t (4 bytes) in POSIX but size_t (8 bytes) in + Winsock. Sert upper 4 bytes explicitely to 0 to avoid EAI_FAIL. */ + whints.ai_addrlen &= UINT32_MAX; +#endif + int ret2 = w32_to_gai_err (GetAddrInfoW (whost, wserv, &whints, &wres)); + if (!ret2) { + struct addrinfo *v4res = ga_duplist (wres, true, idn_flags, ret); + FreeAddrInfoW (wres); + if (!v4res) + { + if (!ret) + cygwin_freeaddrinfo (*res); + __leave; + } + /* If a list of v6 addresses exists, append the v4-in-v6 address + list. Otherwise just return the v4-in-v6 address list. */ if (!ret) - cygwin_freeaddrinfo (*res); - return ret; + { + struct addrinfo *ptr; + for (ptr = *res; ptr->ai_next; ptr = ptr->ai_next) + ; + ptr->ai_next = v4res; + } + else + *res = v4res; + ret = 0; } - /* If a list of v6 addresses exists, append the v4-in-v6 address - list. Otherwise just return the v4-in-v6 address list. */ - if (!ret) - { - struct addrinfo *ptr; - for (ptr = *res; ptr->ai_next; ptr = ptr->ai_next) - ; - ptr->ai_next = v4res; - } - else - *res = v4res; - ret = 0; } } + __except (EFAULT) + { + ret = EAI_SYSTEM; + } + __endtry return ret; } @@ -3445,89 +3515,98 @@ cygwin_getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { - myfault efault; - if (efault.faulted (EFAULT)) - return EAI_SYSTEM; + int ret = 0; - /* When the incoming port number does not resolve to a well-known service, - WinSock's getnameinfo up to Windows 2003 returns with error WSANO_DATA - instead of setting `serv' to the numeric port number string, as required - by RFC 3493. This is fixed on Vista and later. To avoid the error on - systems up to Windows 2003, we check if the port number resolves - to a well-known service. If not, we set the NI_NUMERICSERV flag. */ - if (!wincap.supports_all_posix_ai_flags ()) + __try { - int port = 0; - - switch (sa->sa_family) + /* When the incoming port number does not resolve to a well-known service, + WinSock's getnameinfo up to Windows 2003 returns with error WSANO_DATA + instead of setting `serv' to the numeric port number string, as + required by RFC 3493. This is fixed on Vista and later. To avoid the + error on systems up to Windows 2003, we check if the port number + resolves to a well-known service. If not, we set the NI_NUMERICSERV + flag. */ + if (!wincap.supports_all_posix_ai_flags ()) { - case AF_INET: - port = ((struct sockaddr_in *) sa)->sin_port; - break; - case AF_INET6: - port = ((struct sockaddr_in6 *) sa)->sin6_port; - break; + int port = 0; + + switch (sa->sa_family) + { + case AF_INET: + port = ((struct sockaddr_in *) sa)->sin_port; + break; + case AF_INET6: + port = ((struct sockaddr_in6 *) sa)->sin6_port; + break; + default: + return EAI_FAMILY; + } + if (!port || !getservbyport (port, flags & NI_DGRAM ? "udp" : "tcp")) + flags |= NI_NUMERICSERV; } - if (!port || !getservbyport (port, flags & NI_DGRAM ? "udp" : "tcp")) - flags |= NI_NUMERICSERV; - } - /* We call GetNameInfoW with local buffers and convert to locale - charset to allow RFC 3490 IDNs like glibc 2.3.4 and later. */ + /* We call GetNameInfoW with local buffers and convert to locale + charset to allow RFC 3490 IDNs like glibc 2.3.4 and later. */ #define NI_IDN_MASK (NI_IDN | \ NI_IDN_ALLOW_UNASSIGNED | \ NI_IDN_USE_STD3_ASCII_RULES) - int idn_flags = flags & NI_IDN_MASK; - flags &= ~NI_IDN_MASK; - tmp_pathbuf tp; - wchar_t *whost = NULL, *wserv = NULL; - DWORD whlen = 0, wslen = 0; + int idn_flags = flags & NI_IDN_MASK; + flags &= ~NI_IDN_MASK; + tmp_pathbuf tp; + wchar_t *whost = NULL, *wserv = NULL; + DWORD whlen = 0, wslen = 0; - if (host && hostlen) - { - whost = tp.w_get (); - whlen = NT_MAX_PATH; - } - if (serv && servlen) - { - wserv = tp.w_get (); - wslen = NT_MAX_PATH; - } + if (host && hostlen) + { + whost = tp.w_get (); + whlen = NT_MAX_PATH; + } + if (serv && servlen) + { + wserv = tp.w_get (); + wslen = NT_MAX_PATH; + } - int ret = w32_to_gai_err (GetNameInfoW (sa, salen, whost, whlen, + ret = w32_to_gai_err (GetNameInfoW (sa, salen, whost, whlen, wserv, wslen, flags)); - if (!ret) - { - const wchar_t *src; + if (!ret) + { + const wchar_t *src; - if (whost) - { - if (idn_flags & NI_IDN) + if (whost) { - /* Map flags to equivalent IDN_* flags. */ - wchar_t *idnbuf = tp.w_get (); - if (IdnToUnicode (idn_flags >> 16, whost, -1, - idnbuf, NT_MAX_PATH)) - whost = idnbuf; - else if (GetLastError () != ERROR_PROC_NOT_FOUND) + if (idn_flags & NI_IDN) + { + /* Map flags to equivalent IDN_* flags. */ + wchar_t *idnbuf = tp.w_get (); + if (IdnToUnicode (idn_flags >> 16, whost, -1, + idnbuf, NT_MAX_PATH)) + whost = idnbuf; + else if (GetLastError () != ERROR_PROC_NOT_FOUND) + return EAI_IDN_ENCODE; + } + src = whost; + if (wcsrtombs (host, &src, hostlen, NULL) == (size_t) -1) return EAI_IDN_ENCODE; + if (src) + return EAI_OVERFLOW; + } + if (wserv) + { + src = wserv; + if (wcsrtombs (serv, &src, servlen, NULL) == (size_t) -1) + return EAI_IDN_ENCODE; + if (src) + return EAI_OVERFLOW; } - src = whost; - if (wcsrtombs (host, &src, hostlen, NULL) == (size_t) -1) - return EAI_IDN_ENCODE; - if (src) - return EAI_OVERFLOW; - } - if (wserv) - { - src = wserv; - if (wcsrtombs (serv, &src, servlen, NULL) == (size_t) -1) - return EAI_IDN_ENCODE; - if (src) - return EAI_OVERFLOW; } + else if (ret == EAI_SYSTEM) + set_winsock_errno (); } - else if (ret == EAI_SYSTEM) - set_winsock_errno (); + __except (EFAULT) + { + ret = EAI_SYSTEM; + } + __endtry return ret; } diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 89752cb46..7d8ccbec3 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -1,7 +1,7 @@ /* ntdll.h. Contains ntdll specific stuff not defined elsewhere. Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012, 2013 Red Hat, Inc. + 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -143,6 +143,19 @@ #define HEAP_FLAG_EXECUTABLE 0x40000 #define HEAP_FLAG_DEBUGGED 0x40000000 +#define FILE_VC_QUOTA_NONE 0x00000000 +#define FILE_VC_QUOTA_TRACK 0x00000001 +#define FILE_VC_QUOTA_ENFORCE 0x00000002 +#define FILE_VC_QUOTA_MASK 0x00000003 +#define FILE_VC_CONTENT_INDEX_DISABLED 0x00000008 +#define FILE_VC_LOG_QUOTA_THRESHOLD 0x00000010 +#define FILE_VC_LOG_QUOTA_LIMIT 0x00000020 +#define FILE_VC_LOG_VOLUME_THRESHOLD 0x00000040 +#define FILE_VC_LOG_VOLUME_LIMIT 0x00000080 +#define FILE_VC_QUOTAS_INCOMPLETE 0x00000100 +#define FILE_VC_QUOTAS_REBUILDING 0x00000200 +#define FILE_VC_VALID_MASK 0x000003ff + /* IOCTL code to impersonate client of named pipe. */ #define FSCTL_PIPE_IMPERSONATE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 7, \ METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -999,6 +1012,16 @@ typedef struct _FILE_FS_SIZE_INFORMATION ULONG BytesPerSector; } FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION; +/* Checked on 64 bit. */ +typedef struct _FILE_FS_CONTROL_INFORMATION { + LARGE_INTEGER FreeSpaceStartFiltering; + LARGE_INTEGER FreeSpaceThreshold; + LARGE_INTEGER FreeSpaceStopFiltering; + LARGE_INTEGER DefaultQuotaThreshold; + LARGE_INTEGER DefaultQuotaLimit; + ULONG FileSystemControlFlags; +} FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION; + /* Checked on 64 bit. */ typedef struct _FILE_FS_FULL_SIZE_INFORMATION { @@ -1066,6 +1089,24 @@ typedef struct _DIRECTORY_BASIC_INFORMATION UNICODE_STRING ObjectTypeName; } DIRECTORY_BASIC_INFORMATION, *PDIRECTORY_BASIC_INFORMATION; +/* Checked on 64 bit. */ +typedef struct _FILE_GET_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + SID Sid; +} FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; + +/* Checked on 64 bit. */ +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + /* Checked on 64 bit. */ typedef struct _FILE_GET_EA_INFORMATION { @@ -1180,8 +1221,19 @@ typedef enum _SECTION_INHERIT typedef VOID (APIENTRY *PTIMER_APC_ROUTINE)(PVOID, ULONG, ULONG); -/* Function declarations for ntdll.dll. These don't appear in any - standard Win32 header. */ +#ifdef __x86_64__ +typedef struct _SCOPE_TABLE +{ + ULONG Count; + struct + { + ULONG BeginAddress; + ULONG EndAddress; + ULONG HandlerAddress; + ULONG JumpTarget; + } ScopeRecord[1]; +} SCOPE_TABLE, *PSCOPE_TABLE; +#endif #ifdef __cplusplus /* This is the mapping of the KUSER_SHARED_DATA structure into the user @@ -1190,6 +1242,9 @@ typedef VOID (APIENTRY *PTIMER_APC_ROUTINE)(PVOID, ULONG, ULONG); static volatile KUSER_SHARED_DATA &SharedUserData = *(volatile KUSER_SHARED_DATA *) 0x7ffe0000; +/* Function declarations for ntdll.dll. These don't appear in any + standard Win32 header. */ + extern "C" { #endif @@ -1287,8 +1342,11 @@ extern "C" ULONG, PULONG); NTSTATUS NTAPI NtQueryInformationToken (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG); - NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, VOID *, - ULONG, ULONG *); + NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, + PULONG); + NTSTATUS NTAPI NtQueryQuotaInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, BOOLEAN, PVOID, ULONG, + PSID, BOOLEAN); NTSTATUS NTAPI NtQuerySemaphore (HANDLE, SEMAPHORE_INFORMATION_CLASS, PVOID, ULONG, PULONG); NTSTATUS NTAPI NtQuerySystemInformation (SYSTEM_INFORMATION_CLASS, @@ -1305,9 +1363,8 @@ extern "C" PULONG); NTSTATUS NTAPI NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); - NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, IO_STATUS_BLOCK *, - VOID *, ULONG, - FS_INFORMATION_CLASS); + NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, FS_INFORMATION_CLASS); NTSTATUS NTAPI NtReadFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG); @@ -1319,6 +1376,8 @@ extern "C" NTSTATUS NTAPI NtSetInformationThread (HANDLE, THREADINFOCLASS, PVOID, ULONG); NTSTATUS NTAPI NtSetInformationToken (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG); + NTSTATUS NTAPI NtSetQuotaInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG); NTSTATUS NTAPI NtSetSecurityObject (HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); NTSTATUS NTAPI NtSetTimer (HANDLE, PLARGE_INTEGER, PTIMER_APC_ROUTINE, PVOID, @@ -1326,6 +1385,8 @@ extern "C" NTSTATUS NTAPI NtSetTimerResolution (ULONG, BOOLEAN, PULONG); NTSTATUS NTAPI NtSetValueKey (HANDLE, PUNICODE_STRING, ULONG, ULONG, PVOID, ULONG); + NTSTATUS NTAPI NtSetVolumeInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, FS_INFORMATION_CLASS); NTSTATUS NTAPI NtUnlockFile (HANDLE, PIO_STATUS_BLOCK, PLARGE_INTEGER, PLARGE_INTEGER, ULONG); NTSTATUS NTAPI NtUnlockVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG); diff --git a/winsup/cygwin/ntea.cc b/winsup/cygwin/ntea.cc index 2fb205a04..9e18fa77d 100644 --- a/winsup/cygwin/ntea.cc +++ b/winsup/cygwin/ntea.cc @@ -60,169 +60,173 @@ read_ea (HANDLE hdl, path_conv &pc, const char *name, char *value, size_t size) EA in the file is returned twice. */ char lastname[MAX_EA_NAME_LEN]; - myfault efault; - if (efault.faulted (EFAULT)) - goto out; - - pc.get_object_attr (attr, sec_none_nih); - - debug_printf ("read_ea (%S, %s, %p, %lu)", - attr.ObjectName, name, value, size); - - /* Early open if handle is NULL. This allows to return error codes like - ENOENT before we actually check for the correctness of the EA name and - stuff like that. */ - if (!hdl) + __try { - status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io, - FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - goto out; - } - hdl = NULL; - } + pc.get_object_attr (attr, sec_none_nih); - fea = (PFILE_FULL_EA_INFORMATION) tp.w_get (); + debug_printf ("read_ea (%S, %s, %p, %lu)", + attr.ObjectName, name, value, size); - if (name) - { - size_t nlen; - - /* For compatibility with Linux, we only allow user xattrs and - return ENOTSUP otherwise. */ - if (ascii_strncasematch (name, "user.", 5)) - name += 5; - else + /* Early open if handle is NULL. This allows to return error codes like + ENOENT before we actually check for the correctness of the EA name and + stuff like that. */ + if (!hdl) { - set_errno (ENOTSUP); - goto out; - } - - if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN) - { - set_errno (EINVAL); - return -1; - } - glen = sizeof (FILE_GET_EA_INFORMATION) + nlen; - gea = (PFILE_GET_EA_INFORMATION) alloca (glen); - - gea->NextEntryOffset = 0; - gea->EaNameLength = nlen; - strcpy (gea->EaName, name); - } - - while (true) - { - if (h) - { - status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, gea, glen, - NULL, TRUE); - if (status != STATUS_ACCESS_DENIED || !hdl) - break; - } - status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io, - FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) - break; - hdl = NULL; - } - if (!NT_SUCCESS (status)) - { - switch (status) - { - case STATUS_NO_EAS_ON_FILE: - ret = 0; - break; - case STATUS_INVALID_DEVICE_REQUEST: - set_errno (ENOTSUP); - break; - case STATUS_NOT_FOUND: - /* STATUS_NOT_FOUND is returned when calling NtQueryEaFile on NFS. - In theory this should mean that the file just has no EAs, but in - fact NFS doesn't support EAs, other than the EAs which are used - for NFS requests. We're playing safe and convert STATUS_NOT_FOUND - to ENOATTR, unless we're on NFS, where we convert it to ENOTSUP. */ - set_errno (pc.fs_is_nfs () ? ENOTSUP : ENOATTR); - break; - case STATUS_NONEXISTENT_EA_ENTRY: - /* Actually STATUS_NONEXISTENT_EA_ENTRY is either never generated, or - it was only generated in some old and long forgotton NT version. - See below. For safty reasons, we handle it here, nevertheless. */ - set_errno (ENOATTR); - break; - default: - __seterrno_from_nt_status (status); - break; - } - goto out; - } - if (name) - { - /* Another weird behaviour of NtQueryEaFile. If you ask for a - specific EA which is not present in the file's EA list, you don't - get a useful error code like STATUS_NONEXISTENT_EA_ENTRY. Rather - NtQueryEaFile returns success with the entry's EaValueLength - set to 0. */ - if (!fea->EaValueLength) - { - set_errno (ENOATTR); - goto out; - } - if (size > 0) - { - if (size < fea->EaValueLength) + status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) { - set_errno (ERANGE); - goto out; + __seterrno_from_nt_status (status); + __leave; } - memcpy (value, fea->EaName + fea->EaNameLength + 1, - fea->EaValueLength); } - ret = fea->EaValueLength; - } - else - { - ret = 0; - do + + fea = (PFILE_FULL_EA_INFORMATION) tp.w_get (); + + if (name) { - fea->EaNameLength += 5; /* "user." */ + size_t nlen; + + /* For compatibility with Linux, we only allow user xattrs and + return ENOTSUP otherwise. */ + if (ascii_strncasematch (name, "user.", 5)) + name += 5; + else + { + set_errno (ENOTSUP); + __leave; + } + + if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN) + { + set_errno (EINVAL); + __leave; + } + glen = sizeof (FILE_GET_EA_INFORMATION) + nlen; + gea = (PFILE_GET_EA_INFORMATION) alloca (glen); + + gea->NextEntryOffset = 0; + gea->EaNameLength = nlen; + strcpy (gea->EaName, name); + } + + while (true) + { + if (h) + { + status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, gea, glen, + NULL, TRUE); + if (status != STATUS_ACCESS_DENIED || !hdl) + break; + pc.init_reopen_attr (attr, h); + } + status = NtOpenFile (&h, READ_CONTROL | FILE_READ_EA, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + break; + hdl = NULL; + } + if (!NT_SUCCESS (status)) + { + switch (status) + { + case STATUS_NO_EAS_ON_FILE: + ret = 0; + break; + case STATUS_INVALID_DEVICE_REQUEST: + set_errno (ENOTSUP); + break; + case STATUS_NOT_FOUND: + /* STATUS_NOT_FOUND is returned when calling NtQueryEaFile on NFS. + In theory this should mean that the file just has no EAs, but + in fact NFS doesn't support EAs, other than the EAs which are + used for NFS requests. We're playing safe and convert + STATUS_NOT_FOUND to ENOATTR, unless we're on NFS, where we + convert it to ENOTSUP. */ + set_errno (pc.fs_is_nfs () ? ENOTSUP : ENOATTR); + break; + case STATUS_NONEXISTENT_EA_ENTRY: + /* Actually STATUS_NONEXISTENT_EA_ENTRY is either never generated, + or it was only generated in some old and long forgotton NT + version. See below. For safty reasons, we handle it here, + nevertheless. */ + set_errno (ENOATTR); + break; + default: + __seterrno_from_nt_status (status); + break; + } + __leave; + } + if (name) + { + /* Another weird behaviour of NtQueryEaFile. If you ask for a + specific EA which is not present in the file's EA list, you don't + get a useful error code like STATUS_NONEXISTENT_EA_ENTRY. Rather + NtQueryEaFile returns success with the entry's EaValueLength + set to 0. */ + if (!fea->EaValueLength) + { + set_errno (ENOATTR); + __leave; + } if (size > 0) { - if ((size_t) ret + fea->EaNameLength + 1 > size) + if (size < fea->EaValueLength) { set_errno (ERANGE); - goto out; + __leave; } - /* For compatibility with Linux, we always prepend "user." to - the attribute name, so effectively we only support user - attributes from a application point of view. */ - char tmpbuf[MAX_EA_NAME_LEN * 2]; - char *tp = stpcpy (tmpbuf, "user."); - stpcpy (tp, fea->EaName); - /* NTFS stores all EA names in uppercase unfortunately. To keep - compatibility with ext/xfs EA namespaces and accompanying - tools, which expect the namespaces to be lower case, we return - EA names in lowercase if the file is on a native NTFS. */ - if (pc.fs_is_ntfs ()) - strlwr (tp); - tp = stpcpy (value, tmpbuf) + 1; - ret += tp - value; - value = tp; + memcpy (value, fea->EaName + fea->EaNameLength + 1, + fea->EaValueLength); } - else - ret += fea->EaNameLength + 1; - strcpy (lastname, fea->EaName); - status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, NULL, 0, - NULL, FALSE); + ret = fea->EaValueLength; + } + else + { + ret = 0; + do + { + fea->EaNameLength += 5; /* "user." */ + if (size > 0) + { + if ((size_t) ret + fea->EaNameLength + 1 > size) + { + set_errno (ERANGE); + __leave; + } + /* For compatibility with Linux, we always prepend "user." to + the attribute name, so effectively we only support user + attributes from a application point of view. */ + char tmpbuf[MAX_EA_NAME_LEN * 2]; + char *tp = stpcpy (tmpbuf, "user."); + stpcpy (tp, fea->EaName); + /* NTFS stores all EA names in uppercase unfortunately. To + keep compatibility with ext/xfs EA namespaces and + accompanying tools, which expect the namespaces to be + lower case, we return EA names in lowercase if the file + is on a native NTFS. */ + if (pc.fs_is_ntfs ()) + strlwr (tp); + tp = stpcpy (value, tmpbuf) + 1; + ret += tp - value; + value = tp; + } + else + ret += fea->EaNameLength + 1; + strcpy (lastname, fea->EaName); + status = NtQueryEaFile (h, &io, fea, EA_BUFSIZ, TRUE, NULL, 0, + NULL, FALSE); + } + while (NT_SUCCESS (status) && strcmp (lastname, fea->EaName) != 0); } - while (NT_SUCCESS (status) && strcmp (lastname, fea->EaName) != 0); } - -out: - if (!hdl) - CloseHandle (h); + __except (EFAULT) {} + __endtry + if (!hdl && h) + NtClose (h); debug_printf ("%d = read_ea(%S, %s, %p, %lu)", ret, attr.ObjectName, name, value, size); return ret; @@ -241,122 +245,123 @@ write_ea (HANDLE hdl, path_conv &pc, const char *name, const char *value, ULONG flen; size_t nlen; - myfault efault; - if (efault.faulted (EFAULT)) - goto out; - - pc.get_object_attr (attr, sec_none_nih); - - debug_printf ("write_ea (%S, %s, %p, %lu, %d)", - attr.ObjectName, name, value, size, flags); - - /* Early open if handle is NULL. This allows to return error codes like - ENOENT before we actually check for the correctness of the EA name and - stuff like that. */ - if (!hdl) + __try { - status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io, - FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) + pc.get_object_attr (attr, sec_none_nih); + + debug_printf ("write_ea (%S, %s, %p, %lu, %d)", + attr.ObjectName, name, value, size, flags); + + /* Early open if handle is NULL. This allows to return error codes like + ENOENT before we actually check for the correctness of the EA name and + stuff like that. */ + if (!hdl) { - __seterrno_from_nt_status (status); - goto out; + status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } } - hdl = NULL; - } - /* For compatibility with Linux, we only allow user xattrs and - return ENOTSUP otherwise. */ - if (!ascii_strncasematch (name, "user.", 5)) - { - set_errno (ENOTSUP); - goto out; - } + /* For compatibility with Linux, we only allow user xattrs and + return ENOTSUP otherwise. */ + if (!ascii_strncasematch (name, "user.", 5)) + { + set_errno (ENOTSUP); + __leave; + } - /* removexattr is supposed to fail with ENOATTR if the requested EA is not - available. This is equivalent to the XATTR_REPLACE flag for setxattr. */ - if (!value) - flags = XATTR_REPLACE; + /* removexattr is supposed to fail with ENOATTR if the requested EA is + not available. This is equivalent to XATTR_REPLACE for setxattr. */ + if (!value) + flags = XATTR_REPLACE; - if (flags) - { - if (flags != XATTR_CREATE && flags != XATTR_REPLACE) + if (flags) + { + if (flags != XATTR_CREATE && flags != XATTR_REPLACE) + { + set_errno (EINVAL); + __leave; + } + ssize_t rret = read_ea (hdl, pc, name, NULL, 0); + if (flags == XATTR_CREATE && rret > 0) + { + set_errno (EEXIST); + __leave; + } + if (flags == XATTR_REPLACE && rret < 0) + __leave; + } + + /* Skip "user." prefix. */ + name += 5; + + if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN) { set_errno (EINVAL); - goto out; + __leave; } - ssize_t rret = read_ea (hdl, pc, name, NULL, 0); - if (flags == XATTR_CREATE && rret > 0) + flen = sizeof (FILE_FULL_EA_INFORMATION) + nlen + 1 + size; + fea = (PFILE_FULL_EA_INFORMATION) alloca (flen); + fea->NextEntryOffset = 0; + fea->Flags = 0; + fea->EaNameLength = nlen; + fea->EaValueLength = size; + strcpy (fea->EaName, name); + if (value) + memcpy (fea->EaName + fea->EaNameLength + 1, value, size); + + while (true) { - set_errno (EEXIST); - goto out; - } - if (flags == XATTR_REPLACE && rret < 0) - goto out; - } - - /* Skip "user." prefix. */ - name += 5; - - if ((nlen = strlen (name)) >= MAX_EA_NAME_LEN) - { - set_errno (EINVAL); - goto out; - } - flen = sizeof (FILE_FULL_EA_INFORMATION) + nlen + 1 + size; - fea = (PFILE_FULL_EA_INFORMATION) alloca (flen); - fea->NextEntryOffset = 0; - fea->Flags = 0; - fea->EaNameLength = nlen; - fea->EaValueLength = size; - strcpy (fea->EaName, name); - if (value) - memcpy (fea->EaName + fea->EaNameLength + 1, value, size); - - while (true) - { - if (h) - { - status = NtSetEaFile (h, &io, fea, flen); - if (status != STATUS_ACCESS_DENIED || !hdl) + if (h) + { + status = NtSetEaFile (h, &io, fea, flen); + if (status != STATUS_ACCESS_DENIED || !hdl) + break; + pc.init_reopen_attr (attr, h); + } + status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) break; + hdl = NULL; } - status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_EA, &attr, &io, - FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS (status)) - break; - hdl = NULL; - } - if (!NT_SUCCESS (status)) - { - switch (status) { - case STATUS_EA_TOO_LARGE: - /* STATUS_EA_TOO_LARGE has a matching Win32 error ERROR_EA_TABLE_FULL. - For some unknown reason RtlNtStatusToDosError does not translate - STATUS_EA_TOO_LARGE to ERROR_EA_TABLE_FULL, but instead to - ERROR_EA_LIST_INCONSISTENT. This error code is also returned for - STATUS_EA_LIST_INCONSISTENT, which means the incoming EA list is... - inconsistent. For obvious reasons we translate - ERROR_EA_LIST_INCONSISTENT to EINVAL, so we have to handle - STATUS_EA_TOO_LARGE explicitely here, to get the correct mapping - to ENOSPC. */ - set_errno (ENOSPC); - break; - case STATUS_INVALID_DEVICE_REQUEST: - set_errno (ENOTSUP); - break; - default: - __seterrno_from_nt_status (status); - break; + switch (status) + { + case STATUS_EA_TOO_LARGE: + /* STATUS_EA_TOO_LARGE has a matching Win32 error code + ERROR_EA_TABLE_FULL. For some reason RtlNtStatusToDosError + does not translate STATUS_EA_TOO_LARGE to ERROR_EA_TABLE_FULL, + but instead to ERROR_EA_LIST_INCONSISTENT. This error code is + also returned for STATUS_EA_LIST_INCONSISTENT, which means the + incoming EA list is... inconsistent. For obvious reasons we + translate ERROR_EA_LIST_INCONSISTENT to EINVAL, so we have to + handle STATUS_EA_TOO_LARGE explicitely here, to get the correct + mapping to ENOSPC. */ + set_errno (ENOSPC); + break; + case STATUS_INVALID_DEVICE_REQUEST: + set_errno (ENOTSUP); + break; + default: + __seterrno_from_nt_status (status); + break; + } } + else + ret = 0; } - else - ret = 0; - -out: - if (!hdl) - CloseHandle (h); + __except (EFAULT) {} + __endtry + if (!hdl && h) + NtClose (h); debug_printf ("%d = write_ea(%S, %s, %p, %lu, %d)", ret, attr.ObjectName, name, value, size, flags); return ret; diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 065456251..d8604817a 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -517,10 +517,10 @@ path_conv::get_wide_win32_path (PWCHAR wc) return wc; } -void +static void warn_msdos (const char *src) { - if (user_shared->warned_msdos || !dos_file_warning || !cygwin_finished_initializing) + if (user_shared->warned_msdos || !cygwin_finished_initializing) return; tmp_pathbuf tp; char *posix_path = tp.c_get (); @@ -651,561 +651,574 @@ path_conv::check (const char *src, unsigned opt, } #endif - myfault efault; - if (efault.faulted ()) + __try { - error = EFAULT; - return; - } - int loop = 0; - path_flags = 0; - known_suffix = NULL; - fileattr = INVALID_FILE_ATTRIBUTES; - caseinsensitive = OBJ_CASE_INSENSITIVE; - if (wide_path) - cfree (wide_path); - wide_path = NULL; - if (path) - { - cfree (modifiable_path ()); - path = NULL; - } - close_conv_handle (); - memset (&dev, 0, sizeof (dev)); - fs.clear (); - if (normalized_path) - { - cfree ((void *) normalized_path); - normalized_path = NULL; - } - int component = 0; // Number of translated components - - if (!(opt & PC_NULLEMPTY)) - error = 0; - else if (!*src) - { - error = ENOENT; - return; - } - - bool is_msdos = false; - /* This loop handles symlink expansion. */ - for (;;) - { - MALLOC_CHECK; - assert (src); - - is_relpath = !isabspath (src); - error = normalize_posix_path (src, path_copy, tail); - if (error > 0) - return; - if (error < 0) + int loop = 0; + path_flags = 0; + known_suffix = NULL; + fileattr = INVALID_FILE_ATTRIBUTES; + caseinsensitive = OBJ_CASE_INSENSITIVE; + if (wide_path) + cfree (wide_path); + wide_path = NULL; + if (path) { - if (component == 0) - is_msdos = true; - error = 0; + cfree (modifiable_path ()); + path = NULL; + } + close_conv_handle (); + memset (&dev, 0, sizeof (dev)); + fs.clear (); + if (normalized_path) + { + cfree ((void *) normalized_path); + normalized_path = NULL; + } + int component = 0; // Number of translated components + + if (!(opt & PC_NULLEMPTY)) + error = 0; + else if (!*src) + { + error = ENOENT; + return; } - /* Detect if the user was looking for a directory. We have to strip the - trailing slash initially while trying to add extensions but take it - into account during processing */ - if (tail > path_copy + 2 && isslash (tail[-1])) + bool is_msdos = false; + /* This loop handles symlink expansion. */ + for (;;) { - need_directory = 1; - *--tail = '\0'; - } - path_end = tail; + MALLOC_CHECK; + assert (src); - /* Scan path_copy from right to left looking either for a symlink - or an actual existing file. If an existing file is found, just - return. If a symlink is found, exit the for loop. - Also: be careful to preserve the errno returned from - symlink.check as the caller may need it. */ - /* FIXME: Do we have to worry about multiple \'s here? */ - component = 0; // Number of translated components - sym.contents[0] = '\0'; - - int symlen = 0; - - for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE); - ; - pflags_or = 0) - { - const suffix_info *suff; - char *full_path; - - /* Don't allow symlink.check to set anything in the path_conv - class if we're working on an inner component of the path */ - if (component) - { - suff = NULL; - full_path = pathbuf; - } - else - { - suff = suffixes; - full_path = THIS_path; - } - - /* Convert to native path spec sans symbolic link info. */ - error = mount_table->conv_to_win32_path (path_copy, full_path, dev, - &sym.pflags); - - if (error) + is_relpath = !isabspath (src); + error = normalize_posix_path (src, path_copy, tail); + if (error > 0) return; - - sym.pflags |= pflags_or; - - if (!dev.exists ()) + if (error < 0) { - error = ENXIO; - return; - } - - if (iscygdrive_dev (dev)) - { - if (!component) - fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY; - else - { - fileattr = getfileattr (THIS_path, - sym.pflags & MOUNT_NOPOSIX); - dev = FH_FS; - } - goto out; - } - else if (isdev_dev (dev)) - { - /* Just make sure that the path handling goes on as with FH_FS. */ - } - else if (isvirtual_dev (dev)) - { - /* FIXME: Calling build_fhandler here is not the right way to handle this. */ - fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy); - virtual_ftype_t file_type; - if (!fh) - file_type = virt_none; - else - { - file_type = fh->exists (); - if (file_type == virt_symlink) - { - fh->fill_filebuf (); - symlen = sym.set (fh->get_filebuf ()); - } - delete fh; - } - switch (file_type) - { - case virt_directory: - case virt_rootdir: - if (component == 0) - fileattr = FILE_ATTRIBUTE_DIRECTORY; - break; - case virt_file: - if (component == 0) - fileattr = 0; - break; - case virt_symlink: - goto is_virtual_symlink; - case virt_pipe: - if (component == 0) - { - fileattr = 0; - dev.parse (FH_PIPE); - } - break; - case virt_socket: - if (component == 0) - { - fileattr = 0; - dev.parse (FH_TCP); - } - break; - case virt_fsdir: - case virt_fsfile: - /* Access to real file or directory via block device - entry in /proc/sys. Convert to real file and go with - the flow. */ - dev.parse (FH_FS); - goto is_fs_via_procsys; - case virt_blk: - /* Block special device. If the trailing slash has been - requested, the target is the root directory of the - filesystem on this block device. So we convert this to - a real file and attach the backslash. */ - if (component == 0 && need_directory) - { - dev.parse (FH_FS); - strcat (full_path, "\\"); - fileattr = FILE_ATTRIBUTE_DIRECTORY - | FILE_ATTRIBUTE_DEVICE; - goto out; - } - /*FALLTHRU*/ - case virt_chr: - if (component == 0) - fileattr = FILE_ATTRIBUTE_DEVICE; - break; - default: - if (component == 0) - fileattr = INVALID_FILE_ATTRIBUTES; - goto virtual_component_retry; - } - if (component == 0 || dev != FH_NETDRIVE) - path_flags |= PATH_RO; - goto out; - } - /* devn should not be a device. If it is, then stop parsing now. */ - else if (dev != FH_FS) - { - fileattr = 0; - path_flags = sym.pflags; - if (component) - { - error = ENOTDIR; - return; - } - goto out; /* Found a device. Stop parsing. */ - } - - /* If path is only a drivename, Windows interprets it as the - current working directory on this drive instead of the root - dir which is what we want. So we need the trailing backslash - in this case. */ - if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') - { - full_path[2] = '\\'; - full_path[3] = '\0'; - } - - /* If the incoming path was given in DOS notation, always treat - it as caseinsensitive,noacl path. This must be set before - calling sym.check, otherwise the path is potentially treated - casesensitive. */ - if (is_msdos) - sym.pflags |= PATH_NOPOSIX | PATH_NOACL; - -is_fs_via_procsys: - - symlen = sym.check (full_path, suff, fs, conv_handle); - -is_virtual_symlink: - - if (sym.isdevice) - { - if (component) - { - error = ENOTDIR; - return; - } - dev.parse (sym.major, sym.minor); - dev.setfs (1); - dev.mode = sym.mode; - fileattr = sym.fileattr; - goto out; - } - - if (sym.pflags & PATH_SOCKET) - { - if (component) - { - error = ENOTDIR; - return; - } - fileattr = sym.fileattr; - dev.parse (FH_UNIX); - dev.setfs (1); - goto out; - } - - if (!component) - { - /* Make sure that /dev always exists. */ - fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY - : sym.fileattr; - path_flags = sym.pflags; - } - else if (isdev_dev (dev)) - { - /* If we're looking for a file below /dev, which doesn't exist, - make sure that the device type is converted to FH_FS, so that - subsequent code handles the file correctly. - Unless /dev itself doesn't exist on disk. In that case /dev - is handled as virtual filesystem, and virtual filesystems are - read-only. The PC_KEEP_HANDLE check allows to check for - a call from an informational system call. In that case we - just stick to ENOENT, and the device type doesn't matter - anyway. */ - if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE)) - sym.error = EROFS; - else - dev = FH_FS; - } - - /* If symlink.check found an existing non-symlink file, then - it sets the appropriate flag. It also sets any suffix found - into `ext_here'. */ - if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES) - { - error = sym.error; if (component == 0) - add_ext = true; - else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY)) + is_msdos = true; + error = 0; + } + + /* Detect if the user was looking for a directory. We have to strip + the trailing slash initially while trying to add extensions but + take it into account during processing */ + if (tail > path_copy + 2 && isslash (tail[-1])) + { + need_directory = 1; + *--tail = '\0'; + } + path_end = tail; + + /* Scan path_copy from right to left looking either for a symlink + or an actual existing file. If an existing file is found, just + return. If a symlink is found, exit the for loop. + Also: be careful to preserve the errno returned from + symlink.check as the caller may need it. */ + /* FIXME: Do we have to worry about multiple \'s here? */ + component = 0; // Number of translated components + sym.contents[0] = '\0'; + + int symlen = 0; + + for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE); + ; + pflags_or = 0) + { + const suffix_info *suff; + char *full_path; + + /* Don't allow symlink.check to set anything in the path_conv + class if we're working on an inner component of the path */ + if (component) { - error = ENOTDIR; + suff = NULL; + full_path = pathbuf; + } + else + { + suff = suffixes; + full_path = THIS_path; + } + + /* Convert to native path spec sans symbolic link info. */ + error = mount_table->conv_to_win32_path (path_copy, full_path, + dev, &sym.pflags); + + if (error) + return; + + sym.pflags |= pflags_or; + + if (!dev.exists ()) + { + error = ENXIO; + return; + } + + if (iscygdrive_dev (dev)) + { + if (!component) + fileattr = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_READONLY; + else + { + fileattr = getfileattr (THIS_path, + sym.pflags & MOUNT_NOPOSIX); + dev = FH_FS; + } goto out; } - goto out; // file found - } - /* Found a symlink if symlen > 0. If component == 0, then the - src path itself was a symlink. If !follow_mode then - we're done. Otherwise we have to insert the path found - into the full path that we are building and perform all of - these operations again on the newly derived path. */ - else if (symlen > 0) - { - saw_symlinks = 1; - if (component == 0 && !need_directory - && (!(opt & PC_SYM_FOLLOW) - || (is_rep_symlink () && (opt & PC_SYM_NOFOLLOW_REP)))) + else if (isdev_dev (dev)) { - set_symlink (symlen); // last component of path is a symlink. - if (opt & PC_SYM_CONTENTS) + /* Make sure that the path handling goes on as with FH_FS. */ + } + else if (isvirtual_dev (dev)) + { + /* FIXME: Calling build_fhandler here is not the right way to + handle this. */ + fhandler_virtual *fh = (fhandler_virtual *) + build_fh_dev (dev, path_copy); + virtual_ftype_t file_type; + if (!fh) + file_type = virt_none; + else { - strcpy (THIS_path, sym.contents); + file_type = fh->exists (); + if (file_type == virt_symlink) + { + fh->fill_filebuf (); + symlen = sym.set (fh->get_filebuf ()); + } + delete fh; + } + switch (file_type) + { + case virt_directory: + case virt_rootdir: + if (component == 0) + fileattr = FILE_ATTRIBUTE_DIRECTORY; + break; + case virt_file: + if (component == 0) + fileattr = 0; + break; + case virt_symlink: + goto is_virtual_symlink; + case virt_pipe: + if (component == 0) + { + fileattr = 0; + dev.parse (FH_PIPE); + } + break; + case virt_socket: + if (component == 0) + { + fileattr = 0; + dev.parse (FH_TCP); + } + break; + case virt_fsdir: + case virt_fsfile: + /* Access to real file or directory via block device + entry in /proc/sys. Convert to real file and go with + the flow. */ + dev.parse (FH_FS); + goto is_fs_via_procsys; + case virt_blk: + /* Block special device. If the trailing slash has been + requested, the target is the root directory of the + filesystem on this block device. So we convert this + to a real file and attach the backslash. */ + if (component == 0 && need_directory) + { + dev.parse (FH_FS); + strcat (full_path, "\\"); + fileattr = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_DEVICE; + goto out; + } + /*FALLTHRU*/ + case virt_chr: + if (component == 0) + fileattr = FILE_ATTRIBUTE_DEVICE; + break; + default: + if (component == 0) + fileattr = INVALID_FILE_ATTRIBUTES; + goto virtual_component_retry; + } + if (component == 0 || dev != FH_NETDRIVE) + path_flags |= PATH_RO; + goto out; + } + /* devn should not be a device. If it is, then stop parsing. */ + else if (dev != FH_FS) + { + fileattr = 0; + path_flags = sym.pflags; + if (component) + { + error = ENOTDIR; + return; + } + goto out; /* Found a device. Stop parsing. */ + } + + /* If path is only a drivename, Windows interprets it as the + current working directory on this drive instead of the root + dir which is what we want. So we need the trailing backslash + in this case. */ + if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') + { + full_path[2] = '\\'; + full_path[3] = '\0'; + } + + /* If the incoming path was given in DOS notation, always treat + it as caseinsensitive,noacl path. This must be set before + calling sym.check, otherwise the path is potentially treated + casesensitive. */ + if (is_msdos) + sym.pflags |= PATH_NOPOSIX | PATH_NOACL; + + is_fs_via_procsys: + + symlen = sym.check (full_path, suff, fs, conv_handle); + + is_virtual_symlink: + + if (sym.isdevice) + { + if (component) + { + error = ENOTDIR; + return; + } + dev.parse (sym.major, sym.minor); + dev.setfs (1); + dev.mode = sym.mode; + fileattr = sym.fileattr; + goto out; + } + + if (sym.pflags & PATH_SOCKET) + { + if (component) + { + error = ENOTDIR; + return; + } + fileattr = sym.fileattr; + dev.parse (FH_UNIX); + dev.setfs (1); + goto out; + } + + if (!component) + { + /* Make sure that /dev always exists. */ + fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY + : sym.fileattr; + path_flags = sym.pflags; + } + else if (isdev_dev (dev)) + { + /* If we're looking for a non-existing file below /dev, + make sure that the device type is converted to FH_FS, so + that subsequent code handles the file correctly. Unless + /dev itself doesn't exist on disk. In that case /dev + is handled as virtual filesystem, and virtual filesystems + are read-only. The PC_KEEP_HANDLE check allows to check + for a call from an informational system call. In that + case we just stick to ENOENT, and the device type doesn't + matter anyway. */ + if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE)) + sym.error = EROFS; + else + dev = FH_FS; + } + + /* If symlink.check found an existing non-symlink file, then + it sets the appropriate flag. It also sets any suffix found + into `ext_here'. */ + if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES) + { + error = sym.error; + if (component == 0) + add_ext = true; + else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY)) + { + error = ENOTDIR; goto out; } - add_ext = true; + goto out; // file found + } + /* Found a symlink if symlen > 0. If component == 0, then the + src path itself was a symlink. If !follow_mode then + we're done. Otherwise we have to insert the path found + into the full path that we are building and perform all of + these operations again on the newly derived path. */ + else if (symlen > 0) + { + saw_symlinks = 1; + if (component == 0 && !need_directory + && (!(opt & PC_SYM_FOLLOW) + || (is_rep_symlink () + && (opt & PC_SYM_NOFOLLOW_REP)))) + { + /* last component of path is a symlink. */ + set_symlink (symlen); + if (opt & PC_SYM_CONTENTS) + { + strcpy (THIS_path, sym.contents); + goto out; + } + add_ext = true; + goto out; + } + /* Following a symlink we can't trust the collected + filesystem information any longer. */ + fs.clear (); + /* Close handle, if we have any. Otherwise we're collecting + handles while following symlinks. */ + conv_handle.close (); + break; + } + else if (sym.error && sym.error != ENOENT) + { + error = sym.error; goto out; } - /* Following a symlink we can't trust the collected filesystem - information any longer. */ - fs.clear (); - /* Close handle, if we have any. Otherwise we're collecting - handles while following symlinks. */ - conv_handle.close (); - break; + /* No existing file found. */ + + virtual_component_retry: + /* Find the new "tail" of the path, e.g. in '/for/bar/baz', + /baz is the tail. */ + if (tail != path_end) + *tail = '/'; + while (--tail > path_copy + 1 && *tail != '/') {} + /* Exit loop if there is no tail or we are at the + beginning of a UNC path */ + if (tail <= path_copy + 1) + goto out; // all done + + /* Haven't found an existing pathname component yet. + Pinch off the tail and try again. */ + *tail = '\0'; + component++; } - else if (sym.error && sym.error != ENOENT) + + /* Arrive here if above loop detected a symlink. */ + if (++loop > SYMLOOP_MAX) { - error = sym.error; - goto out; - } - /* No existing file found. */ - -virtual_component_retry: - /* Find the new "tail" of the path, e.g. in '/for/bar/baz', - /baz is the tail. */ - if (tail != path_end) - *tail = '/'; - while (--tail > path_copy + 1 && *tail != '/') {} - /* Exit loop if there is no tail or we are at the - beginning of a UNC path */ - if (tail <= path_copy + 1) - goto out; // all done - - /* Haven't found an existing pathname component yet. - Pinch off the tail and try again. */ - *tail = '\0'; - component++; - } - - /* Arrive here if above loop detected a symlink. */ - if (++loop > SYMLOOP_MAX) - { - error = ELOOP; // Eep. - return; - } - - MALLOC_CHECK; - - - /* Place the link content, possibly with head and/or tail, in tmp_buf */ - - char *headptr; - if (isabspath (sym.contents)) - headptr = tmp_buf; /* absolute path */ - else - { - /* Copy the first part of the path (with ending /) and point to the end. */ - char *prevtail = tail; - while (--prevtail > path_copy && *prevtail != '/') {} - int headlen = prevtail - path_copy + 1;; - memcpy (tmp_buf, path_copy, headlen); - headptr = &tmp_buf[headlen]; - } - - /* Make sure there is enough space */ - if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH)) - { - too_long: - error = ENAMETOOLONG; - set_path ("::ENAMETOOLONG::"); - return; - } - - /* Copy the symlink contents to the end of tmp_buf. - Convert slashes. */ - for (char *p = sym.contents; *p; p++) - *headptr++ = *p == '\\' ? '/' : *p; - *headptr = '\0'; - - /* Copy any tail component (with the 0) */ - if (tail++ < path_end) - { - /* Add a slash if needed. There is space. */ - if (*(headptr - 1) != '/') - *headptr++ = '/'; - int taillen = path_end - tail + 1; - if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH)) - goto too_long; - memcpy (headptr, tail, taillen); - } - - /* Evaluate everything all over again. */ - src = tmp_buf; - } - - if (!(opt & PC_SYM_CONTENTS)) - add_ext = true; - -out: - set_path (THIS_path); - if (add_ext) - add_ext_from_sym (sym); - if (dev == FH_NETDRIVE && component) - { - /* This case indicates a non-existant resp. a non-retrievable - share. This happens for instance if the share is a printer. - In this case the path must not be treated like a FH_NETDRIVE, - but like a FH_FS instead, so the usual open call for files - is used on it. */ - dev.parse (FH_FS); - } - else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES) - { - /* FIXME: Usually we don't set error to ENOENT if a file doesn't - exist. This is typically indicated by the fileattr content. - So, why here? The downside is that cygwin_conv_path just gets - an error for these paths so it reports the error back to the - application. Unlike in all other cases of non-existant files, - for which check doesn't set error, so cygwin_conv_path just - returns the path, as intended. */ - error = ENOENT; - return; - } - else if (!need_directory || error) - /* nothing to do */; - else if (fileattr == INVALID_FILE_ATTRIBUTES) - strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */ - else if (fileattr & FILE_ATTRIBUTE_DIRECTORY) - path_flags &= ~PATH_SYMLINK; - else - { - debug_printf ("%s is a non-directory", path); - error = ENOTDIR; - return; - } - - if (dev.isfs ()) - { - if (strncmp (path, "\\\\.\\", 4)) - { - if (!tail || tail == path) - /* nothing */; - else if (tail[-1] != '\\') - *tail = '\0'; - else - { - error = ENOENT; + error = ELOOP; // Eep. return; } - } - /* If FS hasn't been checked already in symlink_info::check, do so now. */ - if (fs.inited ()|| fs.update (get_nt_native_path (), NULL)) - { - /* Incoming DOS paths are treated like DOS paths in native - Windows applications. No ACLs, just default settings. */ - if (is_msdos) - fs.has_acls (false); - debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ()); - /* CV: We could use this->has_acls() but I want to make sure that - we don't forget that the PATH_NOACL flag must be taken into - account here. */ - if (!(path_flags & PATH_NOACL) && fs.has_acls ()) - set_exec (0); /* We really don't know if this is executable or not here - but set it to not executable since it will be figured out - later by anything which cares about this. */ - } - /* If the FS has been found to have unrelibale inodes, note - that in path_flags. */ - if (!fs.hasgood_inode ()) - path_flags |= PATH_IHASH; - /* If the OS is caseinsensitive or the FS is caseinsensitive, - don't handle path casesensitive. */ - if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()) - path_flags |= PATH_NOPOSIX; - caseinsensitive = (path_flags & PATH_NOPOSIX) - ? OBJ_CASE_INSENSITIVE : 0; - if (exec_state () != dont_know_if_executable) - /* ok */; - else if (isdir ()) - set_exec (1); - else if (issymlink () || issocket ()) - set_exec (0); - } + MALLOC_CHECK; - if (opt & PC_NOFULL) - { - if (is_relpath) - { - mkrelpath (this->modifiable_path (), !!caseinsensitive); - /* Invalidate wide_path so that wide relpath can be created - in later calls to get_nt_native_path or get_wide_win32_path. */ - if (wide_path) - cfree (wide_path); - wide_path = NULL; - } - if (need_directory) - { - size_t n = strlen (this->path); - /* Do not add trailing \ to UNC device names like \\.\a: */ - if (this->path[n - 1] != '\\' && - (strncmp (this->path, "\\\\.\\", 4) != 0)) + + /* Place the link content, possibly with head and/or tail, + in tmp_buf */ + + char *headptr; + if (isabspath (sym.contents)) + headptr = tmp_buf; /* absolute path */ + else { - this->modifiable_path ()[n] = '\\'; - this->modifiable_path ()[n + 1] = '\0'; + /* Copy the first part of the path (with ending /) and point to + the end. */ + char *prevtail = tail; + while (--prevtail > path_copy && *prevtail != '/') {} + int headlen = prevtail - path_copy + 1;; + memcpy (tmp_buf, path_copy, headlen); + headptr = &tmp_buf[headlen]; + } + + /* Make sure there is enough space */ + if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH)) + { + too_long: + error = ENAMETOOLONG; + set_path ("::ENAMETOOLONG::"); + return; + } + + /* Copy the symlink contents to the end of tmp_buf. + Convert slashes. */ + for (char *p = sym.contents; *p; p++) + *headptr++ = *p == '\\' ? '/' : *p; + *headptr = '\0'; + + /* Copy any tail component (with the 0) */ + if (tail++ < path_end) + { + /* Add a slash if needed. There is space. */ + if (*(headptr - 1) != '/') + *headptr++ = '/'; + int taillen = path_end - tail + 1; + if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH)) + goto too_long; + memcpy (headptr, tail, taillen); + } + + /* Evaluate everything all over again. */ + src = tmp_buf; + } + + if (!(opt & PC_SYM_CONTENTS)) + add_ext = true; + + out: + set_path (THIS_path); + if (add_ext) + add_ext_from_sym (sym); + if (dev == FH_NETDRIVE && component) + { + /* This case indicates a non-existant resp. a non-retrievable + share. This happens for instance if the share is a printer. + In this case the path must not be treated like a FH_NETDRIVE, + but like a FH_FS instead, so the usual open call for files + is used on it. */ + dev.parse (FH_FS); + } + else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES) + { + /* FIXME: Usually we don't set error to ENOENT if a file doesn't + exist. This is typically indicated by the fileattr content. + So, why here? The downside is that cygwin_conv_path just gets + an error for these paths so it reports the error back to the + application. Unlike in all other cases of non-existant files, + for which check doesn't set error, so cygwin_conv_path just + returns the path, as intended. */ + error = ENOENT; + return; + } + else if (!need_directory || error) + /* nothing to do */; + else if (fileattr == INVALID_FILE_ATTRIBUTES) + /* Reattach trailing dirsep in native path. */ + strcat (modifiable_path (), "\\"); + else if (fileattr & FILE_ATTRIBUTE_DIRECTORY) + path_flags &= ~PATH_SYMLINK; + else + { + debug_printf ("%s is a non-directory", path); + error = ENOTDIR; + return; + } + + if (dev.isfs ()) + { + if (strncmp (path, "\\\\.\\", 4)) + { + if (!tail || tail == path) + /* nothing */; + else if (tail[-1] != '\\') + *tail = '\0'; + else + { + error = ENOENT; + return; + } + } + + /* If FS hasn't been checked already in symlink_info::check, + do so now. */ + if (fs.inited ()|| fs.update (get_nt_native_path (), NULL)) + { + /* Incoming DOS paths are treated like DOS paths in native + Windows applications. No ACLs, just default settings. */ + if (is_msdos) + fs.has_acls (false); + debug_printf ("this->path(%s), has_acls(%d)", + path, fs.has_acls ()); + /* CV: We could use this->has_acls() but I want to make sure that + we don't forget that the PATH_NOACL flag must be taken into + account here. */ + if (!(path_flags & PATH_NOACL) && fs.has_acls ()) + set_exec (0); /* We really don't know if this is executable or + not here but set it to not executable since + it will be figured out later by anything + which cares about this. */ + } + /* If the FS has been found to have unrelibale inodes, note + that in path_flags. */ + if (!fs.hasgood_inode ()) + path_flags |= PATH_IHASH; + /* If the OS is caseinsensitive or the FS is caseinsensitive, + don't handle path casesensitive. */ + if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()) + path_flags |= PATH_NOPOSIX; + caseinsensitive = (path_flags & PATH_NOPOSIX) + ? OBJ_CASE_INSENSITIVE : 0; + if (exec_state () != dont_know_if_executable) + /* ok */; + else if (isdir ()) + set_exec (1); + else if (issymlink () || issocket ()) + set_exec (0); + } + + if (opt & PC_NOFULL) + { + if (is_relpath) + { + mkrelpath (this->modifiable_path (), !!caseinsensitive); + /* Invalidate wide_path so that wide relpath can be created + in later calls to get_nt_native_path or get_wide_win32_path. */ + if (wide_path) + cfree (wide_path); + wide_path = NULL; + } + if (need_directory) + { + size_t n = strlen (this->path); + /* Do not add trailing \ to UNC device names like \\.\a: */ + if (this->path[n - 1] != '\\' && + (strncmp (this->path, "\\\\.\\", 4) != 0)) + { + this->modifiable_path ()[n] = '\\'; + this->modifiable_path ()[n + 1] = '\0'; + } } } - } - if (saw_symlinks) - set_has_symlinks (); + if (saw_symlinks) + set_has_symlinks (); - if (opt & PC_OPEN) - path_flags |= PATH_OPEN; + if (opt & PC_OPEN) + path_flags |= PATH_OPEN; - if (opt & PC_CTTY) - path_flags |= PATH_CTTY; + if (opt & PC_CTTY) + path_flags |= PATH_CTTY; - if (opt & PC_POSIX) - { - if (tail < path_end && tail > path_copy + 1) - *tail = '/'; - set_normalized_path (path_copy); - if (is_msdos && !(opt & PC_NOWARN)) - warn_msdos (src); - } + if (opt & PC_POSIX) + { + if (tail < path_end && tail > path_copy + 1) + *tail = '/'; + set_normalized_path (path_copy); + if (is_msdos && dos_file_warning && !(opt & PC_NOWARN)) + warn_msdos (src); + } #if 0 - if (!error) - { - last_path_conv = *this; - strcpy (last_src, src); - } + if (!error) + { + last_path_conv = *this; + strcpy (last_src, src); + } #endif + } + __except (NO_ERROR) + { + error = EFAULT; + } + __endtry } path_conv::~path_conv () @@ -1688,332 +1701,342 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice) /* POSIX says that empty 'newpath' is invalid input while empty 'oldpath' is valid -- it's symlink resolver job to verify if symlink contents point to existing filesystem object */ - myfault efault; - if (efault.faulted (EFAULT)) - goto done; - if (!*oldpath || !*newpath) + __try { - set_errno (ENOENT); - goto done; - } - - if (strlen (oldpath) > SYMLINK_MAX) - { - set_errno (ENAMETOOLONG); - goto done; - } - - /* Trailing dirsep is a no-no. */ - len = strlen (newpath); - has_trailing_dirsep = isdirsep (newpath[len - 1]); - if (has_trailing_dirsep) - { - newpath = strdup (newpath); - ((char *) newpath)[len - 1] = '\0'; - } - - check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0); - /* We need the normalized full path below. */ - win32_newpath.check (newpath, check_opt, stat_suffixes); - - /* Default symlink type is determined by global allow_winsymlinks variable. - Device files are always shortcuts. */ - wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks; - /* NFS has its own, dedicated way to create symlinks. */ - if (win32_newpath.fs_is_nfs ()) - wsym_type = WSYM_nfs; - /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O - attribute. Therefore we create symlinks on MVFS always as shortcuts. */ - else if (win32_newpath.fs_is_mvfs ()) - wsym_type = WSYM_lnk; - /* AFS only supports native symlinks. */ - else if (win32_newpath.fs_is_afs ()) - { - /* Bail out if OS doesn't support native symlinks. */ - if (wincap.max_sys_priv () < SE_CREATE_SYMBOLIC_LINK_PRIVILEGE) + if (!*oldpath || !*newpath) { - set_errno (EPERM); - goto done; + set_errno (ENOENT); + __leave; } - wsym_type = WSYM_nativestrict; - } - /* Don't try native symlinks on filesystems not supporting reparse points. */ - else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict) - && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)) - wsym_type = WSYM_sysfile; - /* Attach .lnk suffix when shortcut is requested. */ - if (wsym_type == WSYM_lnk && !win32_newpath.exists () - && (isdevice || !win32_newpath.fs_is_nfs ())) - { - char *newplnk = tp.c_get (); - stpcpy (stpcpy (newplnk, newpath), ".lnk"); - win32_newpath.check (newplnk, check_opt); - } - - if (win32_newpath.error) - { - set_errno (win32_newpath.error); - goto done; - } - - syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath, - win32_newpath.get_nt_native_path (), wsym_type); - - if ((!isdevice && win32_newpath.exists ()) - || win32_newpath.is_auto_device ()) - { - set_errno (EEXIST); - goto done; - } - if (has_trailing_dirsep && !win32_newpath.exists ()) - { - set_errno (ENOENT); - goto done; - } - - /* Handle NFS and native symlinks in their own functions. */ - switch (wsym_type) - { - case WSYM_nfs: - res = symlink_nfs (oldpath, win32_newpath); - goto done; - case WSYM_native: - case WSYM_nativestrict: - res = symlink_native (oldpath, win32_newpath); - if (!res) - goto done; - /* Strictly native? Too bad. */ - if (wsym_type == WSYM_nativestrict) + if (strlen (oldpath) > SYMLINK_MAX) { - __seterrno (); - goto done; + set_errno (ENAMETOOLONG); + __leave; } - /* Otherwise, fall back to default symlink type. */ - wsym_type = WSYM_sysfile; - break; - default: - break; - } - if (wsym_type == WSYM_lnk) - { - path_conv win32_oldpath; - ITEMIDLIST *pidl = NULL; - size_t full_len = 0; - unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0; - char desc[MAX_PATH + 1], *relpath; - - if (!isdevice) + /* Trailing dirsep is a no-no. */ + len = strlen (newpath); + has_trailing_dirsep = isdirsep (newpath[len - 1]); + if (has_trailing_dirsep) { - /* First create an IDLIST to learn how big our shortcut is - going to be. */ - IShellFolder *psl; + newpath = strdup (newpath); + ((char *) newpath)[len - 1] = '\0'; + } - /* The symlink target is relative to the directory in which - the symlink gets created, not relative to the cwd. Therefore - we have to mangle the path quite a bit before calling path_conv. */ - if (isabspath (oldpath)) - win32_oldpath.check (oldpath, - PC_SYM_NOFOLLOW, - stat_suffixes); + check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0); + /* We need the normalized full path below. */ + win32_newpath.check (newpath, check_opt, stat_suffixes); + + /* Default symlink type is determined by global allow_winsymlinks + variable. Device files are always shortcuts. */ + wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks; + /* NFS has its own, dedicated way to create symlinks. */ + if (win32_newpath.fs_is_nfs ()) + wsym_type = WSYM_nfs; + /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O + attribute. Therefore we create symlinks on MVFS always as shortcuts. */ + else if (win32_newpath.fs_is_mvfs ()) + wsym_type = WSYM_lnk; + /* AFS only supports native symlinks. */ + else if (win32_newpath.fs_is_afs ()) + { + /* Bail out if OS doesn't support native symlinks. */ + if (wincap.max_sys_priv () < SE_CREATE_SYMBOLIC_LINK_PRIVILEGE) + { + set_errno (EPERM); + __leave; + } + wsym_type = WSYM_nativestrict; + } + /* Don't try native symlinks on FSes not supporting reparse points. */ + else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict) + && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)) + wsym_type = WSYM_sysfile; + + /* Attach .lnk suffix when shortcut is requested. */ + if (wsym_type == WSYM_lnk && !win32_newpath.exists () + && (isdevice || !win32_newpath.fs_is_nfs ())) + { + char *newplnk = tp.c_get (); + stpcpy (stpcpy (newplnk, newpath), ".lnk"); + win32_newpath.check (newplnk, check_opt); + } + + if (win32_newpath.error) + { + set_errno (win32_newpath.error); + __leave; + } + + syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath, + win32_newpath.get_nt_native_path (), wsym_type); + + if ((!isdevice && win32_newpath.exists ()) + || win32_newpath.is_auto_device ()) + { + set_errno (EEXIST); + __leave; + } + if (has_trailing_dirsep && !win32_newpath.exists ()) + { + set_errno (ENOENT); + __leave; + } + + /* Handle NFS and native symlinks in their own functions. */ + switch (wsym_type) + { + case WSYM_nfs: + res = symlink_nfs (oldpath, win32_newpath); + __leave; + case WSYM_native: + case WSYM_nativestrict: + res = symlink_native (oldpath, win32_newpath); + if (!res) + __leave; + /* Strictly native? Too bad. */ + if (wsym_type == WSYM_nativestrict) + { + __seterrno (); + __leave; + } + /* Otherwise, fall back to default symlink type. */ + wsym_type = WSYM_sysfile; + break; + default: + break; + } + + if (wsym_type == WSYM_lnk) + { + path_conv win32_oldpath; + ITEMIDLIST *pidl = NULL; + size_t full_len = 0; + unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0; + char desc[MAX_PATH + 1], *relpath; + + if (!isdevice) + { + /* First create an IDLIST to learn how big our shortcut is + going to be. */ + IShellFolder *psl; + + /* The symlink target is relative to the directory in which the + symlink gets created, not relative to the cwd. Therefore we + have to mangle the path quite a bit before calling path_conv.*/ + if (isabspath (oldpath)) + win32_oldpath.check (oldpath, + PC_SYM_NOFOLLOW, + stat_suffixes); + else + { + len = strrchr (win32_newpath.normalized_path, '/') + - win32_newpath.normalized_path + 1; + char *absoldpath = tp.t_get (); + stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, + len), + oldpath); + win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, + stat_suffixes); + } + if (SUCCEEDED (SHGetDesktopFolder (&psl))) + { + WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1]; + win32_oldpath.get_wide_win32_path (wc_path); + /* Amazing but true: Even though the ParseDisplayName method + takes a wide char path name, it does not understand the + Win32 prefix for long pathnames! So we have to tack off + the prefix and convert the path to the "normal" syntax + for ParseDisplayName. */ + WCHAR *wc = wc_path + 4; + if (wc[1] != L':') /* native UNC path */ + *(wc += 2) = L'\\'; + HRESULT res; + if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, + NULL, &pidl, + NULL))) + { + ITEMIDLIST *p; + + for (p = pidl; p->mkid.cb > 0; + p = (ITEMIDLIST *)((char *) p + p->mkid.cb)) + ; + pidl_len = (char *) p - (char *) pidl + 2; + } + psl->Release (); + } + } + /* Compute size of shortcut file. */ + full_len = sizeof (win_shortcut_hdr); + if (pidl_len) + full_len += sizeof (unsigned short) + pidl_len; + oldpath_len = strlen (oldpath); + /* Unfortunately the length of the description is restricted to a + length of 2000 bytes. We don't want to add considerations for + the different lengths and even 2000 bytes is not enough for long + path names. So what we do here is to set the description to the + POSIX path only if the path is not longer than MAX_PATH characters. + We append the full path name after the regular shortcut data + (see below), which works fine with Windows Explorer as well + as older Cygwin versions (as long as the whole file isn't bigger + than 8K). The description field is only used for backward + compatibility to older Cygwin versions and those versions are + not capable of handling long path names anyway. */ + desc_len = stpcpy (desc, oldpath_len > MAX_PATH + ? "[path too long]" : oldpath) - desc; + full_len += sizeof (unsigned short) + desc_len; + /* Devices get the oldpath string unchanged as relative path. */ + if (isdevice) + { + relpath_len = oldpath_len; + stpcpy (relpath = tp.c_get (), oldpath); + } else { - len = strrchr (win32_newpath.normalized_path, '/') - - win32_newpath.normalized_path + 1; - char *absoldpath = tp.t_get (); - stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len), - oldpath); - win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes); + relpath_len = strlen (win32_oldpath.get_win32 ()); + stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ()); } - if (SUCCEEDED (SHGetDesktopFolder (&psl))) + full_len += sizeof (unsigned short) + relpath_len; + full_len += sizeof (unsigned short) + oldpath_len; + /* 1 byte more for trailing 0 written by stpcpy. */ + if (full_len < NT_MAX_PATH * sizeof (WCHAR)) + buf = tp.t_get (); + else + buf = (char *) alloca (full_len + 1); + + /* Create shortcut header */ + win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; + memset (shortcut_header, 0, sizeof *shortcut_header); + shortcut_header->size = sizeof *shortcut_header; + shortcut_header->magic = GUID_shortcut; + shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH); + if (pidl) + shortcut_header->flags |= WSH_FLAG_IDLIST; + shortcut_header->run = SW_NORMAL; + cp = buf + sizeof (win_shortcut_hdr); + + /* Create IDLIST */ + if (pidl) { - WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1]; - win32_oldpath.get_wide_win32_path (wc_path); - /* Amazing but true: Even though the ParseDisplayName method - takes a wide char path name, it does not understand the - Win32 prefix for long pathnames! So we have to tack off - the prefix and convert the path to the "normal" syntax - for ParseDisplayName. */ - WCHAR *wc = wc_path + 4; - if (wc[1] != L':') /* native UNC path */ - *(wc += 2) = L'\\'; - HRESULT res; - if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL, - &pidl, NULL))) - { - ITEMIDLIST *p; + *(unsigned short *)cp = pidl_len; + memcpy (cp += 2, pidl, pidl_len); + cp += pidl_len; + CoTaskMemFree (pidl); + } - for (p = pidl; p->mkid.cb > 0; - p = (ITEMIDLIST *)((char *) p + p->mkid.cb)) - ; - pidl_len = (char *) p - (char *) pidl + 2; - } - psl->Release (); + /* Create description */ + *(unsigned short *)cp = desc_len; + cp = stpcpy (cp += 2, desc); + + /* Create relpath */ + *(unsigned short *)cp = relpath_len; + cp = stpcpy (cp += 2, relpath); + + /* Append the POSIX path after the regular shortcut data for + the long path support. */ + unsigned short *plen = (unsigned short *) cp; + cp += 2; + *(PWCHAR) cp = 0xfeff; /* BOM */ + cp += 2; + *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) + * sizeof (WCHAR); + cp += *plen; + } + else + { + /* Default technique creating a symlink. */ + buf = tp.t_get (); + cp = stpcpy (buf, SYMLINK_COOKIE); + *(PWCHAR) cp = 0xfeff; /* BOM */ + cp += 2; + /* Note that the terminating nul is written. */ + cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) + * sizeof (WCHAR); + } + + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + ULONG access; + HANDLE fh; + + access = DELETE | FILE_GENERIC_WRITE; + if (isdevice && win32_newpath.exists ()) + { + status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, + win32_newpath.get_object_attr (attr, + sec_none_nih), + &io, 0, FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } + status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL); + NtClose (fh); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; } } - /* Compute size of shortcut file. */ - full_len = sizeof (win_shortcut_hdr); - if (pidl_len) - full_len += sizeof (unsigned short) + pidl_len; - oldpath_len = strlen (oldpath); - /* Unfortunately the length of the description is restricted to a - length of 2000 bytes. We don't want to add considerations for - the different lengths and even 2000 bytes is not enough for long - path names. So what we do here is to set the description to the - POSIX path only if the path is not longer than MAX_PATH characters. - We append the full path name after the regular shortcut data - (see below), which works fine with Windows Explorer as well - as older Cygwin versions (as long as the whole file isn't bigger - than 8K). The description field is only used for backward - compatibility to older Cygwin versions and those versions are - not capable of handling long path names anyway. */ - desc_len = stpcpy (desc, oldpath_len > MAX_PATH - ? "[path too long]" : oldpath) - desc; - full_len += sizeof (unsigned short) + desc_len; - /* Devices get the oldpath string unchanged as relative path. */ - if (isdevice) - { - relpath_len = oldpath_len; - stpcpy (relpath = tp.c_get (), oldpath); - } - else - { - relpath_len = strlen (win32_oldpath.get_win32 ()); - stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ()); - } - full_len += sizeof (unsigned short) + relpath_len; - full_len += sizeof (unsigned short) + oldpath_len; - /* 1 byte more for trailing 0 written by stpcpy. */ - if (full_len < NT_MAX_PATH * sizeof (WCHAR)) - buf = tp.t_get (); - else - buf = (char *) alloca (full_len + 1); + else if (!isdevice && win32_newpath.has_acls () + && !win32_newpath.isremote ()) + /* If the filesystem supports ACLs, we will overwrite the DACL after the + call to NtCreateFile. This requires a handle with READ_CONTROL and + WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to + open the file again. + FIXME: On remote NTFS shares open sometimes fails because even the + creator of the file doesn't have the right to change the DACL. + I don't know what setting that is or how to recognize such a share, + so for now we don't request WRITE_DAC on remote drives. */ + access |= READ_CONTROL | WRITE_DAC; - /* Create shortcut header */ - win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf; - memset (shortcut_header, 0, sizeof *shortcut_header); - shortcut_header->size = sizeof *shortcut_header; - shortcut_header->magic = GUID_shortcut; - shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH); - if (pidl) - shortcut_header->flags |= WSH_FLAG_IDLIST; - shortcut_header->run = SW_NORMAL; - cp = buf + sizeof (win_shortcut_hdr); - - /* Create IDLIST */ - if (pidl) - { - *(unsigned short *)cp = pidl_len; - memcpy (cp += 2, pidl, pidl_len); - cp += pidl_len; - CoTaskMemFree (pidl); - } - - /* Create description */ - *(unsigned short *)cp = desc_len; - cp = stpcpy (cp += 2, desc); - - /* Create relpath */ - *(unsigned short *)cp = relpath_len; - cp = stpcpy (cp += 2, relpath); - - /* Append the POSIX path after the regular shortcut data for - the long path support. */ - unsigned short *plen = (unsigned short *) cp; - cp += 2; - *(PWCHAR) cp = 0xfeff; /* BOM */ - cp += 2; - *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR); - cp += *plen; - } - else - { - /* Default technique creating a symlink. */ - buf = tp.t_get (); - cp = stpcpy (buf, SYMLINK_COOKIE); - *(PWCHAR) cp = 0xfeff; /* BOM */ - cp += 2; - /* Note that the terminating nul is written. */ - cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR); - } - - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - NTSTATUS status; - ULONG access; - HANDLE fh; - - access = DELETE | FILE_GENERIC_WRITE; - if (isdevice && win32_newpath.exists ()) - { - status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, - win32_newpath.get_object_attr (attr, sec_none_nih), - &io, 0, FILE_OPEN_FOR_BACKUP_INTENT); + status = NtCreateFile (&fh, access, + win32_newpath.get_object_attr (attr, sec_none_nih), + &io, NULL, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_VALID_FLAGS, + isdevice ? FILE_OVERWRITE_IF : FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT + | FILE_NON_DIRECTORY_FILE + | FILE_OPEN_FOR_BACKUP_INTENT, + NULL, 0); if (!NT_SUCCESS (status)) { __seterrno_from_nt_status (status); - goto done; + __leave; + } + if (win32_newpath.has_acls ()) + set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID, + (io.Information == FILE_CREATED ? S_JUSTCREATED : 0) + | S_IFLNK | STD_RBITS | STD_WBITS); + status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, + NULL, NULL); + if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf)) + { + status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk + ? FILE_ATTRIBUTE_READONLY + : FILE_ATTRIBUTE_SYSTEM); + if (!NT_SUCCESS (status)) + debug_printf ("Setting attributes failed, status = %y", status); + res = 0; + } + else + { + __seterrno_from_nt_status (status); + FILE_DISPOSITION_INFORMATION fdi = { TRUE }; + status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, + FileDispositionInformation); + if (!NT_SUCCESS (status)) + debug_printf ("Setting delete dispostion failed, status = %y", + status); } - status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL); NtClose (fh); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - goto done; - } - } - else if (!isdevice && win32_newpath.has_acls () && !win32_newpath.isremote ()) - /* If the filesystem supports ACLs, we will overwrite the DACL after the - call to NtCreateFile. This requires a handle with READ_CONTROL and - WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to - open the file again. - FIXME: On remote NTFS shares open sometimes fails because even the - creator of the file doesn't have the right to change the DACL. - I don't know what setting that is or how to recognize such a share, - so for now we don't request WRITE_DAC on remote drives. */ - access |= READ_CONTROL | WRITE_DAC; - status = NtCreateFile (&fh, access, - win32_newpath.get_object_attr (attr, sec_none_nih), - &io, NULL, FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_VALID_FLAGS, - isdevice ? FILE_OVERWRITE_IF : FILE_CREATE, - FILE_SYNCHRONOUS_IO_NONALERT - | FILE_NON_DIRECTORY_FILE - | FILE_OPEN_FOR_BACKUP_INTENT, - NULL, 0); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - goto done; } - if (win32_newpath.has_acls ()) - set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID, - (io.Information == FILE_CREATED ? S_JUSTCREATED : 0) - | S_IFLNK | STD_RBITS | STD_WBITS); - status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL); - if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf)) - { - status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk - ? FILE_ATTRIBUTE_READONLY - : FILE_ATTRIBUTE_SYSTEM); - if (!NT_SUCCESS (status)) - debug_printf ("Setting attributes failed, status = %y", status); - res = 0; - } - else - { - __seterrno_from_nt_status (status); - FILE_DISPOSITION_INFORMATION fdi = { TRUE }; - status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi, - FileDispositionInformation); - if (!NT_SUCCESS (status)) - debug_printf ("Setting delete dispostion failed, status = %y", status); - } - NtClose (fh); - -done: + __except (EFAULT) {} + __endtry syscall_printf ("%d = symlink_worker(%s, %s, %d)", res, oldpath, newpath, isdevice); if (has_trailing_dirsep) @@ -2811,7 +2834,13 @@ restart: This case is only recognized by the length of the basename part. If it's 0, the incoming file is the root of a drive. So we at least know it's a directory. */ - fileattr = basename.Length ? 0 : FILE_ATTRIBUTE_DIRECTORY; + if (basename.Length) + fileattr = FILE_ATTRIBUTE_DIRECTORY; + else + { + fileattr = 0; + set_error (geterrno_from_nt_status (status)); + } } else { @@ -3109,13 +3138,16 @@ extern "C" char * getcwd (char *buf, size_t ulen) { char* res = NULL; - myfault efault; - if (efault.faulted (EFAULT)) - /* errno set */; - else if (ulen == 0 && buf) - set_errno (EINVAL); - else - res = cygheap->cwd.get (buf, 1, 1, ulen); + + __try + { + if (ulen == 0 && buf) + set_errno (EINVAL); + else + res = cygheap->cwd.get (buf, 1, 1, ulen); + } + __except (EFAULT) {} + __endtry return res; } @@ -3150,58 +3182,64 @@ get_current_dir_name (void) extern "C" int chdir (const char *in_dir) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!*in_dir) - { - set_errno (ENOENT); - return -1; - } - - syscall_printf ("dir '%s'", in_dir); - - /* Convert path. First argument ensures that we don't check for NULL/empty/invalid - again. */ - path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX); - if (path.error) - { - set_errno (path.error); - syscall_printf ("-1 = chdir (%s)", in_dir); - return -1; - } - int res = -1; - const char *posix_cwd = NULL; - dev_t devn = path.get_device (); - if (!path.exists ()) - set_errno (ENOENT); - else if (!path.isdir ()) - set_errno (ENOTDIR); - else if (!isvirtual_dev (devn)) + + __try { - /* The sequence chdir("xx"); chdir(".."); must be a noop if xx - is not a symlink. This is exploited by find.exe. - The posix_cwd is just path.normalized_path. - In other cases we let cwd.set obtain the Posix path through - the mount table. */ - if (!isdrive(path.normalized_path)) - posix_cwd = path.normalized_path; - res = 0; + if (!*in_dir) + { + set_errno (ENOENT); + __leave; + } + + syscall_printf ("dir '%s'", in_dir); + + /* Convert path. First argument ensures that we don't check for + NULL/empty/invalid again. */ + path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX); + if (path.error) + { + set_errno (path.error); + syscall_printf ("-1 = chdir (%s)", in_dir); + __leave; + } + + const char *posix_cwd = NULL; + dev_t devn = path.get_device (); + if (!path.exists ()) + set_errno (ENOENT); + else if (!path.isdir ()) + set_errno (ENOTDIR); + else if (!isvirtual_dev (devn)) + { + /* The sequence chdir("xx"); chdir(".."); must be a noop if xx + is not a symlink. This is exploited by find.exe. + The posix_cwd is just path.normalized_path. + In other cases we let cwd.set obtain the Posix path through + the mount table. */ + if (!isdrive(path.normalized_path)) + posix_cwd = path.normalized_path; + res = 0; + } + else + { + posix_cwd = path.normalized_path; + res = 0; + } + + if (!res) + res = cygheap->cwd.set (&path, posix_cwd); + + /* Note that we're accessing cwd.posix without a lock here. + I didn't think it was worth locking just for strace. */ + syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res, + cygheap->cwd.get_posix (), path.get_nt_native_path ()); } - else - { - posix_cwd = path.normalized_path; - res = 0; - } - - if (!res) - res = cygheap->cwd.set (&path, posix_cwd); - - /* Note that we're accessing cwd.posix without a lock here. I didn't think - it was worth locking just for strace. */ - syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res, - cygheap->cwd.get_posix (), path.get_nt_native_path ()); + __except (EFAULT) + { + res = -1; + } + __endtry MALLOC_CHECK; return res; } @@ -3239,10 +3277,6 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, size_t size) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - path_conv p; size_t lsiz = 0; char *buf = NULL; @@ -3250,152 +3284,177 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, int error = 0; bool relative = !!(what & CCP_RELATIVE); what &= CCP_CONVTYPE_MASK; + int ret = -1; - if (!from) + __try { - set_errno (EINVAL); - return -1; - } - - switch (what) - { - case CCP_POSIX_TO_WIN_A: - { - p.check ((const char *) from, - PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP - | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); - if (p.error) - return_with_errno (p.error); - PUNICODE_STRING up = p.get_nt_native_path (); - buf = tp.c_get (); - sys_wcstombs (buf, NT_MAX_PATH, - up->Buffer, up->Length / sizeof (WCHAR)); - /* Convert native path to standard DOS path. */ - if (!strncmp (buf, "\\??\\", 4)) - { - buf += 4; - if (buf[1] != ':') /* native UNC path */ - *(buf += 2) = '\\'; - } - else if (*buf == '\\') - { - /* Device name points to somewhere else in the NT namespace. - Use GLOBALROOT prefix to convert to Win32 path. */ - char *p = buf + sys_wcstombs (buf, NT_MAX_PATH, - ro_u_globalroot.Buffer, - ro_u_globalroot.Length - / sizeof (WCHAR)); - sys_wcstombs (p, NT_MAX_PATH - (p - buf), - up->Buffer, up->Length / sizeof (WCHAR)); - } - lsiz = strlen (buf) + 1; - /* TODO: Incoming "." is a special case which leads to a trailing - backslash ".\\" in the Win32 path. That's a result of the - conversion in normalize_posix_path. This should not occur - so the below code is just a band-aid. */ - if (relative && !strcmp ((const char *) from, ".") - && !strcmp (buf, ".\\")) - { - lsiz = 2; - buf[1] = '\0'; - } - } - break; - case CCP_POSIX_TO_WIN_W: - p.check ((const char *) from, - PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP - | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); - if (p.error) - return_with_errno (p.error); - /* Relative Windows paths are always restricted to MAX_PATH chars. */ - if (relative && !isabspath (p.get_win32 ()) - && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH) + if (!from) { - /* Recreate as absolute path. */ - p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW - | PC_NO_ACCESS_CHECK | PC_NOWARN); - if (p.error) - return_with_errno (p.error); + set_errno (EINVAL); + __leave; } - lsiz = p.get_wide_win32_path_len () + 1; - path = p.get_nt_native_path ()->Buffer; - /* Convert native path to standard DOS path. */ - if (!wcsncmp (path, L"\\??\\", 4)) + switch (what) { - path[1] = L'\\'; - - /* Drop long path prefix for short pathnames. Unfortunately there's - quite a bunch of Win32 functions, especially in user32.dll, - apparently, which don't grok long path names at all, not even - in the UNICODE API. */ - if ((path[5] == L':' && lsiz <= MAX_PATH + 4) - || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6)) + case CCP_POSIX_TO_WIN_A: + { + p.check ((const char *) from, + PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP + | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); + if (p.error) + { + set_errno (p.error); + __leave; + } + PUNICODE_STRING up = p.get_nt_native_path (); + buf = tp.c_get (); + sys_wcstombs (buf, NT_MAX_PATH, + up->Buffer, up->Length / sizeof (WCHAR)); + /* Convert native path to standard DOS path. */ + if (!strncmp (buf, "\\??\\", 4)) + { + buf += 4; + if (buf[1] != ':') /* native UNC path */ + *(buf += 2) = '\\'; + } + else if (*buf == '\\') + { + /* Device name points to somewhere else in the NT namespace. + Use GLOBALROOT prefix to convert to Win32 path. */ + char *p = buf + sys_wcstombs (buf, NT_MAX_PATH, + ro_u_globalroot.Buffer, + ro_u_globalroot.Length + / sizeof (WCHAR)); + sys_wcstombs (p, NT_MAX_PATH - (p - buf), + up->Buffer, up->Length / sizeof (WCHAR)); + } + lsiz = strlen (buf) + 1; + /* TODO: Incoming "." is a special case which leads to a trailing + backslash ".\\" in the Win32 path. That's a result of the + conversion in normalize_posix_path. This should not occur + so the below code is just a band-aid. */ + if (relative && !strcmp ((const char *) from, ".") + && !strcmp (buf, ".\\")) + { + lsiz = 2; + buf[1] = '\0'; + } + } + break; + case CCP_POSIX_TO_WIN_W: + p.check ((const char *) from, + PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP + | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0)); + if (p.error) { - path += 4; - lsiz -= 4; - if (path[1] != L':') + set_errno (p.error); + __leave; + } + /* Relative Windows paths are always restricted to MAX_PATH chars. */ + if (relative && !isabspath (p.get_win32 ()) + && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH) + { + /* Recreate as absolute path. */ + p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW + | PC_NO_ACCESS_CHECK | PC_NOWARN); + if (p.error) { - *(path += 2) = '\\'; - lsiz -= 2; + set_errno (p.error); + __leave; } } + lsiz = p.get_wide_win32_path_len () + 1; + path = p.get_nt_native_path ()->Buffer; + + /* Convert native path to standard DOS path. */ + if (!wcsncmp (path, L"\\??\\", 4)) + { + path[1] = L'\\'; + + /* Drop long path prefix for short pathnames. Unfortunately there's + quite a bunch of Win32 functions, especially in user32.dll, + apparently, which don't grok long path names at all, not even + in the UNICODE API. */ + if ((path[5] == L':' && lsiz <= MAX_PATH + 4) + || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6)) + { + path += 4; + lsiz -= 4; + if (path[1] != L':') + { + *(path += 2) = '\\'; + lsiz -= 2; + } + } + } + else if (*path == L'\\') + { + /* Device name points to somewhere else in the NT namespace. + Use GLOBALROOT prefix to convert to Win32 path. */ + to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer); + lsiz += ro_u_globalroot.Length / sizeof (WCHAR); + } + /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */ + if (relative && !strcmp ((const char *) from, ".") + && !wcscmp (path, L".\\")) + { + lsiz = 2; + path[1] = L'\0'; + } + lsiz *= sizeof (WCHAR); + break; + case CCP_WIN_A_TO_POSIX: + buf = tp.c_get (); + error = mount_table->conv_to_posix_path ((const char *) from, buf, + relative); + if (error) + { + set_errno (p.error); + __leave; + } + lsiz = strlen (buf) + 1; + break; + case CCP_WIN_W_TO_POSIX: + buf = tp.c_get (); + error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf, + relative); + if (error) + { + set_errno (error); + __leave; + } + lsiz = strlen (buf) + 1; + break; + default: + set_errno (EINVAL); + __leave; } - else if (*path == L'\\') + if (!size) { - /* Device name points to somewhere else in the NT namespace. - Use GLOBALROOT prefix to convert to Win32 path. */ - to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer); - lsiz += ro_u_globalroot.Length / sizeof (WCHAR); + ret = lsiz; + __leave; } - /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */ - if (relative && !strcmp ((const char *) from, ".") - && !wcscmp (path, L".\\")) + if (size < lsiz) { - lsiz = 2; - path[1] = L'\0'; + set_errno (ENOSPC); + __leave; } - lsiz *= sizeof (WCHAR); - break; - case CCP_WIN_A_TO_POSIX: - buf = tp.c_get (); - error = mount_table->conv_to_posix_path ((const char *) from, buf, - relative); - if (error) - return_with_errno (error); - lsiz = strlen (buf) + 1; - break; - case CCP_WIN_W_TO_POSIX: - buf = tp.c_get (); - error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf, - relative); - if (error) - return_with_errno (error); - lsiz = strlen (buf) + 1; - break; - default: - set_errno (EINVAL); - return -1; + switch (what) + { + case CCP_POSIX_TO_WIN_A: + case CCP_WIN_A_TO_POSIX: + case CCP_WIN_W_TO_POSIX: + stpcpy ((char *) to, buf); + break; + case CCP_POSIX_TO_WIN_W: + wcpcpy ((PWCHAR) to, path); + break; + } + ret = 0; } - if (!size) - return lsiz; - if (size < lsiz) - { - set_errno (ENOSPC); - return -1; - } - switch (what) - { - case CCP_POSIX_TO_WIN_A: - case CCP_WIN_A_TO_POSIX: - case CCP_WIN_W_TO_POSIX: - stpcpy ((char *) to, buf); - break; - case CCP_POSIX_TO_WIN_W: - wcpcpy ((PWCHAR) to, path); - break; - } - return 0; + __except (EFAULT) {} + __endtry + return ret; } extern "C" void * @@ -3454,6 +3513,9 @@ cygwin_conv_to_full_posix_path (const char *path, char *posix_path) extern "C" char * realpath (const char *__restrict path, char *__restrict resolved) { + tmp_pathbuf tp; + char *tpath; + /* Make sure the right errno is returned if path is NULL. */ if (!path) { @@ -3463,48 +3525,48 @@ realpath (const char *__restrict path, char *__restrict resolved) /* Guard reading from a potentially invalid path and writing to a potentially invalid resolved. */ - tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - - /* Win32 drive letter paths have to be converted to a POSIX path first, - because path_conv leaves the incoming path untouched except for - converting backslashes to forward slashes. */ - char *tpath; - if (isdrive (path)) + __try { - tpath = tp.c_get (); - mount_table->conv_to_posix_path (path, tpath, 0); - } - else - tpath = (char *) path; - - path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); - - - /* POSIX 2008 requires malloc'ing if resolved is NULL, and states - that using non-NULL resolved is asking for portability - problems. */ - - if (!real_path.error && real_path.exists ()) - { - if (!resolved) + /* Win32 drive letter paths have to be converted to a POSIX path first, + because path_conv leaves the incoming path untouched except for + converting backslashes to forward slashes. */ + if (isdrive (path)) { - resolved = (char *) malloc (strlen (real_path.normalized_path) + 1); - if (!resolved) - return NULL; + tpath = tp.c_get (); + mount_table->conv_to_posix_path (path, tpath, 0); } - strcpy (resolved, real_path.normalized_path); - return resolved; - } + else + tpath = (char *) path; - /* FIXME: on error, Linux puts the name of the path - component which could not be resolved into RESOLVED, but POSIX - does not require this. */ - if (resolved) - resolved[0] = '\0'; - set_errno (real_path.error ?: ENOENT); + path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); + + + /* POSIX 2008 requires malloc'ing if resolved is NULL, and states + that using non-NULL resolved is asking for portability + problems. */ + + if (!real_path.error && real_path.exists ()) + { + if (!resolved) + { + resolved = (char *) + malloc (strlen (real_path.normalized_path) + 1); + if (!resolved) + return NULL; + } + strcpy (resolved, real_path.normalized_path); + return resolved; + } + + /* FIXME: on error, Linux puts the name of the path + component which could not be resolved into RESOLVED, but POSIX + does not require this. */ + if (resolved) + resolved[0] = '\0'; + set_errno (real_path.error ?: ENOENT); + } + __except (EFAULT) {} + __endtry return NULL; } diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index d6c386075..18b05261c 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -281,14 +281,15 @@ class path_conv NULL, sa.lpSecurityDescriptor); return &attr; } - inline void init_reopen_attr (POBJECT_ATTRIBUTES attr, HANDLE h) + inline POBJECT_ATTRIBUTES init_reopen_attr (OBJECT_ATTRIBUTES &attr, HANDLE h) { if (has_buggy_reopen ()) - InitializeObjectAttributes (attr, get_nt_native_path (), + InitializeObjectAttributes (&attr, get_nt_native_path (), objcaseinsensitive (), NULL, NULL) else - InitializeObjectAttributes (attr, &ro_u_empty, objcaseinsensitive (), + InitializeObjectAttributes (&attr, &ro_u_empty, objcaseinsensitive (), h, NULL); + return &attr; } inline size_t get_wide_win32_path_len () { diff --git a/winsup/cygwin/poll.cc b/winsup/cygwin/poll.cc index 8ce4a28be..17411a098 100644 --- a/winsup/cygwin/poll.cc +++ b/winsup/cygwin/poll.cc @@ -1,7 +1,7 @@ /* poll.cc. Implements poll(2) via usage of select(2) call. Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, - 2012 Red Hat, Inc. + 2012, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -143,16 +143,19 @@ ppoll (struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, int timeout; sigset_t oldset = _my_tls.sigmask; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - timeout = (timeout_ts == NULL) - ? -1 - : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000); - if (sigmask) - set_signal_mask (_my_tls.sigmask, *sigmask); - int ret = poll (fds, nfds, timeout); - if (sigmask) - set_signal_mask (_my_tls.sigmask, oldset); - return ret; + __try + { + timeout = (timeout_ts == NULL) + ? -1 + : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000); + if (sigmask) + set_signal_mask (_my_tls.sigmask, *sigmask); + int ret = poll (fds, nfds, timeout); + if (sigmask) + set_signal_mask (_my_tls.sigmask, oldset); + return ret; + } + __except (EFAULT) {} + __endtry + return -1; } diff --git a/winsup/cygwin/posix.sgml b/winsup/cygwin/posix.sgml index 52c77c5be..e8c97d601 100644 --- a/winsup/cygwin/posix.sgml +++ b/winsup/cygwin/posix.sgml @@ -1134,6 +1134,8 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). feenableexcept fedisableexcept fegetexcept + ffsl + ffsll fgetxattr flistxattr fopencookie @@ -1166,6 +1168,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). pthread_getattr_np pthread_sigqueue ptsname_r + quotactl rawmemchr removexattr scandirat @@ -1296,6 +1299,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). rindex (SUSv3) scalb (SUSv3) setutent (XPG2) + stime (SVID) sys_errlist (BSD) sys_nerr (BSD) sys_siglist (BSD) @@ -1553,4 +1557,8 @@ return -1 and set errno to ENOSYS. grantpt and msgrcv and msgsnd are only available when cygserver is running. +The Linux-specific function quotactl only implements +what works on Windows: Windows only supports user block quotas on NTFS, no +group quotas, no inode quotas, no time constraints. + diff --git a/winsup/cygwin/posix_ipc.cc b/winsup/cygwin/posix_ipc.cc index be4a973a9..067fa5bb0 100644 --- a/winsup/cygwin/posix_ipc.cc +++ b/winsup/cygwin/posix_ipc.cc @@ -1,6 +1,6 @@ /* posix_ipc.cc: POSIX IPC API for Cygwin. - Copyright 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. + Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -414,17 +414,17 @@ extern "C" void *mmap64 (void *, size_t, int, int, int, off_t); extern "C" mqd_t mq_open (const char *name, int oflag, ...) { - int i, fd = -1, nonblock, created; + int i, fd = -1, nonblock, created = 0; long msgsize, index; off_t filesize = 0; va_list ap; mode_t mode; - int8_t *mptr; + int8_t *mptr = (int8_t *) MAP_FAILED; struct stat statbuff; struct mq_hdr *mqhdr; struct msg_hdr *msghdr; struct mq_attr *attr; - struct mq_info *mqinfo; + struct mq_info *mqinfo = NULL; LUID luid; size_t len = strlen (name); @@ -433,190 +433,198 @@ mq_open (const char *name, int oflag, ...) if (!check_path (mqname, mqueue, name, len)) return (mqd_t) -1; - myfault efault; - if (efault.faulted (EFAULT)) - return (mqd_t) -1; - - oflag &= (O_CREAT | O_EXCL | O_NONBLOCK); - created = 0; - nonblock = oflag & O_NONBLOCK; - oflag &= ~O_NONBLOCK; - mptr = (int8_t *) MAP_FAILED; - mqinfo = NULL; - -again: - if (oflag & O_CREAT) + __try { - va_start (ap, oflag); /* init ap to final named argument */ - mode = va_arg (ap, mode_t) & ~S_IXUSR; - attr = va_arg (ap, struct mq_attr *); - va_end (ap); + oflag &= (O_CREAT | O_EXCL | O_NONBLOCK); + nonblock = oflag & O_NONBLOCK; + oflag &= ~O_NONBLOCK; - /* Open and specify O_EXCL and user-execute */ - fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR); - if (fd < 0) + again: + if (oflag & O_CREAT) { - if (errno == EEXIST && (oflag & O_EXCL) == 0) - goto exists; /* already exists, OK */ - return (mqd_t) -1; - } - created = 1; - /* First one to create the file initializes it */ - if (attr == NULL) - attr = &defattr; - /* Check minimum and maximum values. The max values are pretty much - arbitrary, taken from the linux mq_overview man page. However, - these max values make sure that the internal mq_fattr structure - can use 32 bit types. */ - else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768 - || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576) - { - set_errno (EINVAL); - goto err; - } - /* Calculate and set the file size */ - msgsize = MSGSIZE (attr->mq_msgsize); - filesize = sizeof (struct mq_hdr) - + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize)); - if (lseek64 (fd, filesize - 1, SEEK_SET) == -1) - goto err; - if (write (fd, "", 1) == -1) - goto err; + va_start (ap, oflag); /* init ap to final named argument */ + mode = va_arg (ap, mode_t) & ~S_IXUSR; + attr = va_arg (ap, struct mq_attr *); + va_end (ap); - /* Memory map the file */ + /* Open and specify O_EXCL and user-execute */ + fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, + mode | S_IXUSR); + if (fd < 0) + { + if (errno == EEXIST && (oflag & O_EXCL) == 0) + goto exists; /* already exists, OK */ + return (mqd_t) -1; + } + created = 1; + /* First one to create the file initializes it */ + if (attr == NULL) + attr = &defattr; + /* Check minimum and maximum values. The max values are pretty much + arbitrary, taken from the linux mq_overview man page. However, + these max values make sure that the internal mq_fattr structure + can use 32 bit types. */ + else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768 + || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576) + { + set_errno (EINVAL); + __leave; + } + /* Calculate and set the file size */ + msgsize = MSGSIZE (attr->mq_msgsize); + filesize = sizeof (struct mq_hdr) + + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize)); + if (lseek64 (fd, filesize - 1, SEEK_SET) == -1) + __leave; + if (write (fd, "", 1) == -1) + __leave; + + /* Memory map the file */ + mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, + PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (mptr == (int8_t *) MAP_FAILED) + __leave; + + /* Allocate one mq_info{} for the queue */ + if (!(mqinfo = (struct mq_info *) + calloc (1, sizeof (struct mq_info)))) + __leave; + mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr; + mqinfo->mqi_magic = MQI_MAGIC; + mqinfo->mqi_flags = nonblock; + + /* Initialize header at beginning of file */ + /* Create free list with all messages on it */ + mqhdr->mqh_attr.mq_flags = 0; + mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg; + mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize; + mqhdr->mqh_attr.mq_curmsgs = 0; + mqhdr->mqh_nwait = 0; + mqhdr->mqh_pid = 0; + NtAllocateLocallyUniqueId (&luid); + __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x", + hash_path_name (0,mqname), + luid.HighPart, luid.LowPart); + mqhdr->mqh_head = 0; + mqhdr->mqh_magic = MQI_MAGIC; + index = sizeof (struct mq_hdr); + mqhdr->mqh_free = index; + for (i = 0; i < attr->mq_maxmsg - 1; i++) + { + msghdr = (struct msg_hdr *) &mptr[index]; + index += sizeof (struct msg_hdr) + msgsize; + msghdr->msg_next = index; + } + msghdr = (struct msg_hdr *) &mptr[index]; + msghdr->msg_next = 0; /* end of free list */ + + /* Initialize mutex & condition variables */ + i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname); + if (i != 0) + { + set_errno (i); + __leave; + } + i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S'); + if (i != 0) + { + set_errno (i); + __leave; + } + i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R'); + if (i != 0) + { + set_errno (i); + __leave; + } + /* Initialization complete, turn off user-execute bit */ + if (fchmod (fd, mode) == -1) + __leave; + close (fd); + return ((mqd_t) mqinfo); + } + + exists: + /* Open the file then memory map */ + if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0) + { + if (errno == ENOENT && (oflag & O_CREAT)) + goto again; + __leave; + } + /* Make certain initialization is complete */ + for (i = 0; i < MAX_TRIES; i++) + { + if (stat64 (mqname, &statbuff) == -1) + { + if (errno == ENOENT && (oflag & O_CREAT)) + { + close (fd); + fd = -1; + goto again; + } + __leave; + } + if ((statbuff.st_mode & S_IXUSR) == 0) + break; + sleep (1); + } + if (i == MAX_TRIES) + { + set_errno (ETIMEDOUT); + __leave; + } + + filesize = statbuff.st_size; mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mptr == (int8_t *) MAP_FAILED) - goto err; + __leave; + close (fd); + fd = -1; - /* Allocate one mq_info{} for the queue */ + /* Allocate one mq_info{} for each open */ if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info)))) - goto err; + __leave; mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr; + if (mqhdr->mqh_magic != MQI_MAGIC) + { + system_printf ( + "Old message queue \"%s\" detected!\n" + "This file is not usable as message queue anymore due to changes in the " + "internal file layout. Please remove the file and try again.", mqname); + set_errno (EACCES); + __leave; + } mqinfo->mqi_magic = MQI_MAGIC; mqinfo->mqi_flags = nonblock; - /* Initialize header at beginning of file */ - /* Create free list with all messages on it */ - mqhdr->mqh_attr.mq_flags = 0; - mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg; - mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize; - mqhdr->mqh_attr.mq_curmsgs = 0; - mqhdr->mqh_nwait = 0; - mqhdr->mqh_pid = 0; - NtAllocateLocallyUniqueId (&luid); - __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x", - hash_path_name (0,mqname), - luid.HighPart, luid.LowPart); - mqhdr->mqh_head = 0; - mqhdr->mqh_magic = MQI_MAGIC; - index = sizeof (struct mq_hdr); - mqhdr->mqh_free = index; - for (i = 0; i < attr->mq_maxmsg - 1; i++) - { - msghdr = (struct msg_hdr *) &mptr[index]; - index += sizeof (struct msg_hdr) + msgsize; - msghdr->msg_next = index; - } - msghdr = (struct msg_hdr *) &mptr[index]; - msghdr->msg_next = 0; /* end of free list */ - - /* Initialize mutex & condition variables */ + /* Initialize mutex & condition variable */ i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname); if (i != 0) - goto pthreaderr; - + { + set_errno (i); + __leave; + } i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S'); if (i != 0) - goto pthreaderr; - + { + set_errno (i); + __leave; + } i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R'); if (i != 0) - goto pthreaderr; - - /* Initialization complete, turn off user-execute bit */ - if (fchmod (fd, mode) == -1) - goto err; - close (fd); - return ((mqd_t) mqinfo); - } - -exists: - /* Open the file then memory map */ - if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0) - { - if (errno == ENOENT && (oflag & O_CREAT)) - goto again; - goto err; - } - /* Make certain initialization is complete */ - for (i = 0; i < MAX_TRIES; i++) - { - if (stat64 (mqname, &statbuff) == -1) { - if (errno == ENOENT && (oflag & O_CREAT)) - { - close (fd); - fd = -1; - goto again; - } - goto err; + set_errno (i); + __leave; } - if ((statbuff.st_mode & S_IXUSR) == 0) - break; - sleep (1); + return (mqd_t) mqinfo; } - if (i == MAX_TRIES) - { - set_errno (ETIMEDOUT); - goto err; - } - - filesize = statbuff.st_size; - mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if (mptr == (int8_t *) MAP_FAILED) - goto err; - close (fd); - fd = -1; - - /* Allocate one mq_info{} for each open */ - if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info)))) - goto err; - mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr; - if (mqhdr->mqh_magic != MQI_MAGIC) - { - system_printf ( -"Old message queue \"%s\" detected!\n" -"This file is not usable as message queue anymore due to changes in the " -"internal file layout. Please remove the file and try again.", mqname); - set_errno (EACCES); - goto err; - } - mqinfo->mqi_magic = MQI_MAGIC; - mqinfo->mqi_flags = nonblock; - - /* Initialize mutex & condition variable */ - i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname); - if (i != 0) - goto pthreaderr; - - i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S'); - if (i != 0) - goto pthreaderr; - - i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R'); - if (i != 0) - goto pthreaderr; - - return (mqd_t) mqinfo; - -pthreaderr: - errno = i; -err: + __except (EFAULT) {} + __endtry /* Don't let following function calls change errno */ save_errno save; - if (created) unlink (mqname); if (mptr != (int8_t *) MAP_FAILED) @@ -644,30 +652,32 @@ mq_getattr (mqd_t mqd, struct mq_attr *mqstat) struct mq_fattr *attr; struct mq_info *mqinfo; - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; - } - mqhdr = mqinfo->mqi_hdr; - attr = &mqhdr->mqh_attr; - if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) - { - errno = n; - return -1; - } - mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */ - mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */ - mqstat->mq_msgsize = attr->mq_msgsize; - mqstat->mq_curmsgs = attr->mq_curmsgs; + mqinfo = (struct mq_info *) mqd; + if (mqinfo->mqi_magic != MQI_MAGIC) + { + set_errno (EBADF); + __leave; + } + mqhdr = mqinfo->mqi_hdr; + attr = &mqhdr->mqh_attr; + if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) + { + errno = n; + __leave; + } + mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */ + mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */ + mqstat->mq_msgsize = attr->mq_msgsize; + mqstat->mq_curmsgs = attr->mq_curmsgs; - ipc_mutex_unlock (mqinfo->mqi_lock); - return 0; + ipc_mutex_unlock (mqinfo->mqi_lock); + return 0; + } + __except (EBADF) {} + __endtry + return -1; } extern "C" int @@ -678,39 +688,41 @@ mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat) struct mq_fattr *attr; struct mq_info *mqinfo; - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; - } - mqhdr = mqinfo->mqi_hdr; - attr = &mqhdr->mqh_attr; - if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) - { - errno = n; - return -1; - } + mqinfo = (struct mq_info *) mqd; + if (mqinfo->mqi_magic != MQI_MAGIC) + { + set_errno (EBADF); + __leave; + } + mqhdr = mqinfo->mqi_hdr; + attr = &mqhdr->mqh_attr; + if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) + { + errno = n; + __leave; + } - if (omqstat != NULL) - { - omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */ - omqstat->mq_maxmsg = attr->mq_maxmsg; - omqstat->mq_msgsize = attr->mq_msgsize; - omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */ + if (omqstat != NULL) + { + omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */ + omqstat->mq_maxmsg = attr->mq_maxmsg; + omqstat->mq_msgsize = attr->mq_msgsize; + omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */ + } + + if (mqstat->mq_flags & O_NONBLOCK) + mqinfo->mqi_flags |= O_NONBLOCK; + else + mqinfo->mqi_flags &= ~O_NONBLOCK; + + ipc_mutex_unlock (mqinfo->mqi_lock); + return 0; } - - if (mqstat->mq_flags & O_NONBLOCK) - mqinfo->mqi_flags |= O_NONBLOCK; - else - mqinfo->mqi_flags &= ~O_NONBLOCK; - - ipc_mutex_unlock (mqinfo->mqi_lock); - return 0; + __except (EBADF) {} + __endtry + return -1; } extern "C" int @@ -721,45 +733,47 @@ mq_notify (mqd_t mqd, const struct sigevent *notification) struct mq_hdr *mqhdr; struct mq_info *mqinfo; - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; - } - mqhdr = mqinfo->mqi_hdr; - if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) - { - errno = n; - return -1; - } - - pid = getpid (); - if (!notification) - { - if (mqhdr->mqh_pid == pid) - mqhdr->mqh_pid = 0; /* unregister calling process */ - } - else - { - if (mqhdr->mqh_pid != 0) + mqinfo = (struct mq_info *) mqd; + if (mqinfo->mqi_magic != MQI_MAGIC) { - if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH) - { - set_errno (EBUSY); - ipc_mutex_unlock (mqinfo->mqi_lock); - return -1; - } + set_errno (EBADF); + __leave; } - mqhdr->mqh_pid = pid; - mqhdr->mqh_event = *notification; + mqhdr = mqinfo->mqi_hdr; + if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) + { + errno = n; + __leave; + } + + pid = getpid (); + if (!notification) + { + if (mqhdr->mqh_pid == pid) + mqhdr->mqh_pid = 0; /* unregister calling process */ + } + else + { + if (mqhdr->mqh_pid != 0) + { + if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH) + { + set_errno (EBUSY); + ipc_mutex_unlock (mqinfo->mqi_lock); + __leave; + } + } + mqhdr->mqh_pid = pid; + mqhdr->mqh_event = *notification; + } + ipc_mutex_unlock (mqinfo->mqi_lock); + return 0; } - ipc_mutex_unlock (mqinfo->mqi_lock); - return 0; + __except (EBADF) {} + __endtry + return -1; } static int @@ -773,113 +787,116 @@ _mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio, struct mq_hdr *mqhdr; struct mq_fattr *attr; struct msg_hdr *msghdr, *nmsghdr, *pmsghdr; - struct mq_info *mqinfo; + struct mq_info *mqinfo = NULL; + bool ipc_mutex_locked = false; + int ret = -1; pthread_testcancel (); - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; - } - if (prio > MQ_PRIO_MAX) - { - set_errno (EINVAL); - return -1; - } - - mqhdr = mqinfo->mqi_hdr; /* struct pointer */ - mptr = (int8_t *) mqhdr; /* byte pointer */ - attr = &mqhdr->mqh_attr; - if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) - { - errno = n; - return -1; - } - - if (len > (size_t) attr->mq_msgsize) - { - set_errno (EMSGSIZE); - goto err; - } - if (attr->mq_curmsgs == 0) - { - if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0) + mqinfo = (struct mq_info *) mqd; + if (mqinfo->mqi_magic != MQI_MAGIC) { - sigev = &mqhdr->mqh_event; - if (sigev->sigev_notify == SIGEV_SIGNAL) - sigqueue (mqhdr->mqh_pid, sigev->sigev_signo, sigev->sigev_value); - mqhdr->mqh_pid = 0; /* unregister */ + set_errno (EBADF); + __leave; } - } - else if (attr->mq_curmsgs >= attr->mq_maxmsg) - { - /* Queue is full */ - if (mqinfo->mqi_flags & O_NONBLOCK) + if (prio > MQ_PRIO_MAX) { - set_errno (EAGAIN); - goto err; + set_errno (EINVAL); + __leave; } - /* Wait for room for one message on the queue */ - while (attr->mq_curmsgs >= attr->mq_maxmsg) + + mqhdr = mqinfo->mqi_hdr; /* struct pointer */ + mptr = (int8_t *) mqhdr; /* byte pointer */ + attr = &mqhdr->mqh_attr; + if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) { - int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend, mqinfo->mqi_lock, - abstime); - if (ret != 0) + errno = n; + __leave; + } + ipc_mutex_locked = true; + if (len > (size_t) attr->mq_msgsize) + { + set_errno (EMSGSIZE); + __leave; + } + if (attr->mq_curmsgs == 0) + { + if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0) { - set_errno (ret); - return -1; + sigev = &mqhdr->mqh_event; + if (sigev->sigev_notify == SIGEV_SIGNAL) + sigqueue (mqhdr->mqh_pid, sigev->sigev_signo, + sigev->sigev_value); + mqhdr->mqh_pid = 0; /* unregister */ } } - } - - /* nmsghdr will point to new message */ - if ((freeindex = mqhdr->mqh_free) == 0) - api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs); - - nmsghdr = (struct msg_hdr *) &mptr[freeindex]; - nmsghdr->msg_prio = prio; - nmsghdr->msg_len = len; - memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */ - mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */ - - /* Find right place for message in linked list */ - index = mqhdr->mqh_head; - pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head); - while (index) - { - msghdr = (struct msg_hdr *) &mptr[index]; - if (prio > msghdr->msg_prio) + else if (attr->mq_curmsgs >= attr->mq_maxmsg) { - nmsghdr->msg_next = index; - pmsghdr->msg_next = freeindex; - break; + /* Queue is full */ + if (mqinfo->mqi_flags & O_NONBLOCK) + { + set_errno (EAGAIN); + __leave; + } + /* Wait for room for one message on the queue */ + while (attr->mq_curmsgs >= attr->mq_maxmsg) + { + int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend, + mqinfo->mqi_lock, abstime); + if (ret != 0) + { + set_errno (ret); + __leave; + } + } } - index = msghdr->msg_next; - pmsghdr = msghdr; - } - if (index == 0) - { - /* Queue was empty or new goes at end of list */ - pmsghdr->msg_next = freeindex; - nmsghdr->msg_next = 0; - } - /* Wake up anyone blocked in mq_receive waiting for a message */ - if (attr->mq_curmsgs == 0) - ipc_cond_signal (mqinfo->mqi_waitrecv); - attr->mq_curmsgs++; - ipc_mutex_unlock (mqinfo->mqi_lock); - return 0; + /* nmsghdr will point to new message */ + if ((freeindex = mqhdr->mqh_free) == 0) + api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs); -err: - ipc_mutex_unlock (mqinfo->mqi_lock); - return -1; + nmsghdr = (struct msg_hdr *) &mptr[freeindex]; + nmsghdr->msg_prio = prio; + nmsghdr->msg_len = len; + memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */ + mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */ + + /* Find right place for message in linked list */ + index = mqhdr->mqh_head; + pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head); + while (index) + { + msghdr = (struct msg_hdr *) &mptr[index]; + if (prio > msghdr->msg_prio) + { + nmsghdr->msg_next = index; + pmsghdr->msg_next = freeindex; + break; + } + index = msghdr->msg_next; + pmsghdr = msghdr; + } + if (index == 0) + { + /* Queue was empty or new goes at end of list */ + pmsghdr->msg_next = freeindex; + nmsghdr->msg_next = 0; + } + /* Wake up anyone blocked in mq_receive waiting for a message */ + if (attr->mq_curmsgs == 0) + ipc_cond_signal (mqinfo->mqi_waitrecv); + attr->mq_curmsgs++; + + ipc_mutex_unlock (mqinfo->mqi_lock); + ret = 0; + } + __except (EBADF) {} + __endtry + if (ipc_mutex_locked) + ipc_mutex_unlock (mqinfo->mqi_lock); + return ret; } extern "C" int @@ -902,85 +919,84 @@ _mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop, int n; long index; int8_t *mptr; - ssize_t len; + ssize_t len = -1; struct mq_hdr *mqhdr; struct mq_fattr *attr; struct msg_hdr *msghdr; - struct mq_info *mqinfo; + struct mq_info *mqinfo = (struct mq_info *) mqd; + bool ipc_mutex_locked = false; pthread_testcancel (); - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; - } - mqhdr = mqinfo->mqi_hdr; /* struct pointer */ - mptr = (int8_t *) mqhdr; /* byte pointer */ - attr = &mqhdr->mqh_attr; - if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) - { - errno = n; - return -1; - } - - if (maxlen < (size_t) attr->mq_msgsize) - { - set_errno (EMSGSIZE); - goto err; - } - if (attr->mq_curmsgs == 0) /* queue is empty */ - { - if (mqinfo->mqi_flags & O_NONBLOCK) + if (mqinfo->mqi_magic != MQI_MAGIC) { - set_errno (EAGAIN); - goto err; + set_errno (EBADF); + __leave; } - /* Wait for a message to be placed onto queue */ - mqhdr->mqh_nwait++; - while (attr->mq_curmsgs == 0) + mqhdr = mqinfo->mqi_hdr; /* struct pointer */ + mptr = (int8_t *) mqhdr; /* byte pointer */ + attr = &mqhdr->mqh_attr; + if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0) { - int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv, mqinfo->mqi_lock, - abstime); - if (ret != 0) + errno = n; + __leave; + } + ipc_mutex_locked = true; + if (maxlen < (size_t) attr->mq_msgsize) + { + set_errno (EMSGSIZE); + __leave; + } + if (attr->mq_curmsgs == 0) /* queue is empty */ + { + if (mqinfo->mqi_flags & O_NONBLOCK) { - set_errno (ret); - return -1; + set_errno (EAGAIN); + __leave; } + /* Wait for a message to be placed onto queue */ + mqhdr->mqh_nwait++; + while (attr->mq_curmsgs == 0) + { + int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv, + mqinfo->mqi_lock, abstime); + if (ret != 0) + { + set_errno (ret); + __leave; + } + } + mqhdr->mqh_nwait--; } - mqhdr->mqh_nwait--; + + if ((index = mqhdr->mqh_head) == 0) + api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs); + + msghdr = (struct msg_hdr *) &mptr[index]; + mqhdr->mqh_head = msghdr->msg_next; /* new head of list */ + len = msghdr->msg_len; + memcpy(ptr, msghdr + 1, len); /* copy the message itself */ + if (priop != NULL) + *priop = msghdr->msg_prio; + + /* Just-read message goes to front of free list */ + msghdr->msg_next = mqhdr->mqh_free; + mqhdr->mqh_free = index; + + /* Wake up anyone blocked in mq_send waiting for room */ + if (attr->mq_curmsgs == attr->mq_maxmsg) + ipc_cond_signal (mqinfo->mqi_waitsend); + attr->mq_curmsgs--; + + ipc_mutex_unlock (mqinfo->mqi_lock); } - - if ((index = mqhdr->mqh_head) == 0) - api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs); - - msghdr = (struct msg_hdr *) &mptr[index]; - mqhdr->mqh_head = msghdr->msg_next; /* new head of list */ - len = msghdr->msg_len; - memcpy(ptr, msghdr + 1, len); /* copy the message itself */ - if (priop != NULL) - *priop = msghdr->msg_prio; - - /* Just-read message goes to front of free list */ - msghdr->msg_next = mqhdr->mqh_free; - mqhdr->mqh_free = index; - - /* Wake up anyone blocked in mq_send waiting for room */ - if (attr->mq_curmsgs == attr->mq_maxmsg) - ipc_cond_signal (mqinfo->mqi_waitsend); - attr->mq_curmsgs--; - - ipc_mutex_unlock (mqinfo->mqi_lock); + __except (EBADF) {} + __endtry + if (ipc_mutex_locked) + ipc_mutex_unlock (mqinfo->mqi_lock); return len; - -err: - ipc_mutex_unlock (mqinfo->mqi_lock); - return -1; } extern "C" ssize_t @@ -1004,34 +1020,36 @@ mq_close (mqd_t mqd) struct mq_fattr *attr; struct mq_info *mqinfo; - myfault efault; - if (efault.faulted (EBADF)) - return -1; - - mqinfo = (struct mq_info *) mqd; - if (mqinfo->mqi_magic != MQI_MAGIC) + __try { - set_errno (EBADF); - return -1; + mqinfo = (struct mq_info *) mqd; + if (mqinfo->mqi_magic != MQI_MAGIC) + { + set_errno (EBADF); + __leave; + } + mqhdr = mqinfo->mqi_hdr; + attr = &mqhdr->mqh_attr; + + if (mq_notify (mqd, NULL)) /* unregister calling process */ + __leave; + + msgsize = MSGSIZE (attr->mq_msgsize); + filesize = sizeof (struct mq_hdr) + + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize)); + if (munmap (mqinfo->mqi_hdr, filesize) == -1) + __leave; + + mqinfo->mqi_magic = 0; /* just in case */ + ipc_cond_close (mqinfo->mqi_waitsend); + ipc_cond_close (mqinfo->mqi_waitrecv); + ipc_mutex_close (mqinfo->mqi_lock); + free (mqinfo); + return 0; } - mqhdr = mqinfo->mqi_hdr; - attr = &mqhdr->mqh_attr; - - if (mq_notify (mqd, NULL)) /* unregister calling process */ - return -1; - - msgsize = MSGSIZE (attr->mq_msgsize); - filesize = sizeof (struct mq_hdr) - + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize)); - if (munmap (mqinfo->mqi_hdr, filesize) == -1) - return -1; - - mqinfo->mqi_magic = 0; /* just in case */ - ipc_cond_close (mqinfo->mqi_waitsend); - ipc_cond_close (mqinfo->mqi_waitrecv); - ipc_mutex_close (mqinfo->mqi_lock); - free (mqinfo); - return 0; + __except (EBADF) {} + __endtry + return -1; } extern "C" int @@ -1062,7 +1080,7 @@ struct sem_finfo extern "C" sem_t * sem_open (const char *name, int oflag, ...) { - int i, fd = -1, created; + int i, fd = -1, created = 0; va_list ap; mode_t mode = 0; unsigned int value = 0; @@ -1078,97 +1096,99 @@ sem_open (const char *name, int oflag, ...) if (!check_path (semname, semaphore, name, len)) return SEM_FAILED; - myfault efault; - if (efault.faulted (EFAULT)) - return SEM_FAILED; - - created = 0; - oflag &= (O_CREAT | O_EXCL); - -again: - if (oflag & O_CREAT) + __try { - va_start (ap, oflag); /* init ap to final named argument */ - mode = va_arg (ap, mode_t) & ~S_IXUSR; - value = va_arg (ap, unsigned int); - va_end (ap); + oflag &= (O_CREAT | O_EXCL); - /* Open and specify O_EXCL and user-execute */ - fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR); - if (fd < 0) + again: + if (oflag & O_CREAT) { - if (errno == EEXIST && (oflag & O_EXCL) == 0) - goto exists; /* already exists, OK */ - return SEM_FAILED; - } - created = 1; - /* First one to create the file initializes it. */ - NtAllocateLocallyUniqueId (&sf.luid); - sf.value = value; - sf.hash = hash_path_name (0, semname); - if (write (fd, &sf, sizeof sf) != sizeof sf) - goto err; - sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen); - if (sem == SEM_FAILED) - goto err; - /* Initialization complete, turn off user-execute bit */ - if (fchmod (fd, mode) == -1) - goto err; - /* Don't close (fd); */ - return sem; - } + va_start (ap, oflag); /* init ap to final named argument */ + mode = va_arg (ap, mode_t) & ~S_IXUSR; + value = va_arg (ap, unsigned int); + va_end (ap); -exists: - /* Open the file and fetch the semaphore name. */ - if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0) - { - if (errno == ENOENT && (oflag & O_CREAT)) - goto again; - goto err; - } - /* Make certain initialization is complete */ - for (i = 0; i < MAX_TRIES; i++) - { - if (stat64 (semname, &statbuff) == -1) + /* Open and specify O_EXCL and user-execute */ + fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, + mode | S_IXUSR); + if (fd < 0) + { + if (errno == EEXIST && (oflag & O_EXCL) == 0) + goto exists; /* already exists, OK */ + return SEM_FAILED; + } + created = 1; + /* First one to create the file initializes it. */ + NtAllocateLocallyUniqueId (&sf.luid); + sf.value = value; + sf.hash = hash_path_name (0, semname); + if (write (fd, &sf, sizeof sf) != sizeof sf) + __leave; + sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, + wasopen); + if (sem == SEM_FAILED) + __leave; + /* Initialization complete, turn off user-execute bit */ + if (fchmod (fd, mode) == -1) + __leave; + /* Don't close (fd); */ + return sem; + } + + exists: + /* Open the file and fetch the semaphore name. */ + if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0) { if (errno == ENOENT && (oflag & O_CREAT)) - { - close (fd); - fd = -1; - goto again; - } - goto err; + goto again; + __leave; } - if ((statbuff.st_mode & S_IXUSR) == 0) - break; - sleep (1); + /* Make certain initialization is complete */ + for (i = 0; i < MAX_TRIES; i++) + { + if (stat64 (semname, &statbuff) == -1) + { + if (errno == ENOENT && (oflag & O_CREAT)) + { + close (fd); + fd = -1; + goto again; + } + __leave; + } + if ((statbuff.st_mode & S_IXUSR) == 0) + break; + sleep (1); + } + if (i == MAX_TRIES) + { + set_errno (ETIMEDOUT); + __leave; + } + if (file.lock (fd, sizeof sf)) + __leave; + if (read (fd, &sf, sizeof sf) != sizeof sf) + __leave; + sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, + wasopen); + file.unlock (fd); + if (sem == SEM_FAILED) + __leave; + /* If wasopen is set, the semaphore was already opened and we already have + an open file descriptor pointing to the file. This means, we have to + close the file descriptor created in this call. It won't be stored + anywhere anyway. */ + if (wasopen) + close (fd); + return sem; } - if (i == MAX_TRIES) - { - set_errno (ETIMEDOUT); - goto err; - } - if (file.lock (fd, sizeof sf)) - goto err; - if (read (fd, &sf, sizeof sf) != sizeof sf) - goto err; - sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, wasopen); - file.unlock (fd); - if (sem == SEM_FAILED) - goto err; - /* If wasopen is set, the semaphore was already opened and we already have - an open file descriptor pointing to the file. This means, we have to - close the file descriptor created in this call. It won't be stored - anywhere anyway. */ - if (wasopen) - close (fd); - return sem; - -err: + __except (EFAULT) {} + __endtry /* Don't let following function calls change errno */ save_errno save; - file.unlock (fd); + if (fd >= 0) + file.unlock (fd); if (created) unlink (semname); if (sem != SEM_FAILED) diff --git a/winsup/cygwin/quotactl.cc b/winsup/cygwin/quotactl.cc new file mode 100644 index 000000000..fbc66eed3 --- /dev/null +++ b/winsup/cygwin/quotactl.cc @@ -0,0 +1,341 @@ +/* quotactl.cc: code for manipulating disk quotas + + Copyright 2014 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 "cygtls.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "ntdll.h" +#include "tls_pbuf.h" +#include "pwdgrp.h" +#include +#include + +#define PGQI_SIZE (sizeof (FILE_GET_QUOTA_INFORMATION) + SECURITY_MAX_SID_SIZE) +#define PFQI_SIZE (sizeof (FILE_QUOTA_INFORMATION) + SECURITY_MAX_SID_SIZE) + +/* Modelled after the Linux quotactl function. */ +extern "C" int +quotactl (int cmd, const char *special, int id, caddr_t addr) +{ + ACCESS_MASK access = FILE_READ_DATA; + cygsid sid; + path_conv pc; + tmp_pathbuf tp; + UNICODE_STRING path; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE fh; + IO_STATUS_BLOCK io; + FILE_FS_CONTROL_INFORMATION ffci; + int ret = 0; + + uint32_t subcmd = (uint32_t) cmd >> SUBCMDSHIFT; + uint32_t type = (uint32_t) cmd & SUBCMDMASK; + + if (type != USRQUOTA && type != GRPQUOTA) + { + set_errno (EINVAL); + return -1; + } + switch (subcmd) + { + case Q_SYNC: + if (!special) + return 0; + access |= FILE_WRITE_DATA; + break; + case Q_QUOTAON: + if (id < QFMT_VFS_OLD || id > QFMT_VFS_V1) + { + set_errno (EINVAL); + return -1; + } + /*FALLTHRU*/ + case Q_QUOTAOFF: + case Q_SETINFO: + access |= FILE_WRITE_DATA; + break; + case Q_GETFMT: + case Q_GETINFO: + break; + case Q_SETQUOTA: + access |= FILE_WRITE_DATA; + /*FALLTHRU*/ + case Q_GETQUOTA: + /* Windows feature: Default limits. Get or set them with id == -1. */ + if (id != -1) + { + struct passwd *pw = NULL; + struct group *gr = NULL; + + if (type == USRQUOTA) + pw = internal_getpwuid (id); + else + gr = internal_getgrgid (id); + if (pw) + sid.getfrompw (pw); + else if (gr) + sid.getfromgr (gr); + else + { + set_errno (EINVAL); + return -1; + } + } + break; + default: + set_errno (EINVAL); + return -1; + } + /* Check path */ + pc.check (special, PC_SYM_FOLLOW | PC_NOWARN, stat_suffixes); + if (pc.error) + { + set_errno (pc.error); + return -1; + } + if (!pc.exists ()) + { + set_errno (ENOENT); + return -1; + } + if (!S_ISBLK (pc.dev.mode)) + { + set_errno (ENOTBLK); + return -1; + } + pc.get_object_attr (attr, sec_none_nih); + /* For the following functions to work, we must attach the virtual path to + the quota file to the device path. + + FIXME: Note that this is NTFS-specific. Adding ReFS in another step. */ + tp.u_get (&path); + RtlCopyUnicodeString (&path, attr.ObjectName); + RtlAppendUnicodeToString (&path, L"\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION"); + attr.ObjectName = &path; + + /* Open filesystem */ + status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_VALID_FLAGS, 0); + if (NT_SUCCESS (status)) + switch (subcmd) + { + case Q_SYNC: + /* No sync, just report success. */ + status = STATUS_SUCCESS; + break; + case Q_QUOTAON: + case Q_QUOTAOFF: + /* Ignore filename in addr. */ + status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci, + FileFsControlInformation); + if (!NT_SUCCESS (status)) + break; + ffci.FileSystemControlFlags &= ~FILE_VC_QUOTA_ENFORCE + & ~FILE_VC_QUOTA_TRACK + & ~FILE_VC_QUOTAS_INCOMPLETE + & ~FILE_VC_QUOTAS_REBUILDING; + if (subcmd == Q_QUOTAON) + ffci.FileSystemControlFlags |= FILE_VC_QUOTA_ENFORCE; + status = NtSetVolumeInformationFile (fh, &io, &ffci, sizeof ffci, + FileFsControlInformation); + break; + case Q_GETFMT: + __try + { + uint32_t *retval = (uint32_t *) addr; + + /* Always fake the latest format. */ + *retval = QFMT_VFS_V1; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + status = STATUS_SUCCESS; + break; + case Q_GETINFO: + __try + { + struct dqinfo *dqi = (struct dqinfo *) addr; + + dqi->dqi_bgrace = dqi->dqi_igrace = UINT64_MAX; + dqi->dqi_flags = 0; + dqi->dqi_valid = IIF_BGRACE | IIF_IGRACE; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + status = STATUS_SUCCESS; + break; + case Q_SETINFO: + /* No settings possible, just report success. */ + status = STATUS_SUCCESS; + break; + case Q_GETQUOTA: + /* Windows feature: Default limits. Get or set them with id == -1. */ + if (id == -1) + { + status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci, + FileFsControlInformation); + if (!NT_SUCCESS (status)) + break; + __try + { + struct dqblk *dq = (struct dqblk *) addr; + + dq->dqb_bhardlimit = (uint64_t) ffci.DefaultQuotaLimit.QuadPart; + if (dq->dqb_bhardlimit != UINT64_MAX) + dq->dqb_bhardlimit /= BLOCK_SIZE; + dq->dqb_bsoftlimit = + (uint64_t) ffci.DefaultQuotaThreshold.QuadPart; + if (dq->dqb_bsoftlimit != UINT64_MAX) + dq->dqb_bsoftlimit /= BLOCK_SIZE; + dq->dqb_curspace = 0; + dq->dqb_ihardlimit = UINT64_MAX; + dq->dqb_isoftlimit = UINT64_MAX; + dq->dqb_curinodes = 0; + dq->dqb_btime = UINT64_MAX; + dq->dqb_itime = UINT64_MAX; + dq->dqb_valid = QIF_BLIMITS; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + } + else + { + PFILE_GET_QUOTA_INFORMATION pgqi = (PFILE_GET_QUOTA_INFORMATION) + alloca (PGQI_SIZE); + PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION) + alloca (PFQI_SIZE); + + pgqi->NextEntryOffset = 0; + pgqi->SidLength = RtlLengthSid (sid); + RtlCopySid (RtlLengthSid (sid), &pgqi->Sid, sid); + status = NtQueryQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE, + TRUE, pgqi, PGQI_SIZE, + NULL, TRUE); + if (!NT_SUCCESS (status)) + break; + __try + { + struct dqblk *dq = (struct dqblk *) addr; + + dq->dqb_bhardlimit = (uint64_t) pfqi->QuotaLimit.QuadPart; + if (dq->dqb_bhardlimit != UINT64_MAX) + dq->dqb_bhardlimit /= BLOCK_SIZE; + dq->dqb_bsoftlimit = (uint64_t) pfqi->QuotaThreshold.QuadPart; + if (dq->dqb_bsoftlimit != UINT64_MAX) + dq->dqb_bsoftlimit /= BLOCK_SIZE; + dq->dqb_curspace = (uint64_t) pfqi->QuotaUsed.QuadPart; + if (dq->dqb_curspace != UINT64_MAX) + dq->dqb_curspace /= BLOCK_SIZE; + dq->dqb_ihardlimit = UINT64_MAX; + dq->dqb_isoftlimit = UINT64_MAX; + dq->dqb_curinodes = 0; + dq->dqb_btime = UINT64_MAX; + dq->dqb_itime = UINT64_MAX; + dq->dqb_valid = QIF_BLIMITS | QIF_SPACE; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + } + break; + case Q_SETQUOTA: + /* Windows feature: Default limits. Get or set them with id == -1. */ + if (id == -1) + { + status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci, + FileFsControlInformation); + if (!NT_SUCCESS (status)) + break; + __try + { + struct dqblk *dq = (struct dqblk *) addr; + + if (!(dq->dqb_valid & QIF_BLIMITS)) + break; + ffci.DefaultQuotaLimit.QuadPart = dq->dqb_bhardlimit; + if (ffci.DefaultQuotaLimit.QuadPart != -1) + ffci.DefaultQuotaLimit.QuadPart *= BLOCK_SIZE; + ffci.DefaultQuotaThreshold.QuadPart = dq->dqb_bsoftlimit; + if (ffci.DefaultQuotaThreshold.QuadPart != -1) + ffci.DefaultQuotaThreshold.QuadPart *= BLOCK_SIZE; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + status = NtSetVolumeInformationFile (fh, &io, &ffci, sizeof ffci, + FileFsControlInformation); + } + else + { + PFILE_GET_QUOTA_INFORMATION pgqi = (PFILE_GET_QUOTA_INFORMATION) + alloca (PGQI_SIZE); + PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION) + alloca (PFQI_SIZE); + + pgqi->NextEntryOffset = 0; + pgqi->SidLength = RtlLengthSid (sid); + RtlCopySid (RtlLengthSid (sid), &pgqi->Sid, sid); + status = NtQueryQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE, + TRUE, pgqi, PGQI_SIZE, + NULL, TRUE); + if (!NT_SUCCESS (status)) + break; + __try + { + struct dqblk *dq = (struct dqblk *) addr; + + if (!(dq->dqb_valid & QIF_BLIMITS)) + break; + pfqi->QuotaLimit.QuadPart = dq->dqb_bhardlimit; + if (pfqi->QuotaLimit.QuadPart != -1) + pfqi->QuotaLimit.QuadPart *= BLOCK_SIZE; + pfqi->QuotaThreshold.QuadPart = dq->dqb_bsoftlimit; + if (pfqi->QuotaThreshold.QuadPart != -1) + pfqi->QuotaThreshold.QuadPart *= BLOCK_SIZE; + } + __except (EFAULT) + { + ret = -1; + break; + } + __endtry + status = NtSetQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE); + } + break; + } + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + ret = -1; + } + return ret; +} diff --git a/winsup/cygwin/release/1.7.33 b/winsup/cygwin/release/1.7.33 new file mode 100644 index 000000000..0875e48a9 --- /dev/null +++ b/winsup/cygwin/release/1.7.33 @@ -0,0 +1,80 @@ +What's new: +----------- + +- /proc/cygdrive is a new symlink pointing to the current cygdrive prefix. + This can be utilized in scripts to access paths via cygdrive prefix, even + if the cygdrive prefix has been changed by the user. + +- /proc/partitions now prints the windows mount points the device is mounted + on. This allows to recognize the underlying Windows devices of the Cygwin + raw device names. + +- New API: quotactl, designed after the Linux/BSD function, but severely + restricted: Windows only supports user block quotas on NTFS, no group + quotas, no inode quotas, no time constraints. + +- New APIs: ffsl, ffsll (glibc extensions). + +- New API: stime (SVr4). + + +What changed: +------------- + +- New internal exception handling based on SEH on 64 bit Cygwin. + +- When exec'ing applications, check if $PATH exists and is non-empty. If not, + add PATH variable with Cygwin installation directory as content to Windows + environment to allow loading of Cygwin system DLLs. + +- Disable CYGWIN "dosfilewarning" option by default. + +- Improve various header files for C++- and standards-compliance. + +- Doug Lea malloc implementation update from 2.8.3 to the latest 2.8.6. + +- atexit is now exported as statically linked function from libcygwin.a. + This allows reliable access to the DSO handle of the caller for newly + built executables. The former atexit entry point into the DLL remains + for backward compatibility only. + + +Bug Fixes +--------- + +- Per POSIX, dirfd(3) now returns EINVAL rather than EBADF on invalid + directory stream. + +- Fix a resource leak in rmdir(2). + +- Fix fchmod(2)/fchown(2)/fsetxattr(2) in case the file got renamed after + open and before calling one of the affected functions. + Addresses: https://cygwin.com/ml/cygwin/2014-08/msg00517.html + +- Handle Netapp-specific problem in statvfs(2)/fstatvfs(2). + Addresses: https://cygwin.com/ml/cygwin/2014-06/msg00425.html + +- Fix chown(2) on ptys in a corner case. + +- Generate correct error when a path is inaccessible due to missing permissions. + Addresses: https://cygwin.com/ml/cygwin-developers/2014-10/msg00010.html + +- Don't hang in accept calls if socket is no listener. Set errno to EINVAL + instead. + +- Don't allow seeking on serial lines and sockets. Set errno to ESPIPE + instead. + Addresses: https://cygwin.com/ml/cygwin/2014-08/msg00319.html + +- Fix output of /proc//statm. + +- Fix a SEGV in cygcheck if the environment variable COMSPEC is not, or + incorrectly set. + Addresses: https://cygwin.com/ml/cygwin/2014-10/msg00292.html + +- Fix a SEGV in some 64 bit applications explicitely dlclosing DLLs. + Addresses: https://cygwin.com/ml/cygwin/2014-10/msg00402.html + +- Fix -fuse-cxa-atexit handling where dlclose fails to trigger calling + global dtors in dynamically loaded modules in C++ applications (and + thus another potential SEGV). diff --git a/winsup/cygwin/resource.cc b/winsup/cygwin/resource.cc index 25e87e6e0..27ba4d4be 100644 --- a/winsup/cygwin/resource.cc +++ b/winsup/cygwin/resource.cc @@ -1,7 +1,7 @@ /* resource.cc: getrusage () and friends. Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2005, 2008, 2009, 2010, - 2011, 2012, 2013 Red Hat, Inc. + 2011, 2012, 2013, 2014 Red Hat, Inc. Written by Steve Chamberlain (sac@cygnus.com), Doug Evans (dje@cygnus.com), Geoffrey Noer (noer@cygnus.com) of Cygnus Support. @@ -116,53 +116,51 @@ getrlimit (int resource, struct rlimit *rlp) { MEMORY_BASIC_INFORMATION m; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - rlp->rlim_cur = RLIM_INFINITY; - rlp->rlim_max = RLIM_INFINITY; - - switch (resource) + __try { - case RLIMIT_CPU: - case RLIMIT_FSIZE: - case RLIMIT_DATA: - case RLIMIT_AS: - break; - case RLIMIT_STACK: - if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m)) - debug_printf ("couldn't get stack info, returning def.values. %E"); - else + rlp->rlim_cur = RLIM_INFINITY; + rlp->rlim_max = RLIM_INFINITY; + + switch (resource) { - rlp->rlim_cur = (rlim_t) &m - (rlim_t) m.AllocationBase; - rlp->rlim_max = (rlim_t) m.BaseAddress + m.RegionSize - - (rlim_t) m.AllocationBase; + case RLIMIT_CPU: + case RLIMIT_FSIZE: + case RLIMIT_DATA: + case RLIMIT_AS: + break; + case RLIMIT_STACK: + if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m)) + debug_printf ("couldn't get stack info, returning def.values. %E"); + else + { + rlp->rlim_cur = (rlim_t) &m - (rlim_t) m.AllocationBase; + rlp->rlim_max = (rlim_t) m.BaseAddress + m.RegionSize + - (rlim_t) m.AllocationBase; + } + break; + case RLIMIT_NOFILE: + rlp->rlim_cur = getdtablesize (); + if (rlp->rlim_cur < OPEN_MAX) + rlp->rlim_cur = OPEN_MAX; + rlp->rlim_max = OPEN_MAX_MAX; + break; + case RLIMIT_CORE: + rlp->rlim_cur = cygheap->rlim_core; + break; + default: + set_errno (EINVAL); + __leave; } - break; - case RLIMIT_NOFILE: - rlp->rlim_cur = getdtablesize (); - if (rlp->rlim_cur < OPEN_MAX) - rlp->rlim_cur = OPEN_MAX; - rlp->rlim_max = OPEN_MAX_MAX; - break; - case RLIMIT_CORE: - rlp->rlim_cur = cygheap->rlim_core; - break; - default: - set_errno (EINVAL); - return -1; + return 0; } - return 0; + __except (EFAULT) {} + __endtry + return -1; } extern "C" int setrlimit (int resource, const struct rlimit *rlp) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - struct rlimit oldlimits; /* Check if the request is to actually change the resource settings. @@ -170,29 +168,35 @@ setrlimit (int resource, const struct rlimit *rlp) if (getrlimit (resource, &oldlimits) < 0) return -1; - if (oldlimits.rlim_cur == rlp->rlim_cur && - oldlimits.rlim_max == rlp->rlim_max) - /* No change in resource requirements, succeed immediately */ - return 0; - - if (rlp->rlim_cur > rlp->rlim_max) + __try { - set_errno (EINVAL); - return -1; - } + if (oldlimits.rlim_cur == rlp->rlim_cur && + oldlimits.rlim_max == rlp->rlim_max) + /* No change in resource requirements, succeed immediately */ + return 0; - switch (resource) - { - case RLIMIT_CORE: - cygheap->rlim_core = rlp->rlim_cur; - break; - case RLIMIT_NOFILE: - if (rlp->rlim_cur != RLIM_INFINITY) - return setdtablesize (rlp->rlim_cur); - break; - default: - set_errno (EINVAL); - return -1; + if (rlp->rlim_cur > rlp->rlim_max) + { + set_errno (EINVAL); + __leave; + } + + switch (resource) + { + case RLIMIT_CORE: + cygheap->rlim_core = rlp->rlim_cur; + break; + case RLIMIT_NOFILE: + if (rlp->rlim_cur != RLIM_INFINITY) + return setdtablesize (rlp->rlim_cur); + break; + default: + set_errno (EINVAL); + __leave; + } + return 0; } - return 0; + __except (EFAULT) + __endtry + return -1; } diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 251e9609e..606920da0 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -40,7 +40,7 @@ LONG get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool justcreated) { - NTSTATUS status; + NTSTATUS status = STATUS_SUCCESS; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; ULONG len = SD_MAXIMUM_SIZE, rlen; @@ -57,20 +57,19 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, sd, len, &rlen); if (!NT_SUCCESS (status)) - { - debug_printf ("NtQuerySecurityObject (%S), status %y", - pc.get_nt_native_path (), status); - fh = NULL; - } + debug_printf ("NtQuerySecurityObject (%S), status %y", + pc.get_nt_native_path (), status); } /* If the handle was NULL, or fetching with the original handle didn't work, try to reopen the file with READ_CONTROL and fetch the security descriptor using that handle. */ - if (!fh) + if (!fh || !NT_SUCCESS (status)) { status = NtOpenFile (&fh, READ_CONTROL, - pc.get_object_attr (attr, sec_none_nih), &io, - FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); + fh ? pc.init_reopen_attr (attr, fh) + : pc.get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS (status)) { sd.free (); @@ -217,8 +216,10 @@ set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown) OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER : 0) | WRITE_DAC, - pc.get_object_attr (attr, sec_none_nih), - &io, FILE_SHARE_VALID_FLAGS, + fh ? pc.init_reopen_attr (attr, fh) + : pc.get_object_attr (attr, sec_none_nih), + &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS (status)) { @@ -506,7 +507,7 @@ alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute, /* NOTE: If the high bit of attribute is set, we have just created a file or directory. See below for an explanation. */ - debug_printf("uid %u, gid %u, attribute %y", uid, gid, attribute); + debug_printf("uid %u, gid %u, attribute 0%o", uid, gid, attribute); /* Get owner and group from current security descriptor. */ PSID cur_owner_sid = NULL; @@ -964,7 +965,7 @@ set_file_attribute (HANDLE handle, path_conv &pc, } else ret = 0; - syscall_printf ("%d = set_file_attribute(%S, %d, %d, %y)", + syscall_printf ("%d = set_file_attribute(%S, %d, %d, 0%o)", ret, pc.get_nt_native_path (), uid, gid, attribute); return ret; } diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index e83d9a024..75eb72637 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1,7 +1,7 @@ /* select.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -230,21 +230,24 @@ pselect(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval tv; sigset_t oldset = _my_tls.sigmask; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (ts) + __try { - tv.tv_sec = ts->tv_sec; - tv.tv_usec = ts->tv_nsec / 1000; + if (ts) + { + tv.tv_sec = ts->tv_sec; + tv.tv_usec = ts->tv_nsec / 1000; + } + if (set) + set_signal_mask (_my_tls.sigmask, *set); + int ret = cygwin_select (maxfds, readfds, writefds, exceptfds, + ts ? &tv : NULL); + if (set) + set_signal_mask (_my_tls.sigmask, oldset); + return ret; } - if (set) - set_signal_mask (_my_tls.sigmask, *set); - int ret = cygwin_select (maxfds, readfds, writefds, exceptfds, - ts ? &tv : NULL); - if (set) - set_signal_mask (_my_tls.sigmask, oldset); - return ret; + __except (EFAULT) {} + __endtry + return -1; } /* Call cleanup functions for all inspected fds. Gets rid of any @@ -470,6 +473,9 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds, if (me->read_selected && me->read_ready) { UNIX_FD_SET (me->fd, readfds); + /* Special AF_LOCAL handling. */ + if ((sock = me->fh->is_socket ()) && sock->connect_state () == connect_pending) + sock->connect_state (connected); ready++; } if (me->write_selected && me->write_ready) diff --git a/winsup/cygwin/sem.cc b/winsup/cygwin/sem.cc index 2c172990a..f7a5db674 100644 --- a/winsup/cygwin/sem.cc +++ b/winsup/cygwin/sem.cc @@ -1,6 +1,6 @@ /* sem.cc: XSI IPC interface for Cygwin. - Copyright 2002, 2003, 2004, 2005, 2008, 2009, 2012 Red Hat, Inc. + Copyright 2002, 2003, 2004, 2005, 2008, 2009, 2012, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -85,19 +85,22 @@ semctl (int semid, int semnum, int cmd, ...) } syscall_printf ("semctl (semid = %d, semnum = %d, cmd = %d, arg = %p)", semid, semnum, cmd, arg.buf); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - client_request_sem request (semid, semnum, cmd, &arg); - if (request.make_request () == -1 || request.retval () == -1) + __try { - syscall_printf ("-1 [%d] = semctl ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; + client_request_sem request (semid, semnum, cmd, &arg); + if (request.make_request () == -1 || request.retval () == -1) + { + syscall_printf ("-1 [%d] = semctl ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; + } + return request.retval (); } - return request.retval (); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -122,17 +125,20 @@ semop (int semid, struct sembuf *sops, size_t nsops) { syscall_printf ("semop (semid = %d, sops = %p, nsops = %ld)", semid, sops, nsops); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - client_request_sem request (semid, sops, nsops); - if (request.make_request () == -1 || request.retval () == -1) + __try { - syscall_printf ("-1 [%d] = semop ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; + client_request_sem request (semid, sops, nsops); + if (request.make_request () == -1 || request.retval () == -1) + { + syscall_printf ("-1 [%d] = semop ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; + } + return request.retval (); } - return request.retval (); + __except (EFAULT) {} + __endtry + return -1; } diff --git a/winsup/cygwin/setlsapwd.cc b/winsup/cygwin/setlsapwd.cc index 5bc5c5e25..a906412ca 100644 --- a/winsup/cygwin/setlsapwd.cc +++ b/winsup/cygwin/setlsapwd.cc @@ -94,7 +94,7 @@ setlsapwd (const char *passwd, const char *username) } if (data_buf) { - memset (data.Buffer, 0, data.Length); + RtlSecureZeroMemory (data.Buffer, data.Length); free (data_buf); } } diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc index 03213781a..868545e3b 100644 --- a/winsup/cygwin/shm.cc +++ b/winsup/cygwin/shm.cc @@ -260,41 +260,45 @@ shmctl (int shmid, int cmd, struct shmid_ds *buf) { syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = %p)", shmid, cmd, buf); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - client_request_shm request (shmid, cmd, buf); - if (request.make_request () == -1 || request.retval () == -1) + __try { - syscall_printf ("-1 [%d] = shmctl ()", request.error_code ()); - set_errno (request.error_code ()); - if (request.error_code () == ENOSYS) - raise (SIGSYS); - return -1; - } - if (cmd == IPC_RMID) - { - /* Cleanup */ - shm_shmid_list *ssh_entry, *ssh_next_entry; - SLIST_LOCK (); - SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry) + client_request_shm request (shmid, cmd, buf); + if (request.make_request () == -1 || request.retval () == -1) { - if (ssh_entry->shmid == shmid) - { - /* Remove this entry from the list and close the handle - only if it's not in use anymore. */ - if (ssh_entry->ref_count <= 0) - { - SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next); - CloseHandle (ssh_entry->hdl); - delete ssh_entry; - } - break; - } + syscall_printf ("-1 [%d] = shmctl ()", request.error_code ()); + set_errno (request.error_code ()); + if (request.error_code () == ENOSYS) + raise (SIGSYS); + __leave; } - SLIST_UNLOCK (); + if (cmd == IPC_RMID) + { + /* Cleanup */ + shm_shmid_list *ssh_entry, *ssh_next_entry; + SLIST_LOCK (); + SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry) + { + if (ssh_entry->shmid == shmid) + { + /* Remove this entry from the list and close the handle + only if it's not in use anymore. */ + if (ssh_entry->ref_count <= 0) + { + SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, + ssh_next); + CloseHandle (ssh_entry->hdl); + delete ssh_entry; + } + break; + } + } + SLIST_UNLOCK (); + } + return request.retval (); } - return request.retval (); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index dc01337c2..d2ca81e00 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -1,7 +1,7 @@ /* signal.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com Significant changes by Sergey Okhapkin @@ -197,33 +197,37 @@ handle_sigprocmask (int how, const sigset_t *set, sigset_t *oldset, sigset_t& op return EINVAL; } - myfault efault; - if (efault.faulted (EFAULT)) - return EFAULT; - - if (oldset) - *oldset = opmask; - - if (set) - { - sigset_t newmask = opmask; - switch (how) + __try { - case SIG_BLOCK: - /* add set to current mask */ - newmask |= *set; - break; - case SIG_UNBLOCK: - /* remove set from current mask */ - newmask &= ~*set; - break; - case SIG_SETMASK: - /* just set it */ - newmask = *set; - break; + if (oldset) + *oldset = opmask; + + if (set) + { + sigset_t newmask = opmask; + switch (how) + { + case SIG_BLOCK: + /* add set to current mask */ + newmask |= *set; + break; + case SIG_UNBLOCK: + /* remove set from current mask */ + newmask &= ~*set; + break; + case SIG_SETMASK: + /* just set it */ + newmask = *set; + break; + } + set_signal_mask (opmask, newmask); } - set_signal_mask (opmask, newmask); } + __except (EFAULT) + { + return EFAULT; + } + __endtry return 0; } @@ -382,8 +386,7 @@ sigaction_worker (int sig, const struct sigaction *newact, struct sigaction *oldact, bool isinternal) { int res = -1; - myfault efault; - if (!efault.faulted (EFAULT)) + __try { sig_dispatch_pending (); /* check that sig is in right range */ @@ -394,14 +397,17 @@ sigaction_worker (int sig, const struct sigaction *newact, struct sigaction oa = global_sigs[sig]; if (!newact) - sigproc_printf ("signal %d, newact %p, oa %p", sig, newact, oa, oa.sa_handler); + sigproc_printf ("signal %d, newact %p, oa %p", + sig, newact, oa, oa.sa_handler); else { - sigproc_printf ("signal %d, newact %p (handler %p), oa %p", sig, newact, newact->sa_handler, oa, oa.sa_handler); + sigproc_printf ("signal %d, newact %p (handler %p), oa %p", + sig, newact, newact->sa_handler, oa, + oa.sa_handler); if (sig == SIGKILL || sig == SIGSTOP) { set_errno (EINVAL); - goto out; + __leave; } struct sigaction na = *newact; struct sigaction& gs = global_sigs[sig]; @@ -430,8 +436,8 @@ sigaction_worker (int sig, const struct sigaction *newact, res = 0; } } - -out: + __except (EFAULT) {} + __endtry return res; } @@ -560,41 +566,41 @@ sigwait (const sigset_t *set, int *sig_ptr) extern "C" int sigwaitinfo (const sigset_t *set, siginfo_t *info) { + int res = -1; + pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - return EFAULT; - - set_signal_mask (_my_tls.sigwait_mask, *set); - sig_dispatch_pending (true); - - int res; - switch (cygwait (NULL, cw_infinite, cw_sig_eintr | cw_cancel | cw_cancel_self)) + __try { - case WAIT_SIGNALED: - if (!sigismember (set, _my_tls.infodata.si_signo)) - { - set_errno (EINTR); - res = -1; - } - else - { - _my_tls.lock (); - if (info) - *info = _my_tls.infodata; - res = _my_tls.infodata.si_signo; - _my_tls.sig = 0; - if (_my_tls.retaddr () == (__stack_t) sigdelayed) - _my_tls.pop (); - _my_tls.unlock (); - } - break; - default: - __seterrno (); - res = -1; - } + set_signal_mask (_my_tls.sigwait_mask, *set); + sig_dispatch_pending (true); + switch (cygwait (NULL, cw_infinite, cw_sig_eintr | cw_cancel | cw_cancel_self)) + { + case WAIT_SIGNALED: + if (!sigismember (set, _my_tls.infodata.si_signo)) + set_errno (EINTR); + else + { + _my_tls.lock (); + if (info) + *info = _my_tls.infodata; + res = _my_tls.infodata.si_signo; + _my_tls.sig = 0; + if (_my_tls.retaddr () == (__stack_t) sigdelayed) + _my_tls.pop (); + _my_tls.unlock (); + } + break; + default: + __seterrno (); + break; + } + } + __except (EFAULT) { + res = -1; + } + __endtry sigproc_printf ("returning signal %d", res); return res; } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index d1b1fbad6..56f36a3e6 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -1,7 +1,7 @@ /* spawn.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -330,552 +330,552 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, STARTUPINFOW si = {}; int looped = 0; - myfault efault; system_call_handle system_call (mode == _P_SYSTEM); - if (efault.faulted ()) + + __try + { + child_info_types chtype; + if (mode == _P_OVERLAY) + chtype = _CH_EXEC; + else + chtype = _CH_SPAWN; + + moreinfo = cygheap_exec_info::alloc (); + + /* CreateProcess takes one long string that is the command line (sigh). + We need to quote any argument that has whitespace or embedded "'s. */ + + int ac; + for (ac = 0; argv[ac]; ac++) + /* nothing */; + + int err; + const char *ext; + if ((ext = perhaps_suffix (prog_arg, real_path, err, FE_NADA)) == NULL) + { + set_errno (err); + res = -1; + __leave; + } + + res = newargv.setup (prog_arg, real_path, ext, ac, argv, p_type_exec); + + if (res) + __leave; + + if (!real_path.iscygexec () && ::cygheap->cwd.get_error ()) + { + small_printf ("Error: Current working directory %s.\n" + "Can't start native Windows application from here.\n\n", + ::cygheap->cwd.get_error_desc ()); + set_errno (::cygheap->cwd.get_error ()); + res = -1; + __leave; + } + + if (ac == 3 && argv[1][0] == '/' && tolower (argv[1][1]) == 'c' && + (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe"))) + { + real_path.check (prog_arg); + cmd.add ("\""); + if (!real_path.error) + cmd.add (real_path.get_win32 ()); + else + cmd.add (argv[0]); + cmd.add ("\""); + cmd.add (" "); + cmd.add (argv[1]); + cmd.add (" "); + cmd.add (argv[2]); + real_path.set_path (argv[0]); + null_app_name = true; + } + else + { + if (real_path.iscygexec ()) + { + moreinfo->argc = newargv.argc; + moreinfo->argv = newargv; + } + if ((wincmdln || !real_path.iscygexec ()) + && !cmd.fromargv (newargv, real_path.get_win32 (), + real_path.iscygexec ())) + { + res = -1; + __leave; + } + + + if (mode != _P_OVERLAY || !real_path.iscygexec () + || !DuplicateHandle (GetCurrentProcess (), myself.shared_handle (), + GetCurrentProcess (), &moreinfo->myself_pinfo, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + moreinfo->myself_pinfo = NULL; + else + VerifyHandle (moreinfo->myself_pinfo); + } + + PROCESS_INFORMATION pi; + pi.hProcess = pi.hThread = NULL; + pi.dwProcessId = pi.dwThreadId = 0; + + /* Set up needed handles for stdio */ + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false); + si.hStdOutput = handle ((in__stdout < 0 ? 1 : in__stdout), true); + si.hStdError = handle (2, true); + + si.cb = sizeof (si); + + c_flags = GetPriorityClass (GetCurrentProcess ()); + sigproc_printf ("priority class %d", c_flags); + + c_flags |= CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT; + + if (wincap.has_program_compatibility_assistant ()) + { + /* We're adding the CREATE_BREAKAWAY_FROM_JOB flag here to workaround + issues with the "Program Compatibility Assistant (PCA) Service" + starting with Windows Vista. For some reason, when starting long + running sessions from mintty(*), the affected svchost.exe process + takes more and more memory and at one point takes over the CPU. At + this point the machine becomes unresponsive. The only way to get + back to normal is to stop the entire mintty session, or to stop the + PCA service. However, a process which is controlled by PCA is part + of a compatibility job, which allows child processes to break away + from the job. This helps to avoid this issue. + + First we call IsProcessInJob. It fetches the information whether or + not we're part of a job 20 times faster than QueryInformationJobObject. + + (*) Note that this is not mintty's fault. It has just been observed + with mintty in the first place. See the archives for more info: + http://cygwin.com/ml/cygwin-developers/2012-02/msg00018.html */ + + JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo; + BOOL is_in_job; + + if (IsProcessInJob (GetCurrentProcess (), NULL, &is_in_job) + && is_in_job + && QueryInformationJobObject (NULL, JobObjectBasicLimitInformation, + &jobinfo, sizeof jobinfo, NULL) + && (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK + | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))) + { + debug_printf ("Add CREATE_BREAKAWAY_FROM_JOB"); + c_flags |= CREATE_BREAKAWAY_FROM_JOB; + } + } + + if (mode == _P_DETACH) + c_flags |= DETACHED_PROCESS; + else + fhandler_console::need_invisible (); + + if (mode != _P_OVERLAY) + myself->exec_sendsig = NULL; + else + { + /* Reset sendsig so that any process which wants to send a signal + to this pid will wait for the new process to become active. + Save the old value in case the exec fails. */ + if (!myself->exec_sendsig) + { + myself->exec_sendsig = myself->sendsig; + myself->exec_dwProcessId = myself->dwProcessId; + myself->sendsig = NULL; + reset_sendsig = true; + } + } + + if (null_app_name) + runpath = NULL; + else + { + USHORT len = real_path.get_nt_native_path ()->Length / sizeof (WCHAR); + if (RtlEqualUnicodePathPrefix (real_path.get_nt_native_path (), + &ro_u_natp, FALSE)) + { + runpath = real_path.get_wide_win32_path (runpath); + /* If the executable path length is < MAX_PATH, make sure the long + path win32 prefix is removed from the path to make subsequent + not long path aware native Win32 child processes happy. */ + if (len < MAX_PATH + 4) + { + if (runpath[5] == ':') + runpath += 4; + else if (len < MAX_PATH + 6) + *(runpath += 6) = L'\\'; + } + } + else if (len < NT_MAX_PATH - ro_u_globalroot.Length / sizeof (WCHAR)) + { + UNICODE_STRING rpath; + + RtlInitEmptyUnicodeString (&rpath, runpath, + (NT_MAX_PATH - 1) * sizeof (WCHAR)); + RtlCopyUnicodeString (&rpath, &ro_u_globalroot); + RtlAppendUnicodeStringToString (&rpath, + real_path.get_nt_native_path ()); + } + else + { + set_errno (ENAMETOOLONG); + res = -1; + __leave; + } + } + + cygbench ("spawn-worker"); + + if (!real_path.iscygexec()) + ::cygheap->fdtab.set_file_pointers_for_exec (); + + moreinfo->envp = build_env (envp, envblock, moreinfo->envc, + real_path.iscygexec ()); + if (!moreinfo->envp || !envblock) + { + set_errno (E2BIG); + res = -1; + __leave; + } + set (chtype, real_path.iscygexec ()); + __stdin = in__stdin; + __stdout = in__stdout; + record_children (); + + si.lpReserved2 = (LPBYTE) this; + si.cbReserved2 = sizeof (*this); + + /* Depends on set call above. + Some file types might need extra effort in the parent after CreateProcess + and before copying the datastructures to the child. So we have to start + the child in suspend state, unfortunately, to avoid a race condition. */ + if (!newargv.win16_exe + && (!iscygwin () || mode != _P_OVERLAY + || ::cygheap->fdtab.need_fixup_before ())) + c_flags |= CREATE_SUSPENDED; + /* If a native application should be spawned, we test here if the spawning + process is running in a console and, if so, if it's a foreground or + background process. If it's a background process, we start the native + process with the CREATE_NEW_PROCESS_GROUP flag set. This lets the native + process ignore Ctrl-C by default. If we don't do that, pressing Ctrl-C + in a console will break native processes running in the background, + because the Ctrl-C event is sent to all processes in the console, unless + they ignore it explicitely. CREATE_NEW_PROCESS_GROUP does that for us. */ + if (!iscygwin () && fhandler_console::exists () + && fhandler_console::tc_getpgid () != myself->pgid) + c_flags |= CREATE_NEW_PROCESS_GROUP; + refresh_cygheap (); + + if (mode == _P_DETACH) + /* all set */; + else if (mode != _P_OVERLAY || !my_wr_proc_pipe) + prefork (); + else + wr_proc_pipe = my_wr_proc_pipe; + + /* Don't allow child to inherit these handles if it's not a Cygwin program. + wr_proc_pipe will be injected later. parent won't be used by the child + so there is no reason for the child to have it open as it can confuse + ps into thinking that children of windows processes are all part of + the same "execed" process. + FIXME: Someday, make it so that parent is never created when starting + non-Cygwin processes. */ + if (!iscygwin ()) + { + SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation (parent, HANDLE_FLAG_INHERIT, 0); + } + /* FIXME: racy */ + if (mode != _P_OVERLAY) + SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, 0); + parent_winpid = GetCurrentProcessId (); + + loop: + /* When ruid != euid we create the new process under the current original + account and impersonate in child, this way maintaining the different + effective vs. real ids. + FIXME: If ruid != euid and ruid != saved_uid we currently give + up on ruid. The new process will have ruid == euid. */ + ::cygheap->user.deimpersonate (); + + if (!real_path.iscygexec () && mode == _P_OVERLAY) + myself->process_state |= PID_NOTCYGWIN; + + wchar_t wcmd[(size_t) cmd]; + if (!::cygheap->user.issetuid () + || (::cygheap->user.saved_uid == ::cygheap->user.real_uid + && ::cygheap->user.saved_gid == ::cygheap->user.real_gid + && !::cygheap->user.groups.issetgroups () + && !::cygheap->user.setuid_to_restricted)) + { + rc = CreateProcessW (runpath, /* image name - with full path */ + cmd.wcs (wcmd),/* what was passed to exec */ + &sec_none_nih, /* process security attrs */ + &sec_none_nih, /* thread security attrs */ + TRUE, /* inherit handles from parent */ + c_flags, + envblock, /* environment */ + NULL, + &si, + &pi); + } + else + { + /* Give access to myself */ + if (mode == _P_OVERLAY) + myself.set_acl(); + + WCHAR wstname[1024] = { L'\0' }; + HWINSTA hwst_orig = NULL, hwst = NULL; + HDESK hdsk_orig = NULL, hdsk = NULL; + PSECURITY_ATTRIBUTES sa; + DWORD n; + + hwst_orig = GetProcessWindowStation (); + hdsk_orig = GetThreadDesktop (GetCurrentThreadId ()); + GetUserObjectInformationW (hwst_orig, UOI_NAME, wstname, 1024, &n); + /* Prior to Vista it was possible to start a service with the + "Interact with desktop" flag. This started the service in the + interactive window station of the console. A big security + risk, but we don't want to disable this behaviour for older + OSes because it's still heavily used by some users. They have + been warned. */ + if (!::cygheap->user.setuid_to_restricted + && wcscasecmp (wstname, L"WinSta0") != 0) + { + WCHAR sid[128]; + + sa = sec_user ((PSECURITY_ATTRIBUTES) alloca (1024), + ::cygheap->user.sid ()); + /* We're creating a window station per user, not per logon session. + First of all we might not have a valid logon session for + the user (logon by create_token), and second, it doesn't + make sense in terms of security to create a new window + station for every logon of the same user. It just fills up + the system with window stations for no good reason. */ + hwst = CreateWindowStationW (::cygheap->user.get_windows_id (sid), 0, + GENERIC_READ | GENERIC_WRITE, sa); + if (!hwst) + system_printf ("CreateWindowStation failed, %E"); + else if (!SetProcessWindowStation (hwst)) + system_printf ("SetProcessWindowStation failed, %E"); + else if (!(hdsk = CreateDesktopW (L"Default", NULL, NULL, 0, + GENERIC_ALL, sa))) + system_printf ("CreateDesktop failed, %E"); + else + { + wcpcpy (wcpcpy (wstname, sid), L"\\Default"); + si.lpDesktop = wstname; + debug_printf ("Desktop: %W", si.lpDesktop); + } + } + + rc = CreateProcessAsUserW (::cygheap->user.primary_token (), + runpath, /* image name - with full path */ + cmd.wcs (wcmd),/* what was passed to exec */ + &sec_none_nih, /* process security attrs */ + &sec_none_nih, /* thread security attrs */ + TRUE, /* inherit handles from parent */ + c_flags, + envblock, /* environment */ + NULL, + &si, + &pi); + if (hwst) + { + SetProcessWindowStation (hwst_orig); + CloseWindowStation (hwst); + } + if (hdsk) + { + SetThreadDesktop (hdsk_orig); + CloseDesktop (hdsk); + } + } + + if (mode != _P_OVERLAY) + SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT); + + /* Set errno now so that debugging messages from it appear before our + final debugging message [this is a general rule for debugging + messages]. */ + if (!rc) + { + __seterrno (); + syscall_printf ("CreateProcess failed, %E"); + /* If this was a failed exec, restore the saved sendsig. */ + if (reset_sendsig) + { + myself->sendsig = myself->exec_sendsig; + myself->exec_sendsig = NULL; + } + myself->process_state &= ~PID_NOTCYGWIN; + /* Reset handle inheritance to default when the execution of a non-Cygwin + process fails. Only need to do this for _P_OVERLAY since the handle will + be closed otherwise. Don't need to do this for 'parent' since it will + be closed in every case. See FIXME above. */ + if (!iscygwin () && mode == _P_OVERLAY) + SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT); + if (wr_proc_pipe == my_wr_proc_pipe) + wr_proc_pipe = NULL; /* We still own it: don't nuke in destructor */ + + /* Restore impersonation. In case of _P_OVERLAY this isn't + allowed since it would overwrite child data. */ + if (mode != _P_OVERLAY) + ::cygheap->user.reimpersonate (); + + res = -1; + __leave; + } + + /* The CREATE_SUSPENDED case is handled below */ + if (iscygwin () && !(c_flags & CREATE_SUSPENDED)) + strace.write_childpid (pi.dwProcessId); + + /* Fixup the parent data structures if needed and resume the child's + main thread. */ + if (::cygheap->fdtab.need_fixup_before ()) + ::cygheap->fdtab.fixup_before_exec (pi.dwProcessId); + + if (mode != _P_OVERLAY) + cygpid = cygwin_pid (pi.dwProcessId); + else + cygpid = myself->pid; + + /* We print the original program name here so the user can see that too. */ + syscall_printf ("pid %d, prog_arg %s, cmd line %.9500s)", + rc ? cygpid : (unsigned int) -1, prog_arg, (const char *) cmd); + + /* Name the handle similarly to proc_subproc. */ + ProtectHandle1 (pi.hProcess, childhProc); + + if (mode == _P_OVERLAY) + { + myself->dwProcessId = pi.dwProcessId; + strace.execing = 1; + myself.hProcess = hExeced = pi.hProcess; + real_path.get_wide_win32_path (myself->progname); // FIXME: race? + sigproc_printf ("new process name %W", myself->progname); + if (!iscygwin ()) + close_all_files (); + } + else + { + myself->set_has_pgid_children (); + ProtectHandle (pi.hThread); + pinfo child (cygpid, + PID_IN_USE | (real_path.iscygexec () ? 0 : PID_NOTCYGWIN)); + if (!child) + { + syscall_printf ("pinfo failed"); + if (get_errno () != ENOMEM) + set_errno (EAGAIN); + res = -1; + __leave; + } + child->dwProcessId = pi.dwProcessId; + child.hProcess = pi.hProcess; + + real_path.get_wide_win32_path (child->progname); + /* FIXME: This introduces an unreferenced, open handle into the child. + The purpose is to keep the pid shared memory open so that all of + the fields filled out by child.remember do not disappear and so there + is not a brief period during which the pid is not available. + However, we should try to find another way to do this eventually. */ + DuplicateHandle (GetCurrentProcess (), child.shared_handle (), + pi.hProcess, NULL, 0, 0, DUPLICATE_SAME_ACCESS); + child->start_time = time (NULL); /* Register child's starting time. */ + child->nice = myself->nice; + postfork (child); + if (!child.remember (mode == _P_DETACH)) + { + /* FIXME: Child in strange state now */ + CloseHandle (pi.hProcess); + ForceCloseHandle (pi.hThread); + res = -1; + __leave; + } + } + + /* Start the child running */ + if (c_flags & CREATE_SUSPENDED) + { + /* Inject a non-inheritable wr_proc_pipe handle into child so that we + can accurately track when the child exits without keeping this + process waiting around for it to exit. */ + if (!iscygwin ()) + DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, NULL, + 0, false, DUPLICATE_SAME_ACCESS); + ResumeThread (pi.hThread); + if (iscygwin ()) + strace.write_childpid (pi.dwProcessId); + } + ForceCloseHandle (pi.hThread); + + sigproc_printf ("spawned windows pid %d", pi.dwProcessId); + + bool synced; + if ((mode == _P_DETACH || mode == _P_NOWAIT) && !iscygwin ()) + synced = false; + else + /* Just mark a non-cygwin process as 'synced'. We will still eventually + wait for it to exit in maybe_set_exit_code_from_windows(). */ + synced = iscygwin () ? sync (pi.dwProcessId, pi.hProcess, INFINITE) : true; + + switch (mode) + { + case _P_OVERLAY: + myself.hProcess = pi.hProcess; + if (!synced) + { + if (!proc_retry (pi.hProcess)) + { + looped++; + goto loop; + } + close_all_files (true); + } + else + { + if (iscygwin ()) + close_all_files (true); + if (!my_wr_proc_pipe + && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) + wait_for_myself (); + } + myself.exit (EXITCODE_NOSET); + break; + case _P_WAIT: + case _P_SYSTEM: + system_call.arm (); + if (waitpid (cygpid, &res, 0) != cygpid) + res = -1; + break; + case _P_DETACH: + res = 0; /* Lost all memory of this child. */ + break; + case _P_NOWAIT: + case _P_NOWAITO: + case _P_VFORK: + res = cygpid; + break; + default: + break; + } + } + __except (NO_ERROR) { if (get_errno () == ENOMEM) set_errno (E2BIG); else set_errno (EFAULT); res = -1; - goto out; } - - child_info_types chtype; - if (mode == _P_OVERLAY) - chtype = _CH_EXEC; - else - chtype = _CH_SPAWN; - - moreinfo = cygheap_exec_info::alloc (); - - /* CreateProcess takes one long string that is the command line (sigh). - We need to quote any argument that has whitespace or embedded "'s. */ - - int ac; - for (ac = 0; argv[ac]; ac++) - /* nothing */; - - int err; - const char *ext; - if ((ext = perhaps_suffix (prog_arg, real_path, err, FE_NADA)) == NULL) - { - set_errno (err); - res = -1; - goto out; - } - - res = newargv.setup (prog_arg, real_path, ext, ac, argv, p_type_exec); - - if (res) - goto out; - - if (!real_path.iscygexec () && ::cygheap->cwd.get_error ()) - { - small_printf ("Error: Current working directory %s.\n" - "Can't start native Windows application from here.\n\n", - ::cygheap->cwd.get_error_desc ()); - set_errno (::cygheap->cwd.get_error ()); - res = -1; - goto out; - } - - if (ac == 3 && argv[1][0] == '/' && tolower (argv[1][1]) == 'c' && - (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe"))) - { - real_path.check (prog_arg); - cmd.add ("\""); - if (!real_path.error) - cmd.add (real_path.get_win32 ()); - else - cmd.add (argv[0]); - cmd.add ("\""); - cmd.add (" "); - cmd.add (argv[1]); - cmd.add (" "); - cmd.add (argv[2]); - real_path.set_path (argv[0]); - null_app_name = true; - } - else - { - if (real_path.iscygexec ()) - { - moreinfo->argc = newargv.argc; - moreinfo->argv = newargv; - } - if ((wincmdln || !real_path.iscygexec ()) - && !cmd.fromargv (newargv, real_path.get_win32 (), - real_path.iscygexec ())) - { - res = -1; - goto out; - } - - - if (mode != _P_OVERLAY || !real_path.iscygexec () - || !DuplicateHandle (GetCurrentProcess (), myself.shared_handle (), - GetCurrentProcess (), &moreinfo->myself_pinfo, - 0, TRUE, DUPLICATE_SAME_ACCESS)) - moreinfo->myself_pinfo = NULL; - else - VerifyHandle (moreinfo->myself_pinfo); - } - - PROCESS_INFORMATION pi; - pi.hProcess = pi.hThread = NULL; - pi.dwProcessId = pi.dwThreadId = 0; - - /* Set up needed handles for stdio */ - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false); - si.hStdOutput = handle ((in__stdout < 0 ? 1 : in__stdout), true); - si.hStdError = handle (2, true); - - si.cb = sizeof (si); - - c_flags = GetPriorityClass (GetCurrentProcess ()); - sigproc_printf ("priority class %d", c_flags); - - c_flags |= CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT; - - if (wincap.has_program_compatibility_assistant ()) - { - /* We're adding the CREATE_BREAKAWAY_FROM_JOB flag here to workaround - issues with the "Program Compatibility Assistant (PCA) Service" - starting with Windows Vista. For some reason, when starting long - running sessions from mintty(*), the affected svchost.exe process - takes more and more memory and at one point takes over the CPU. At - this point the machine becomes unresponsive. The only way to get - back to normal is to stop the entire mintty session, or to stop the - PCA service. However, a process which is controlled by PCA is part - of a compatibility job, which allows child processes to break away - from the job. This helps to avoid this issue. - - First we call IsProcessInJob. It fetches the information whether or - not we're part of a job 20 times faster than QueryInformationJobObject. - - (*) Note that this is not mintty's fault. It has just been observed - with mintty in the first place. See the archives for more info: - http://cygwin.com/ml/cygwin-developers/2012-02/msg00018.html */ - - JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo; - BOOL is_in_job; - - if (IsProcessInJob (GetCurrentProcess (), NULL, &is_in_job) - && is_in_job - && QueryInformationJobObject (NULL, JobObjectBasicLimitInformation, - &jobinfo, sizeof jobinfo, NULL) - && (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK - | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))) - { - debug_printf ("Add CREATE_BREAKAWAY_FROM_JOB"); - c_flags |= CREATE_BREAKAWAY_FROM_JOB; - } - } - - if (mode == _P_DETACH) - c_flags |= DETACHED_PROCESS; - else - fhandler_console::need_invisible (); - - if (mode != _P_OVERLAY) - myself->exec_sendsig = NULL; - else - { - /* Reset sendsig so that any process which wants to send a signal - to this pid will wait for the new process to become active. - Save the old value in case the exec fails. */ - if (!myself->exec_sendsig) - { - myself->exec_sendsig = myself->sendsig; - myself->exec_dwProcessId = myself->dwProcessId; - myself->sendsig = NULL; - reset_sendsig = true; - } - } - - if (null_app_name) - runpath = NULL; - else - { - USHORT len = real_path.get_nt_native_path ()->Length / sizeof (WCHAR); - if (RtlEqualUnicodePathPrefix (real_path.get_nt_native_path (), - &ro_u_natp, FALSE)) - { - runpath = real_path.get_wide_win32_path (runpath); - /* If the executable path length is < MAX_PATH, make sure the long - path win32 prefix is removed from the path to make subsequent - not long path aware native Win32 child processes happy. */ - if (len < MAX_PATH + 4) - { - if (runpath[5] == ':') - runpath += 4; - else if (len < MAX_PATH + 6) - *(runpath += 6) = L'\\'; - } - } - else if (len < NT_MAX_PATH - ro_u_globalroot.Length / sizeof (WCHAR)) - { - UNICODE_STRING rpath; - - RtlInitEmptyUnicodeString (&rpath, runpath, - (NT_MAX_PATH - 1) * sizeof (WCHAR)); - RtlCopyUnicodeString (&rpath, &ro_u_globalroot); - RtlAppendUnicodeStringToString (&rpath, - real_path.get_nt_native_path ()); - } - else - { - set_errno (ENAMETOOLONG); - res = -1; - goto out; - } - } - - cygbench ("spawn-worker"); - - if (!real_path.iscygexec()) - ::cygheap->fdtab.set_file_pointers_for_exec (); - - moreinfo->envp = build_env (envp, envblock, moreinfo->envc, - real_path.iscygexec ()); - if (!moreinfo->envp || !envblock) - { - set_errno (E2BIG); - res = -1; - goto out; - } - set (chtype, real_path.iscygexec ()); - __stdin = in__stdin; - __stdout = in__stdout; - record_children (); - - si.lpReserved2 = (LPBYTE) this; - si.cbReserved2 = sizeof (*this); - - /* Depends on set call above. - Some file types might need extra effort in the parent after CreateProcess - and before copying the datastructures to the child. So we have to start - the child in suspend state, unfortunately, to avoid a race condition. */ - if (!newargv.win16_exe - && (!iscygwin () || mode != _P_OVERLAY - || ::cygheap->fdtab.need_fixup_before ())) - c_flags |= CREATE_SUSPENDED; - /* If a native application should be spawned, we test here if the spawning - process is running in a console and, if so, if it's a foreground or - background process. If it's a background process, we start the native - process with the CREATE_NEW_PROCESS_GROUP flag set. This lets the native - process ignore Ctrl-C by default. If we don't do that, pressing Ctrl-C - in a console will break native processes running in the background, - because the Ctrl-C event is sent to all processes in the console, unless - they ignore it explicitely. CREATE_NEW_PROCESS_GROUP does that for us. */ - if (!iscygwin () && fhandler_console::exists () - && fhandler_console::tc_getpgid () != myself->pgid) - c_flags |= CREATE_NEW_PROCESS_GROUP; - refresh_cygheap (); - - if (mode == _P_DETACH) - /* all set */; - else if (mode != _P_OVERLAY || !my_wr_proc_pipe) - prefork (); - else - wr_proc_pipe = my_wr_proc_pipe; - - /* Don't allow child to inherit these handles if it's not a Cygwin program. - wr_proc_pipe will be injected later. parent won't be used by the child - so there is no reason for the child to have it open as it can confuse - ps into thinking that children of windows processes are all part of - the same "execed" process. - FIXME: Someday, make it so that parent is never created when starting - non-Cygwin processes. */ - if (!iscygwin ()) - { - SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, 0); - SetHandleInformation (parent, HANDLE_FLAG_INHERIT, 0); - } - /* FIXME: racy */ - if (mode != _P_OVERLAY) - SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, 0); - parent_winpid = GetCurrentProcessId (); - -loop: - /* When ruid != euid we create the new process under the current original - account and impersonate in child, this way maintaining the different - effective vs. real ids. - FIXME: If ruid != euid and ruid != saved_uid we currently give - up on ruid. The new process will have ruid == euid. */ - ::cygheap->user.deimpersonate (); - - if (!real_path.iscygexec () && mode == _P_OVERLAY) - myself->process_state |= PID_NOTCYGWIN; - - wchar_t wcmd[(size_t) cmd]; - if (!::cygheap->user.issetuid () - || (::cygheap->user.saved_uid == ::cygheap->user.real_uid - && ::cygheap->user.saved_gid == ::cygheap->user.real_gid - && !::cygheap->user.groups.issetgroups () - && !::cygheap->user.setuid_to_restricted)) - { - rc = CreateProcessW (runpath, /* image name - with full path */ - cmd.wcs (wcmd),/* what was passed to exec */ - &sec_none_nih, /* process security attrs */ - &sec_none_nih, /* thread security attrs */ - TRUE, /* inherit handles from parent */ - c_flags, - envblock, /* environment */ - NULL, - &si, - &pi); - } - else - { - /* Give access to myself */ - if (mode == _P_OVERLAY) - myself.set_acl(); - - WCHAR wstname[1024] = { L'\0' }; - HWINSTA hwst_orig = NULL, hwst = NULL; - HDESK hdsk_orig = NULL, hdsk = NULL; - PSECURITY_ATTRIBUTES sa; - DWORD n; - - hwst_orig = GetProcessWindowStation (); - hdsk_orig = GetThreadDesktop (GetCurrentThreadId ()); - GetUserObjectInformationW (hwst_orig, UOI_NAME, wstname, 1024, &n); - /* Prior to Vista it was possible to start a service with the - "Interact with desktop" flag. This started the service in the - interactive window station of the console. A big security - risk, but we don't want to disable this behaviour for older - OSes because it's still heavily used by some users. They have - been warned. */ - if (!::cygheap->user.setuid_to_restricted - && wcscasecmp (wstname, L"WinSta0") != 0) - { - WCHAR sid[128]; - - sa = sec_user ((PSECURITY_ATTRIBUTES) alloca (1024), - ::cygheap->user.sid ()); - /* We're creating a window station per user, not per logon session. - First of all we might not have a valid logon session for - the user (logon by create_token), and second, it doesn't - make sense in terms of security to create a new window - station for every logon of the same user. It just fills up - the system with window stations for no good reason. */ - hwst = CreateWindowStationW (::cygheap->user.get_windows_id (sid), 0, - GENERIC_READ | GENERIC_WRITE, sa); - if (!hwst) - system_printf ("CreateWindowStation failed, %E"); - else if (!SetProcessWindowStation (hwst)) - system_printf ("SetProcessWindowStation failed, %E"); - else if (!(hdsk = CreateDesktopW (L"Default", NULL, NULL, 0, - GENERIC_ALL, sa))) - system_printf ("CreateDesktop failed, %E"); - else - { - wcpcpy (wcpcpy (wstname, sid), L"\\Default"); - si.lpDesktop = wstname; - debug_printf ("Desktop: %W", si.lpDesktop); - } - } - - rc = CreateProcessAsUserW (::cygheap->user.primary_token (), - runpath, /* image name - with full path */ - cmd.wcs (wcmd),/* what was passed to exec */ - &sec_none_nih, /* process security attrs */ - &sec_none_nih, /* thread security attrs */ - TRUE, /* inherit handles from parent */ - c_flags, - envblock, /* environment */ - NULL, - &si, - &pi); - if (hwst) - { - SetProcessWindowStation (hwst_orig); - CloseWindowStation (hwst); - } - if (hdsk) - { - SetThreadDesktop (hdsk_orig); - CloseDesktop (hdsk); - } - } - - if (mode != _P_OVERLAY) - SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT); - - /* Set errno now so that debugging messages from it appear before our - final debugging message [this is a general rule for debugging - messages]. */ - if (!rc) - { - __seterrno (); - syscall_printf ("CreateProcess failed, %E"); - /* If this was a failed exec, restore the saved sendsig. */ - if (reset_sendsig) - { - myself->sendsig = myself->exec_sendsig; - myself->exec_sendsig = NULL; - } - myself->process_state &= ~PID_NOTCYGWIN; - /* Reset handle inheritance to default when the execution of a non-Cygwin - process fails. Only need to do this for _P_OVERLAY since the handle will - be closed otherwise. Don't need to do this for 'parent' since it will - be closed in every case. See FIXME above. */ - if (!iscygwin () && mode == _P_OVERLAY) - SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT); - if (wr_proc_pipe == my_wr_proc_pipe) - wr_proc_pipe = NULL; /* We still own it: don't nuke in destructor */ - - /* Restore impersonation. In case of _P_OVERLAY this isn't - allowed since it would overwrite child data. */ - if (mode != _P_OVERLAY) - ::cygheap->user.reimpersonate (); - - res = -1; - goto out; - } - - /* The CREATE_SUSPENDED case is handled below */ - if (iscygwin () && !(c_flags & CREATE_SUSPENDED)) - strace.write_childpid (pi.dwProcessId); - - /* Fixup the parent data structures if needed and resume the child's - main thread. */ - if (::cygheap->fdtab.need_fixup_before ()) - ::cygheap->fdtab.fixup_before_exec (pi.dwProcessId); - - if (mode != _P_OVERLAY) - cygpid = cygwin_pid (pi.dwProcessId); - else - cygpid = myself->pid; - - /* We print the original program name here so the user can see that too. */ - syscall_printf ("pid %d, prog_arg %s, cmd line %.9500s)", - rc ? cygpid : (unsigned int) -1, prog_arg, (const char *) cmd); - - /* Name the handle similarly to proc_subproc. */ - ProtectHandle1 (pi.hProcess, childhProc); - - if (mode == _P_OVERLAY) - { - myself->dwProcessId = pi.dwProcessId; - strace.execing = 1; - myself.hProcess = hExeced = pi.hProcess; - real_path.get_wide_win32_path (myself->progname); // FIXME: race? - sigproc_printf ("new process name %W", myself->progname); - if (!iscygwin ()) - close_all_files (); - } - else - { - myself->set_has_pgid_children (); - ProtectHandle (pi.hThread); - pinfo child (cygpid, - PID_IN_USE | (real_path.iscygexec () ? 0 : PID_NOTCYGWIN)); - if (!child) - { - syscall_printf ("pinfo failed"); - if (get_errno () != ENOMEM) - set_errno (EAGAIN); - res = -1; - goto out; - } - child->dwProcessId = pi.dwProcessId; - child.hProcess = pi.hProcess; - - real_path.get_wide_win32_path (child->progname); - /* FIXME: This introduces an unreferenced, open handle into the child. - The purpose is to keep the pid shared memory open so that all of - the fields filled out by child.remember do not disappear and so there - is not a brief period during which the pid is not available. - However, we should try to find another way to do this eventually. */ - DuplicateHandle (GetCurrentProcess (), child.shared_handle (), - pi.hProcess, NULL, 0, 0, DUPLICATE_SAME_ACCESS); - child->start_time = time (NULL); /* Register child's starting time. */ - child->nice = myself->nice; - postfork (child); - if (!child.remember (mode == _P_DETACH)) - { - /* FIXME: Child in strange state now */ - CloseHandle (pi.hProcess); - ForceCloseHandle (pi.hThread); - res = -1; - goto out; - } - } - - /* Start the child running */ - if (c_flags & CREATE_SUSPENDED) - { - /* Inject a non-inheritable wr_proc_pipe handle into child so that we - can accurately track when the child exits without keeping this - process waiting around for it to exit. */ - if (!iscygwin ()) - DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, NULL, - 0, false, DUPLICATE_SAME_ACCESS); - ResumeThread (pi.hThread); - if (iscygwin ()) - strace.write_childpid (pi.dwProcessId); - } - ForceCloseHandle (pi.hThread); - - sigproc_printf ("spawned windows pid %d", pi.dwProcessId); - - bool synced; - if ((mode == _P_DETACH || mode == _P_NOWAIT) && !iscygwin ()) - synced = false; - else - /* Just mark a non-cygwin process as 'synced'. We will still eventually - wait for it to exit in maybe_set_exit_code_from_windows(). */ - synced = iscygwin () ? sync (pi.dwProcessId, pi.hProcess, INFINITE) : true; - - switch (mode) - { - case _P_OVERLAY: - myself.hProcess = pi.hProcess; - if (!synced) - { - if (!proc_retry (pi.hProcess)) - { - looped++; - goto loop; - } - close_all_files (true); - } - else - { - if (iscygwin ()) - close_all_files (true); - if (!my_wr_proc_pipe - && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) - wait_for_myself (); - } - myself.exit (EXITCODE_NOSET); - break; - case _P_WAIT: - case _P_SYSTEM: - system_call.arm (); - if (waitpid (cygpid, &res, 0) != cygpid) - res = -1; - break; - case _P_DETACH: - res = 0; /* Lost all memory of this child. */ - break; - case _P_NOWAIT: - case _P_NOWAITO: - case _P_VFORK: - res = cygpid; - break; - default: - break; - } - -out: + __endtry this->cleanup (); if (envblock) free (envblock); @@ -1137,28 +1137,31 @@ av::setup (const char *prog_arg, path_conv& real_path, const char *ext, } { - myfault efault; - if (efault.faulted ()) + __try + { + if (buf[0] == 'M' && buf[1] == 'Z') + { + WORD subsys; + unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8); + win16_exe = off < sizeof (IMAGE_DOS_HEADER); + if (!win16_exe) + real_path.set_cygexec (hook_or_detect_cygwin (buf, NULL, + subsys, hm)); + else + real_path.set_cygexec (false); + UnmapViewOfFile (buf); + CloseHandle (hm); + break; + } + } + __except (NO_ERROR) { UnmapViewOfFile (buf); CloseHandle (hm); real_path.set_cygexec (false); break; } - if (buf[0] == 'M' && buf[1] == 'Z') - { - WORD subsys; - unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8); - win16_exe = off < sizeof (IMAGE_DOS_HEADER); - if (!win16_exe) - real_path.set_cygexec (hook_or_detect_cygwin (buf, NULL, - subsys, hm)); - else - real_path.set_cygexec (false); - UnmapViewOfFile (buf); - CloseHandle (hm); - break; - } + __endtry } CloseHandle (hm); diff --git a/winsup/cygwin/string.h b/winsup/cygwin/string.h index 0a42f60eb..3b8d564c4 100644 --- a/winsup/cygwin/string.h +++ b/winsup/cygwin/string.h @@ -79,13 +79,8 @@ int __stdcall cygwin_strncasecmp (const char *, const char *, size_t); #define strcasematch(s1,s2) (!cygwin_strcasecmp ((s1),(s2))) #define strncasematch(s1,s2,n) (!cygwin_strncasecmp ((s1),(s2),(n))) -#undef strlwr -#define strlwr cygwin_strlwr -char * __stdcall cygwin_strlwr (char *); - -#undef strupr -#define strupr cygwin_strupr -char * __stdcall cygwin_strupr (char *); +char *strlwr (char *); +char *strupr (char *); #endif /* __INSIDE_CYGWIN__ */ diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index c4857ea8a..544169d78 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -720,7 +720,7 @@ retry_open: if (!NT_SUCCESS (status2)) debug_printf ("Removing R/O on %S failed, status = %y", pc.get_nt_native_path (), status2); - pc.init_reopen_attr (&attr, fh_ro); + pc.init_reopen_attr (attr, fh_ro); } else { @@ -948,8 +948,8 @@ try_again: pc.get_nt_native_path ()); /* Re-open from handle so we open the correct file no matter if it has been moved to the bin or not. */ - pc.init_reopen_attr (&attr, fh); - status = NtOpenFile (&fh2, DELETE, &attr, &io, + status = NtOpenFile (&fh2, DELETE, + pc.init_reopen_attr (attr, fh), &io, bin_stat == move_to_bin ? FILE_SHARE_VALID_FLAGS : FILE_SHARE_DELETE, flags | FILE_DELETE_ON_CLOSE); @@ -1164,31 +1164,31 @@ getsid (pid_t pid) extern "C" ssize_t read (int fd, void *ptr, size_t len) { - pthread_testcancel (); - - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - size_t res = (size_t) -1; - cygheap_fdget cfd (fd); - if (cfd < 0) - goto done; + pthread_testcancel (); - if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) + __try { - set_errno (EBADF); - goto done; + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + + if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) + { + set_errno (EBADF); + __leave; + } + + /* Could block, so let user know we at least got here. */ + syscall_printf ("read(%d, %p, %d) %sblocking", + fd, ptr, len, cfd->is_nonblocking () ? "non" : ""); + + cfd->read (ptr, len); + res = len; } - - /* Could block, so let user know we at least got here. */ - syscall_printf ("read(%d, %p, %d) %sblocking", - fd, ptr, len, cfd->is_nonblocking () ? "non" : ""); - - cfd->read (ptr, res = len); - -done: + __except (EFAULT) {} + __endtry syscall_printf ("%lR = read(%d, %p, %d)", res, fd, ptr, len); MALLOC_CHECK; return (ssize_t) res; @@ -1199,38 +1199,38 @@ EXPORT_ALIAS (read, _read) extern "C" ssize_t readv (int fd, const struct iovec *const iov, const int iovcnt) { + ssize_t res = -1; + pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - ssize_t res = -1; - const ssize_t tot = check_iovec_for_read (iov, iovcnt); - - cygheap_fdget cfd (fd); - if (cfd < 0) - goto done; - - if (tot <= 0) + __try { - res = tot; - goto done; + const ssize_t tot = check_iovec_for_read (iov, iovcnt); + + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + + if (tot <= 0) + { + res = tot; + __leave; + } + + if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) + { + set_errno (EBADF); + __leave; + } + + /* Could block, so let user know we at least got here. */ + syscall_printf ("readv(%d, %p, %d) %sblocking", + fd, iov, iovcnt, cfd->is_nonblocking () ? "non" : ""); + + res = cfd->readv (iov, iovcnt, tot); } - - if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) - { - set_errno (EBADF); - goto done; - } - - /* Could block, so let user know we at least got here. */ - syscall_printf ("readv(%d, %p, %d) %sblocking", - fd, iov, iovcnt, cfd->is_nonblocking () ? "non" : ""); - - res = cfd->readv (iov, iovcnt, tot); - -done: + __except (EFAULT) {} + __endtry syscall_printf ("%lR = readv(%d, %p, %d)", res, fd, iov, iovcnt); MALLOC_CHECK; return res; @@ -1239,9 +1239,10 @@ done: extern "C" ssize_t pread (int fd, void *ptr, size_t len, off_t off) { + ssize_t res; + pthread_testcancel (); - ssize_t res; cygheap_fdget cfd (fd); if (cfd < 0) res = -1; @@ -1255,35 +1256,33 @@ pread (int fd, void *ptr, size_t len, off_t off) extern "C" ssize_t write (int fd, const void *ptr, size_t len) { - pthread_testcancel (); - - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - ssize_t res = -1; - cygheap_fdget cfd (fd); - if (cfd < 0) - goto done; + pthread_testcancel (); - if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) + __try { - set_errno (EBADF); - goto done; + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + + if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) + { + set_errno (EBADF); + __leave; + } + + /* Could block, so let user know we at least got here. */ + if (fd == 1 || fd == 2) + paranoid_printf ("write(%d, %p, %d)", fd, ptr, len); + else + syscall_printf ("write(%d, %p, %d)", fd, ptr, len); + + res = cfd->write (ptr, len); } - - /* Could block, so let user know we at least got here. */ - if (fd == 1 || fd == 2) - paranoid_printf ("write(%d, %p, %d)", fd, ptr, len); - else - syscall_printf ("write(%d, %p, %d)", fd, ptr, len); - - res = cfd->write (ptr, len); - -done: + __except (EFAULT) {} + __endtry syscall_printf ("%lR = write(%d, %p, %d)", res, fd, ptr, len); - MALLOC_CHECK; return res; } @@ -1293,45 +1292,44 @@ EXPORT_ALIAS (write, _write) extern "C" ssize_t writev (const int fd, const struct iovec *const iov, const int iovcnt) { + ssize_t res = -1; + pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - ssize_t res = -1; - const ssize_t tot = check_iovec_for_write (iov, iovcnt); - - cygheap_fdget cfd (fd); - if (cfd < 0) - goto done; - - if (tot <= 0) + __try { - res = tot; - goto done; + const ssize_t tot = check_iovec_for_write (iov, iovcnt); + + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + + if (tot <= 0) + { + res = tot; + __leave; + } + + if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) + { + set_errno (EBADF); + __leave; + } + + /* 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->writev (iov, iovcnt, tot); } - - 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->writev (iov, iovcnt, tot); - -done: + __except (EFAULT) {} + __endtry if (fd == 1 || fd == 2) paranoid_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt); else syscall_printf ("%lR = writev(%d, %p, %d)", res, fd, iov, iovcnt); - MALLOC_CHECK; return res; } @@ -1362,75 +1360,76 @@ open (const char *unix_path, int flags, ...) va_list ap; mode_t mode = 0; - syscall_printf ("open(%s, %y)", unix_path, flags); pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - /* errno already set */; - else if (!*unix_path) - set_errno (ENOENT); - else + + __try { - /* 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) + syscall_printf ("open(%s, %y)", unix_path, flags); + if (!*unix_path) + set_errno (ENOENT); + else { - /* This is a temporary kludge until all utilities can catch up with - a change in behavior that implements linux functionality: opening - a tty should not automatically cause it to become the controlling - tty for the process. */ - int opt = PC_OPEN | ((flags & (O_NOFOLLOW | O_EXCL)) - ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW); - if (!(flags & O_NOCTTY) && fd > 2 && myself->ctty != -2) + /* 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) { - flags |= O_NOCTTY; - opt |= PC_CTTY; /* flag that, if opened, this fhandler could - later be capable of being a controlling - terminal if /dev/tty is opened. */ - } - if (!(fh = build_fh_name (unix_path, opt, stat_suffixes))) - res = -1; // errno already set - else if ((flags & O_NOFOLLOW) && fh->issymlink ()) - { - delete fh; - res = -1; - set_errno (ELOOP); - } - else if ((flags & O_DIRECTORY) && fh->exists () && !fh->pc.isdir ()) - { - delete fh; - res = -1; - set_errno (ENOTDIR); - } - else if (((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) && fh->exists ()) - { - delete fh; - res = -1; - set_errno (EEXIST); - } - else if ((fh->is_fs_special () && fh->device_access_denied (flags)) - || !fh->open_with_arch (flags, (mode & 07777) & ~cygheap->umask)) - { - delete fh; - res = -1; - } - else - { - fd = fh; - if (fd <= 2) - set_std_handle (fd); - res = fd; + /* This is a temporary kludge until all utilities can catch up + with a change in behavior that implements linux functionality: + opening a tty should not automatically cause it to become the + controlling tty for the process. */ + int opt = PC_OPEN | ((flags & (O_NOFOLLOW | O_EXCL)) + ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW); + if (!(flags & O_NOCTTY) && fd > 2 && myself->ctty != -2) + { + flags |= O_NOCTTY; + /* flag that, if opened, this fhandler could later be capable + of being a controlling terminal if /dev/tty is opened. */ + opt |= PC_CTTY; + } + if (!(fh = build_fh_name (unix_path, opt, stat_suffixes))) + ; // errno already set + else if ((flags & O_NOFOLLOW) && fh->issymlink ()) + { + delete fh; + set_errno (ELOOP); + } + else if ((flags & O_DIRECTORY) && fh->exists () + && !fh->pc.isdir ()) + { + delete fh; + set_errno (ENOTDIR); + } + else if (((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + && fh->exists ()) + { + delete fh; + set_errno (EEXIST); + } + else if ((fh->is_fs_special () + && fh->device_access_denied (flags)) + || !fh->open_with_arch (flags, (mode & 07777) + & ~cygheap->umask)) + delete fh; + else + { + fd = fh; + if (fd <= 2) + set_std_handle (fd); + res = fd; + } } } - } - syscall_printf ("%R = open(%s, %y)", res, unix_path, flags); + syscall_printf ("%R = open(%s, %y)", res, unix_path, flags); + } + __except (EFAULT) {} + __endtry return res; } @@ -1894,34 +1893,33 @@ stat_worker (path_conv &pc, struct stat *buf) { int res = -1; - myfault efault; - if (efault.faulted (EFAULT)) - goto error; - - if (pc.error) + __try { - debug_printf ("got %d error from path_conv", pc.error); - set_errno (pc.error); + if (pc.error) + { + debug_printf ("got %d error from path_conv", pc.error); + set_errno (pc.error); + } + else if (pc.exists ()) + { + fhandler_base *fh; + + if (!(fh = build_fh_pc (pc))) + __leave; + + debug_printf ("(%S, %p, %p), file_attributes %d", + pc.get_nt_native_path (), buf, fh, (DWORD) *fh); + memset (buf, 0, sizeof (*buf)); + res = fh->fstat (buf); + if (!res) + fh->stat_fixup (buf); + delete fh; + } + else + set_errno (ENOENT); } - else if (pc.exists ()) - { - fhandler_base *fh; - - if (!(fh = build_fh_pc (pc))) - goto error; - - debug_printf ("(%S, %p, %p), file_attributes %d", - pc.get_nt_native_path (), buf, fh, (DWORD) *fh); - memset (buf, 0, sizeof (*buf)); - res = fh->fstat (buf); - if (!res) - fh->stat_fixup (buf); - delete fh; - } - else - set_errno (ENOENT); - - error: + __except (EFAULT) {} + __endtry MALLOC_CHECK; syscall_printf ("%d = (%S,%p)", res, pc.get_nt_native_path (), buf); return res; @@ -2113,497 +2111,511 @@ rename (const char *oldpath, const char *newpath) FILE_STANDARD_INFORMATION ofsi; PFILE_RENAME_INFORMATION pfri; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (!*oldpath || !*newpath) + __try { - /* Reject rename("","x"), rename("x",""). */ - set_errno (ENOENT); - goto out; - } - if (has_dot_last_component (oldpath, true)) - { - /* Reject rename("dir/.","x"). */ - oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); - set_errno (oldpc.isdir () ? EINVAL : ENOTDIR); - goto out; - } - if (has_dot_last_component (newpath, true)) - { - /* Reject rename("dir","x/."). */ - newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); - set_errno (!newpc.exists () ? ENOENT : newpc.isdir () ? EINVAL : ENOTDIR); - goto out; - } - - /* A trailing slash requires that the pathname points to an existing - directory. If it's not, it's a ENOTDIR condition. The same goes - for newpath a bit further down this function. */ - olen = strlen (oldpath); - if (isdirsep (oldpath[olen - 1])) - { - char *buf; - char *p = stpcpy (buf = tp.c_get (), oldpath) - 1; - oldpath = buf; - while (p >= oldpath && isdirsep (*p)) - *p-- = '\0'; - olen = p + 1 - oldpath; - if (!olen) + if (!*oldpath || !*newpath) { - /* The root directory cannot be renamed. This also rejects - the corner case of rename("/","/"), even though it is the - same file. */ - set_errno (EINVAL); - goto out; + /* Reject rename("","x"), rename("x",""). */ + set_errno (ENOENT); + __leave; } - old_dir_requested = true; - } - oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); - if (oldpc.error) - { - set_errno (oldpc.error); - goto out; - } - if (!oldpc.exists ()) - { - set_errno (ENOENT); - goto out; - } - if (oldpc.isspecial () && !oldpc.issocket () && !oldpc.is_fs_special ()) - { - /* No renames from virtual FS */ - set_errno (EROFS); - goto out; - } - if (oldpc.has_attribute (FILE_ATTRIBUTE_REPARSE_POINT) && !oldpc.issymlink ()) - { - /* Volume mount point. If we try to rename a volume mount point, NT - returns STATUS_NOT_SAME_DEVICE ==> Win32 ERROR_NOT_SAME_DEVICE ==> - errno EXDEV. That's bad since mv(1) will now perform a cross-device - move. So what we do here is to treat the volume mount point just - like Linux treats a mount point. */ - set_errno (EBUSY); - goto out; - } - if (old_dir_requested && !oldpc.isdir ()) - { - /* Reject rename("file/","x"). */ - set_errno (ENOTDIR); - goto out; - } - if (oldpc.known_suffix - && (ascii_strcasematch (oldpath + olen - 4, ".lnk") - || ascii_strcasematch (oldpath + olen - 4, ".exe"))) - old_explicit_suffix = true; - - nlen = strlen (newpath); - if (isdirsep (newpath[nlen - 1])) - { - char *buf; - char *p = stpcpy (buf = tp.c_get (), newpath) - 1; - newpath = buf; - while (p >= newpath && isdirsep (*p)) - *p-- = '\0'; - nlen = p + 1 - newpath; - if (!nlen) /* The root directory is never empty. */ + if (has_dot_last_component (oldpath, true)) { - set_errno (ENOTEMPTY); - goto out; + /* Reject rename("dir/.","x"). */ + oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); + set_errno (oldpc.isdir () ? EINVAL : ENOTDIR); + __leave; } - new_dir_requested = true; - } - newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); - if (newpc.error) - { - set_errno (newpc.error); - goto out; - } - if (newpc.isspecial () && !newpc.issocket ()) /* No renames to virtual FSes */ - { - set_errno (EROFS); - goto out; - } - if (new_dir_requested && !(newpc.exists () - ? newpc.isdir () : oldpc.isdir ())) - { - /* Reject rename("file1","file2/"), but allow rename("dir","d/"). */ - set_errno (newpc.exists () ? ENOTDIR : ENOENT); - goto out; - } - if (newpc.exists () && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ())) - { - /* Reject rename("file","dir") and rename("dir","file"). */ - set_errno (newpc.isdir () ? EISDIR : ENOTDIR); - goto out; - } - if (newpc.known_suffix - && (ascii_strcasematch (newpath + nlen - 4, ".lnk") - || ascii_strcasematch (newpath + nlen - 4, ".exe"))) - new_explicit_suffix = true; - - /* This test is necessary in almost every case, so just do it once here. */ - equal_path = RtlEqualUnicodeString (oldpc.get_nt_native_path (), - newpc.get_nt_native_path (), - oldpc.objcaseinsensitive ()); - - /* First check if oldpath and newpath only differ by case. If so, it's - just a request to change the case of the filename. By simply setting - the file attributes to INVALID_FILE_ATTRIBUTES (which translates to - "file doesn't exist"), all later tests are skipped. */ - if (oldpc.objcaseinsensitive () && newpc.exists () && equal_path - && old_explicit_suffix == new_explicit_suffix) - { - if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), - newpc.get_nt_native_path (), - FALSE)) + if (has_dot_last_component (newpath, true)) { - res = 0; - goto out; + /* Reject rename("dir","x/."). */ + newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); + set_errno (!newpc.exists () ? ENOENT + : newpc.isdir () ? EINVAL : ENOTDIR); + __leave; } - newpc.file_attributes (INVALID_FILE_ATTRIBUTES); - } - else if (oldpc.isdir ()) - { - /* Check for newpath being identical or a subdir of oldpath. */ - if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (), - newpc.get_nt_native_path (), - TRUE)) + + /* A trailing slash requires that the pathname points to an existing + directory. If it's not, it's a ENOTDIR condition. The same goes + for newpath a bit further down this function. */ + olen = strlen (oldpath); + if (isdirsep (oldpath[olen - 1])) { - if (newpc.get_nt_native_path ()->Length - == oldpc.get_nt_native_path ()->Length) - { - res = 0; - goto out; - } - if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer - + oldpc.get_nt_native_path ()->Length) == L'\\') + char *buf; + char *p = stpcpy (buf = tp.c_get (), oldpath) - 1; + oldpath = buf; + while (p >= oldpath && isdirsep (*p)) + *p-- = '\0'; + olen = p + 1 - oldpath; + if (!olen) { + /* The root directory cannot be renamed. This also rejects + the corner case of rename("/","/"), even though it is the + same file. */ set_errno (EINVAL); - goto out; + __leave; } + old_dir_requested = true; } - } - else if (!newpc.exists ()) - { - if (equal_path && old_explicit_suffix != new_explicit_suffix) + oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); + if (oldpc.error) + { + set_errno (oldpc.error); + __leave; + } + if (!oldpc.exists ()) + { + set_errno (ENOENT); + __leave; + } + if (oldpc.isspecial () && !oldpc.issocket () && !oldpc.is_fs_special ()) + { + /* No renames from virtual FS */ + set_errno (EROFS); + __leave; + } + if (oldpc.has_attribute (FILE_ATTRIBUTE_REPARSE_POINT) + && !oldpc.issymlink ()) + { + /* Volume mount point. If we try to rename a volume mount point, NT + returns STATUS_NOT_SAME_DEVICE ==> Win32 ERROR_NOT_SAME_DEVICE ==> + errno EXDEV. That's bad since mv(1) will now perform a + cross-device move. So what we do here is to treat the volume + mount point just like Linux treats a mount point. */ + set_errno (EBUSY); + __leave; + } + if (old_dir_requested && !oldpc.isdir ()) + { + /* Reject rename("file/","x"). */ + set_errno (ENOTDIR); + __leave; + } + if (oldpc.known_suffix + && (ascii_strcasematch (oldpath + olen - 4, ".lnk") + || ascii_strcasematch (oldpath + olen - 4, ".exe"))) + old_explicit_suffix = true; + + nlen = strlen (newpath); + if (isdirsep (newpath[nlen - 1])) + { + char *buf; + char *p = stpcpy (buf = tp.c_get (), newpath) - 1; + newpath = buf; + while (p >= newpath && isdirsep (*p)) + *p-- = '\0'; + nlen = p + 1 - newpath; + if (!nlen) /* The root directory is never empty. */ + { + set_errno (ENOTEMPTY); + __leave; + } + new_dir_requested = true; + } + newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); + if (newpc.error) + { + set_errno (newpc.error); + __leave; + } + if (newpc.isspecial () && !newpc.issocket ()) + { + /* No renames to virtual FSes */ + set_errno (EROFS); + __leave; + } + if (new_dir_requested && !(newpc.exists () + ? newpc.isdir () : oldpc.isdir ())) + { + /* Reject rename("file1","file2/"), but allow rename("dir","d/"). */ + set_errno (newpc.exists () ? ENOTDIR : ENOENT); + __leave; + } + if (newpc.exists () + && (oldpc.isdir () ? !newpc.isdir () : newpc.isdir ())) + { + /* Reject rename("file","dir") and rename("dir","file"). */ + set_errno (newpc.isdir () ? EISDIR : ENOTDIR); + __leave; + } + if (newpc.known_suffix + && (ascii_strcasematch (newpath + nlen - 4, ".lnk") + || ascii_strcasematch (newpath + nlen - 4, ".exe"))) + new_explicit_suffix = true; + + /* This test is necessary in almost every case, so do it once here. */ + equal_path = RtlEqualUnicodeString (oldpc.get_nt_native_path (), + newpc.get_nt_native_path (), + oldpc.objcaseinsensitive ()); + + /* First check if oldpath and newpath only differ by case. If so, it's + just a request to change the case of the filename. By simply setting + the file attributes to INVALID_FILE_ATTRIBUTES (which translates to + "file doesn't exist"), all later tests are skipped. */ + if (oldpc.objcaseinsensitive () && newpc.exists () && equal_path + && old_explicit_suffix == new_explicit_suffix) { - newpc.check (newpath, PC_SYM_NOFOLLOW); if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), newpc.get_nt_native_path (), - oldpc.objcaseinsensitive ())) + FALSE)) { res = 0; - goto out; + __leave; + } + newpc.file_attributes (INVALID_FILE_ATTRIBUTES); + } + else if (oldpc.isdir ()) + { + /* Check for newpath being identical or a subdir of oldpath. */ + if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (), + newpc.get_nt_native_path (), + TRUE)) + { + if (newpc.get_nt_native_path ()->Length + == oldpc.get_nt_native_path ()->Length) + { + res = 0; + __leave; + } + if (*(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer + + oldpc.get_nt_native_path ()->Length) == L'\\') + { + set_errno (EINVAL); + __leave; + } } } - else if (oldpc.is_lnk_special () - && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), - &ro_u_lnk, TRUE)) - rename_append_suffix (newpc, newpath, nlen, ".lnk"); - else if (oldpc.is_binary () && !old_explicit_suffix - && oldpc.known_suffix - && !nt_path_has_executable_suffix (newpc.get_nt_native_path ())) - /* Never append .exe suffix if oldpath had .exe suffix given - explicitely, or if oldpath wasn't already a .exe file, or - if the destination filename has one of the blessed executable - suffixes. - Note: To rename an executable foo.exe to bar-without-suffix, - the .exe suffix must be given explicitly in oldpath. */ - rename_append_suffix (newpc, newpath, nlen, ".exe"); - } - else - { - if (equal_path && old_explicit_suffix != new_explicit_suffix) + else if (!newpc.exists ()) { - newpc.check (newpath, PC_SYM_NOFOLLOW); - if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), - newpc.get_nt_native_path (), - oldpc.objcaseinsensitive ())) + if (equal_path && old_explicit_suffix != new_explicit_suffix) { - res = 0; - goto out; - } - } - else if (oldpc.is_lnk_special ()) - { - if (!newpc.is_lnk_special () - && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), - &ro_u_lnk, TRUE)) - { - rename_append_suffix (new2pc, newpath, nlen, ".lnk"); - removepc = &newpc; - } - } - else if (oldpc.is_binary ()) - { - /* Never append .exe suffix if oldpath had .exe suffix given - explicitely, or if newfile is a binary (in which case the given - name probably makes sense as it is), or if the destination - filename has one of the blessed executable suffixes. */ - if (!old_explicit_suffix && oldpc.known_suffix - && !newpc.is_binary () - && !nt_path_has_executable_suffix (newpc.get_nt_native_path ())) - { - rename_append_suffix (new2pc, newpath, nlen, ".exe"); - removepc = &newpc; + newpc.check (newpath, PC_SYM_NOFOLLOW); + if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), + newpc.get_nt_native_path (), + oldpc.objcaseinsensitive ())) + { + res = 0; + __leave; + } } + else if (oldpc.is_lnk_special () + && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), + &ro_u_lnk, TRUE)) + rename_append_suffix (newpc, newpath, nlen, ".lnk"); + else if (oldpc.is_binary () && !old_explicit_suffix + && oldpc.known_suffix + && !nt_path_has_executable_suffix + (newpc.get_nt_native_path ())) + /* Never append .exe suffix if oldpath had .exe suffix given + explicitely, or if oldpath wasn't already a .exe file, or + if the destination filename has one of the blessed executable + suffixes. + Note: To rename an executable foo.exe to bar-without-suffix, + the .exe suffix must be given explicitly in oldpath. */ + rename_append_suffix (newpc, newpath, nlen, ".exe"); } else { - /* If the new path is an existing .lnk symlink or a .exe file, - but the new path has not been specified with explicit suffix, - rename to the new name without suffix, as expected, but also - remove the clashing symlink or executable. Did I ever mention - how I hate the file suffix idea? */ - if ((newpc.is_lnk_special () - || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), - &ro_u_exe, TRUE)) - && !new_explicit_suffix) + if (equal_path && old_explicit_suffix != new_explicit_suffix) { - new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); - newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR); - if (new2pc.is_binary () || new2pc.is_lnk_special ()) - removepc = &new2pc; + newpc.check (newpath, PC_SYM_NOFOLLOW); + if (RtlEqualUnicodeString (oldpc.get_nt_native_path (), + newpc.get_nt_native_path (), + oldpc.objcaseinsensitive ())) + { + res = 0; + __leave; + } + } + else if (oldpc.is_lnk_special ()) + { + if (!newpc.is_lnk_special () + && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), + &ro_u_lnk, TRUE)) + { + rename_append_suffix (new2pc, newpath, nlen, ".lnk"); + removepc = &newpc; + } + } + else if (oldpc.is_binary ()) + { + /* Never append .exe suffix if oldpath had .exe suffix given + explicitely, or if newfile is a binary (in which case the given + name probably makes sense as it is), or if the destination + filename has one of the blessed executable suffixes. */ + if (!old_explicit_suffix && oldpc.known_suffix + && !newpc.is_binary () + && !nt_path_has_executable_suffix + (newpc.get_nt_native_path ())) + { + rename_append_suffix (new2pc, newpath, nlen, ".exe"); + removepc = &newpc; + } + } + else + { + /* If the new path is an existing .lnk symlink or a .exe file, + but the new path has not been specified with explicit suffix, + rename to the new name without suffix, as expected, but also + remove the clashing symlink or executable. Did I ever mention + how I hate the file suffix idea? */ + if ((newpc.is_lnk_special () + || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (), + &ro_u_exe, TRUE)) + && !new_explicit_suffix) + { + new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes); + newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR); + if (new2pc.is_binary () || new2pc.is_lnk_special ()) + removepc = &new2pc; + } } } - } - dstpc = (removepc == &newpc) ? &new2pc : &newpc; + dstpc = (removepc == &newpc) ? &new2pc : &newpc; - /* Check cross-device before touching anything. Otherwise we might end - up with an unlinked target dir even if the actual rename didn't work. */ - if (oldpc.fs_type () != dstpc->fs_type () - || oldpc.fs_serial_number () != dstpc->fs_serial_number ()) - { - set_errno (EXDEV); - goto out; - } - - /* Opening the file must be part of the transaction. It's not sufficient - to call only NtSetInformationFile under the transaction. Therefore we - have to start the transaction here, if necessary. */ - if (wincap.has_transactions () - && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) - && (dstpc->isdir () - || (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)))) - start_transaction (old_trans, trans); - - int retry_count; - retry_count = 0; -retry: - /* Talking about inconsistent behaviour... - - DELETE is required to rename a file. So far, so good. - - At least one cifs FS (Tru64) needs FILE_READ_ATTRIBUTE, otherwise the - FileRenameInformation call fails with STATUS_ACCESS_DENIED. However, - on NFS we get a STATUS_ACCESS_DENIED if FILE_READ_ATTRIBUTE is used - and the file we try to rename is a symlink. Urgh. - - Samba (only some versions?) doesn't like the FILE_SHARE_DELETE mode if - the file has the R/O attribute set and returns STATUS_ACCESS_DENIED in - that case. */ - { - ULONG access = DELETE | (oldpc.fs_is_cifs () ? FILE_READ_ATTRIBUTES : 0); - ULONG sharing = FILE_SHARE_READ | FILE_SHARE_WRITE - | (oldpc.fs_is_samba () ? 0 : FILE_SHARE_DELETE); - ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT - | (oldpc.is_rep_symlink () ? FILE_OPEN_REPARSE_POINT : 0); - status = NtOpenFile (&fh, access, - oldpc.get_object_attr (attr, sec_none_nih), - &io, sharing, flags); - } - if (!NT_SUCCESS (status)) - { - debug_printf ("status %y", status); - if (status == STATUS_SHARING_VIOLATION - && cygwait (10L) != WAIT_SIGNALED) + /* Check cross-device before touching anything. Otherwise we might end + up with an unlinked target dir even if the actual rename didn't work.*/ + if (oldpc.fs_type () != dstpc->fs_type () + || oldpc.fs_serial_number () != dstpc->fs_serial_number ()) { - /* Typical BLODA problem. Some virus scanners check newly generated - files and while doing that disallow DELETE access. That's really - bad because it breaks applications which copy files by creating - a temporary filename and then rename the temp filename to the - target filename. This renaming fails due to the jealous virus - scanner and the application fails to create the target file. + set_errno (EXDEV); + __leave; + } - This kludge tries to work around that by yielding until the - sharing violation goes away, or a signal arrived, or after - about a second, give or take. */ - if (++retry_count < 40) + /* Opening the file must be part of the transaction. It's not sufficient + to call only NtSetInformationFile under the transaction. Therefore we + have to start the transaction here, if necessary. */ + if (wincap.has_transactions () + && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) + && (dstpc->isdir () + || (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)))) + start_transaction (old_trans, trans); + + int retry_count; + retry_count = 0; + retry: + /* Talking about inconsistent behaviour... + - DELETE is required to rename a file. So far, so good. + - At least one cifs FS (Tru64) needs FILE_READ_ATTRIBUTE, otherwise the + FileRenameInformation call fails with STATUS_ACCESS_DENIED. However, + on NFS we get a STATUS_ACCESS_DENIED if FILE_READ_ATTRIBUTE is used + and the file we try to rename is a symlink. Urgh. + - Samba (only some versions?) doesn't like the FILE_SHARE_DELETE + mode if the file has the R/O attribute set and returns + STATUS_ACCESS_DENIED in that case. */ + { + ULONG access = DELETE + | (oldpc.fs_is_cifs () ? FILE_READ_ATTRIBUTES : 0); + ULONG sharing = FILE_SHARE_READ | FILE_SHARE_WRITE + | (oldpc.fs_is_samba () ? 0 : FILE_SHARE_DELETE); + ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT + | (oldpc.is_rep_symlink () ? FILE_OPEN_REPARSE_POINT : 0); + status = NtOpenFile (&fh, access, + oldpc.get_object_attr (attr, sec_none_nih), + &io, sharing, flags); + } + if (!NT_SUCCESS (status)) + { + debug_printf ("status %y", status); + if (status == STATUS_SHARING_VIOLATION + && cygwait (10L) != WAIT_SIGNALED) { - yield (); + /* Typical BLODA problem. Some virus scanners check newly + generated files and while doing that disallow DELETE access. + That's really bad because it breaks applications which copy + files by creating a temporary filename and then rename the + temp filename to the target filename. This renaming fails due + to the jealous virus scanner and the application fails to + create the target file. + + This kludge tries to work around that by yielding until the + sharing violation goes away, or a signal arrived, or after + about a second, give or take. */ + if (++retry_count < 40) + { + yield (); + goto retry; + } + } + else if (NT_TRANSACTIONAL_ERROR (status) && trans) + { + /* If NtOpenFile fails due to transactional problems, stop + transaction and go ahead without. */ + stop_transaction (status, old_trans, trans); + debug_printf ("Transaction failure. Retry open."); goto retry; } + __seterrno_from_nt_status (status); + __leave; } - else if (NT_TRANSACTIONAL_ERROR (status) && trans) - { - /* If NtOpenFile fails due to transactional problems, stop - transaction and go ahead without. */ - stop_transaction (status, old_trans, trans); - debug_printf ("Transaction failure. Retry open."); - goto retry; - } - __seterrno_from_nt_status (status); - goto out; - } - /* Renaming a dir to another, existing dir fails always, even if - ReplaceIfExists is set to TRUE and the existing dir is empty. So - we have to remove the destination dir first. This also covers the - case that the destination directory is not empty. In that case, - unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */ - if (dstpc->isdir ()) - { - status = unlink_nt (*dstpc); - if (!NT_SUCCESS (status)) + /* Renaming a dir to another, existing dir fails always, even if + ReplaceIfExists is set to TRUE and the existing dir is empty. So + we have to remove the destination dir first. This also covers the + case that the destination directory is not empty. In that case, + unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */ + if (dstpc->isdir ()) { - __seterrno_from_nt_status (status); - goto out; + status = unlink_nt (*dstpc); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } } - } - /* You can't copy a file if the destination exists and has the R/O - attribute set. Remove the R/O attribute first. But first check - if a removepc exists. If so, dstpc points to a non-existing file - due to a mangled suffix. */ - else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)) - { - status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES, - dstpc->get_object_attr (attr, sec_none_nih), - &io, FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT - | (dstpc->is_rep_symlink () - ? FILE_OPEN_REPARSE_POINT : 0)); - if (!NT_SUCCESS (status)) + /* You can't copy a file if the destination exists and has the R/O + attribute set. Remove the R/O attribute first. But first check + if a removepc exists. If so, dstpc points to a non-existing file + due to a mangled suffix. */ + else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)) { - __seterrno_from_nt_status (status); - goto out; + status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES, + dstpc->get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT + | (dstpc->is_rep_symlink () + ? FILE_OPEN_REPARSE_POINT : 0)); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } + status = NtSetAttributesFile (nfh, dstpc->file_attributes () + & ~FILE_ATTRIBUTE_READONLY); + NtClose (nfh); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + __leave; + } } - status = NtSetAttributesFile (nfh, dstpc->file_attributes () - & ~FILE_ATTRIBUTE_READONLY); - NtClose (nfh); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - goto out; - } - } - /* SUSv3: If the old argument and the new argument resolve to the same - existing file, rename() shall return successfully and perform no - other action. - The test tries to be as quick as possible. Due to the above cross device - check we already know both files are on the same device. So it just - tests if oldpath has more than 1 hardlink, then it opens newpath - and tests for identical file ids. If so, oldpath and newpath refer to - the same file. */ - if ((removepc || dstpc->exists ()) - && !oldpc.isdir () - && NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofsi, sizeof ofsi, - FileStandardInformation)) - && ofsi.NumberOfLinks > 1 - && NT_SUCCESS (NtOpenFile (&nfh, READ_CONTROL, + /* SUSv3: If the old argument and the new argument resolve to the same + existing file, rename() shall return successfully and perform no + other action. + The test tries to be as quick as possible. Due to the above cross + device check we already know both files are on the same device. So + it just tests if oldpath has more than 1 hardlink, then it opens + newpath and tests for identical file ids. If so, oldpath and newpath + refer to the same file. */ + if ((removepc || dstpc->exists ()) + && !oldpc.isdir () + && NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofsi, sizeof ofsi, + FileStandardInformation)) + && ofsi.NumberOfLinks > 1 + && NT_SUCCESS (NtOpenFile (&nfh, READ_CONTROL, (removepc ?: dstpc)->get_object_attr (attr, sec_none_nih), &io, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT | ((removepc ?: dstpc)->is_rep_symlink () ? FILE_OPEN_REPARSE_POINT : 0)))) - { - FILE_INTERNAL_INFORMATION ofii, nfii; + { + FILE_INTERNAL_INFORMATION ofii, nfii; - if (NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofii, sizeof ofii, - FileInternalInformation)) - && NT_SUCCESS (NtQueryInformationFile (nfh, &io, &nfii, sizeof nfii, - FileInternalInformation)) - && ofii.FileId.QuadPart == nfii.FileId.QuadPart) - { - debug_printf ("%s and %s are the same file", oldpath, newpath); - NtClose (nfh); - res = 0; - goto out; - } - NtClose (nfh); - } - /* Create FILE_RENAME_INFORMATION struct. Using a tmp_pathbuf area allows - for paths of up to 32757 chars. This test is just for paranoia's sake. */ - if (dstpc->get_nt_native_path ()->Length > NT_MAX_PATH * sizeof (WCHAR) - - sizeof (FILE_RENAME_INFORMATION)) - { - debug_printf ("target filename too long"); - set_errno (EINVAL); - goto out; - } - pfri = (PFILE_RENAME_INFORMATION) tp.w_get (); - pfri->ReplaceIfExists = TRUE; - pfri->RootDirectory = NULL; - pfri->FileNameLength = dstpc->get_nt_native_path ()->Length; - memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer, - pfri->FileNameLength); - status = NtSetInformationFile (fh, &io, pfri, - sizeof *pfri + pfri->FileNameLength, - FileRenameInformation); - /* This happens if the access rights don't allow deleting the destination. - Even if the handle to the original file is opened with BACKUP - and/or RECOVERY, these flags don't apply to the destination of the - rename operation. So, a privileged user can't rename a file to an - existing file, if the permissions of the existing file aren't right. - Like directories, we have to handle this separately by removing the - destination before renaming. */ - if (status == STATUS_ACCESS_DENIED && dstpc->exists () && !dstpc->isdir ()) - { - if (wincap.has_transactions () - && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) - && !trans) - { - start_transaction (old_trans, trans); - /* As mentioned earlier, opening the file must be part of the - transaction. Therefore we have to reopen the file here if the - transaction hasn't been started already. Unfortunately we can't - use the NT "reopen file from existing handle" feature. In that - case NtOpenFile returns STATUS_TRANSACTIONAL_CONFLICT. We *have* - to close the handle to the file first, *then* we can re-open it. - Fortunately nothing has happened yet, so the atomicity of the - rename functionality is not spoiled. */ - NtClose (fh); -retry_reopen: - status = NtOpenFile (&fh, DELETE, - oldpc.get_object_attr (attr, sec_none_nih), - &io, FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT - | (oldpc.is_rep_symlink () - ? FILE_OPEN_REPARSE_POINT : 0)); - if (!NT_SUCCESS (status)) + if (NT_SUCCESS (NtQueryInformationFile (fh, &io, &ofii, sizeof ofii, + FileInternalInformation)) + && NT_SUCCESS (NtQueryInformationFile (nfh, &io, &nfii, + sizeof nfii, + FileInternalInformation)) + && ofii.FileId.QuadPart == nfii.FileId.QuadPart) { - if (NT_TRANSACTIONAL_ERROR (status) && trans) - { - /* If NtOpenFile fails due to transactional problems, stop - transaction and go ahead without. */ - stop_transaction (status, old_trans, trans); - debug_printf ("Transaction failure. Retry open."); - goto retry_reopen; - } - __seterrno_from_nt_status (status); - goto out; + debug_printf ("%s and %s are the same file", oldpath, newpath); + NtClose (nfh); + res = 0; + __leave; } + NtClose (nfh); } - if (NT_SUCCESS (status = unlink_nt (*dstpc))) - status = NtSetInformationFile (fh, &io, pfri, - sizeof *pfri + pfri->FileNameLength, - FileRenameInformation); + /* Create FILE_RENAME_INFORMATION struct. Using a tmp_pathbuf area + allows for paths of up to 32757 chars. This test is just for + paranoia's sake. */ + if (dstpc->get_nt_native_path ()->Length + > NT_MAX_PATH * sizeof (WCHAR) - sizeof (FILE_RENAME_INFORMATION)) + { + debug_printf ("target filename too long"); + set_errno (EINVAL); + __leave; + } + pfri = (PFILE_RENAME_INFORMATION) tp.w_get (); + pfri->ReplaceIfExists = TRUE; + pfri->RootDirectory = NULL; + pfri->FileNameLength = dstpc->get_nt_native_path ()->Length; + memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer, + pfri->FileNameLength); + status = NtSetInformationFile (fh, &io, pfri, + sizeof *pfri + pfri->FileNameLength, + FileRenameInformation); + /* This happens if the access rights don't allow deleting the destination. + Even if the handle to the original file is opened with BACKUP + and/or RECOVERY, these flags don't apply to the destination of the + rename operation. So, a privileged user can't rename a file to an + existing file, if the permissions of the existing file aren't right. + Like directories, we have to handle this separately by removing the + destination before renaming. */ + if (status == STATUS_ACCESS_DENIED && dstpc->exists () + && !dstpc->isdir ()) + { + if (wincap.has_transactions () + && (dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) + && !trans) + { + start_transaction (old_trans, trans); + /* As mentioned earlier, opening the file must be part of the + transaction. Therefore we have to reopen the file here if the + transaction hasn't been started already. Unfortunately we + can't use the NT "reopen file from existing handle" feature. + In that case NtOpenFile returns STATUS_TRANSACTIONAL_CONFLICT. + We *have* to close the handle to the file first, *then* we can + re-open it. Fortunately nothing has happened yet, so the + atomicity of the rename functionality is not spoiled. */ + NtClose (fh); + retry_reopen: + status = NtOpenFile (&fh, DELETE, + oldpc.get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT + | (oldpc.is_rep_symlink () + ? FILE_OPEN_REPARSE_POINT : 0)); + if (!NT_SUCCESS (status)) + { + if (NT_TRANSACTIONAL_ERROR (status) && trans) + { + /* If NtOpenFile fails due to transactional problems, + stop transaction and go ahead without. */ + stop_transaction (status, old_trans, trans); + debug_printf ("Transaction failure. Retry open."); + goto retry_reopen; + } + __seterrno_from_nt_status (status); + __leave; + } + } + if (NT_SUCCESS (status = unlink_nt (*dstpc))) + status = NtSetInformationFile (fh, &io, pfri, + sizeof *pfri + pfri->FileNameLength, + FileRenameInformation); + } + if (NT_SUCCESS (status)) + { + if (removepc) + unlink_nt (*removepc); + res = 0; + } + else + __seterrno_from_nt_status (status); } - if (NT_SUCCESS (status)) + __except (EFAULT) { - if (removepc) - unlink_nt (*removepc); - res = 0; + res = -1; } - else - __seterrno_from_nt_status (status); - -out: + __endtry if (fh) NtClose (fh); /* Stop transaction if we started one. */ if (trans) stop_transaction (status, old_trans, trans); - syscall_printf ("%R = rename(%s, %s)", res, oldpath, newpath); + if (get_errno () != EFAULT) + syscall_printf ("%R = rename(%s, %s)", res, oldpath, newpath); return res; } @@ -2612,28 +2624,28 @@ system (const char *cmdstring) { pthread_testcancel (); - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - int res; - const char* command[4]; - if (cmdstring == NULL) return 1; - command[0] = "sh"; - command[1] = "-c"; - command[2] = cmdstring; - command[3] = (const char *) NULL; + int res = -1; + const char* command[4]; - if ((res = spawnvp (_P_SYSTEM, "/bin/sh", command)) == -1) + __try { - // when exec fails, return value should be as if shell - // executed exit (127) - res = 127; - } + command[0] = "sh"; + command[1] = "-c"; + command[2] = cmdstring; + command[3] = (const char *) NULL; + if ((res = spawnvp (_P_SYSTEM, "/bin/sh", command)) == -1) + { + // when exec fails, return value should be as if shell + // executed exit (127) + res = 127; + } + } + __except (EFAULT) {} + __endtry return res; } @@ -2678,24 +2690,25 @@ fpathconf (int fd, int v) extern "C" long int pathconf (const char *file, int v) { - fhandler_base *fh; + fhandler_base *fh = NULL; long ret = -1; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (!*file) + __try { - set_errno (ENOENT); - return -1; + if (!*file) + { + set_errno (ENOENT); + return -1; + } + if (!(fh = build_fh_name (file, PC_SYM_FOLLOW, stat_suffixes))) + return -1; + if (!fh->exists ()) + set_errno (ENOENT); + else + ret = fh->fpathconf (v); } - if (!(fh = build_fh_name (file, PC_SYM_FOLLOW, stat_suffixes))) - return -1; - if (!fh->exists ()) - set_errno (ENOENT); - else - ret = fh->fpathconf (v); + __except (EFAULT) {} + __endtry delete fh; return ret; } @@ -2704,10 +2717,8 @@ extern "C" int ttyname_r (int fd, char *buf, size_t buflen) { int ret = 0; - myfault efault; - if (efault.faulted ()) - ret = EFAULT; - else + + __try { cygheap_fdget cfd (fd, true); if (cfd < 0) @@ -2718,8 +2729,13 @@ ttyname_r (int fd, char *buf, size_t buflen) ret = ERANGE; else strcpy (buf, cfd->ttyname ()); + debug_printf ("returning %d tty: %s", ret, ret ? "NULL" : buf); } - debug_printf ("returning %d tty: %s", ret, ret ? "NULL" : buf); + __except (NO_ERROR) + { + ret = EFAULT; + } + __endtry return ret; } @@ -2976,14 +2992,16 @@ _get_osfhandle (int fd) extern "C" int fstatvfs (int fd, struct statvfs *sfs) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - cygheap_fdget cfd (fd); - if (cfd < 0) - return -1; - return cfd->fstatvfs (sfs); + __try + { + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + return cfd->fstatvfs (sfs); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -2992,30 +3010,31 @@ statvfs (const char *name, struct statvfs *sfs) int res = -1; fhandler_base *fh = NULL; - myfault efault; - if (efault.faulted (EFAULT)) - goto error; - - if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes))) - goto error; - - if (fh->error ()) + __try { - debug_printf ("got %d error from build_fh_name", fh->error ()); - set_errno (fh->error ()); - } - else if (fh->exists ()) - { - debug_printf ("(%s, %p), file_attributes %d", name, sfs, (DWORD) *fh); - res = fh->fstatvfs (sfs); - } - else - set_errno (ENOENT); + if (!(fh = build_fh_name (name, PC_SYM_FOLLOW, stat_suffixes))) + __leave; + if (fh->error ()) + { + debug_printf ("got %d error from build_fh_name", fh->error ()); + set_errno (fh->error ()); + } + else if (fh->exists ()) + { + debug_printf ("(%s, %p), file_attributes %d", name, sfs, (DWORD) *fh); + res = fh->fstatvfs (sfs); + } + else + set_errno (ENOENT); + + } + __except (EFAULT) {} + __endtry delete fh; - error: MALLOC_CHECK; - syscall_printf ("%R = statvfs(%s,%p)", res, name, sfs); + if (get_errno () != EFAULT) + syscall_printf ("%R = statvfs(%s,%p)", res, name, sfs); return res; } @@ -3156,55 +3175,58 @@ mknod_worker (const char *path, mode_t type, mode_t mode, _major_t major, extern "C" int mknod32 (const char *path, mode_t mode, dev_t dev) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (!*path) + __try { - set_errno (ENOENT); - return -1; + if (!*path) + { + set_errno (ENOENT); + __leave; + } + + if (strlen (path) >= PATH_MAX) + __leave; + + path_conv w32path (path, PC_SYM_NOFOLLOW); + if (w32path.exists ()) + { + set_errno (EEXIST); + __leave; + } + + mode_t type = mode & S_IFMT; + _major_t major = _major (dev); + _minor_t minor = _minor (dev); + switch (type) + { + case S_IFCHR: + case S_IFBLK: + break; + + case S_IFIFO: + major = _major (FH_FIFO); + minor = _minor (FH_FIFO); + break; + + case 0: + case S_IFREG: + { + int fd = open (path, O_CREAT, mode); + if (fd < 0) + __leave; + close (fd); + return 0; + } + + default: + set_errno (EINVAL); + __leave; + } + + return mknod_worker (w32path.get_win32 (), type, mode, major, minor); } - - if (strlen (path) >= PATH_MAX) - return -1; - - path_conv w32path (path, PC_SYM_NOFOLLOW); - if (w32path.exists ()) - { - set_errno (EEXIST); - return -1; - } - - mode_t type = mode & S_IFMT; - _major_t major = _major (dev); - _minor_t minor = _minor (dev); - switch (type) - { - case S_IFCHR: - case S_IFBLK: - break; - - case S_IFIFO: - major = _major (FH_FIFO); - minor = _minor (FH_FIFO); - break; - - case 0: - case S_IFREG: - { - int fd = open (path, O_CREAT, mode); - if (fd < 0) - return -1; - close (fd); - return 0; - } - - default: - set_errno (EINVAL); - return -1; - } - - return mknod_worker (w32path.get_win32 (), type, mode, major, minor); + __except (EFAULT) + __endtry + return -1; } extern "C" int @@ -3815,23 +3837,19 @@ nice (int incr) 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; + return __builtin_ffs (i); +} - a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24); +extern "C" int +ffsl (long i) +{ + return __builtin_ffsl (i); +} - return table[x >> a] + a; +extern "C" int +ffsll (long long i) +{ + return __builtin_ffsll (i); } static void @@ -3913,15 +3931,19 @@ endutent () extern "C" void utmpname (const char *file) { - myfault efault; - if (efault.faulted () || !*file) + __try { - debug_printf ("Invalid file"); - return; + if (*file) + { + endutent (); + utmp_file = strdup (file); + debug_printf ("New UTMP file: %s", utmp_file); + return; + } } - endutent (); - utmp_file = strdup (file); - debug_printf ("New UTMP file: %s", utmp_file); + __except (NO_ERROR) {} + __endtry + debug_printf ("Invalid file"); } EXPORT_ALIAS (utmpname, utmpxname) @@ -3966,94 +3988,99 @@ getutent () extern "C" struct utmp * getutid (const struct utmp *id) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - if (utmp_fd < 0) + __try { - 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; + internal_setutent (false); + if (utmp_fd < 0) + __leave; + } + 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: + break; + } } } + __except (EFAULT) {} + __endtry return NULL; } extern "C" struct utmp * getutline (const struct utmp *line) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - if (utmp_fd < 0) + __try { - internal_setutent (false); if (utmp_fd < 0) - return NULL; + { + internal_setutent (false); + if (utmp_fd < 0) + __leave; + } + + 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; } - - 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; - + __except (EFAULT) {} + __endtry return NULL; } extern "C" struct utmp * pututline (const struct utmp *ut) { - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - internal_setutent (true); - if (utmp_fd < 0) + __try { - debug_printf ("error: utmp_fd %d", utmp_fd); - return NULL; - } - 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); + internal_setutent (true); + if (utmp_fd < 0) + { + debug_printf ("error: utmp_fd %d", utmp_fd); + __leave; + } + 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); - struct utmp *u; - if ((u = getutid (ut))) - { - lseek (utmp_fd, -sizeof *ut, SEEK_CUR); - write (utmp_fd, ut, sizeof *ut); + struct utmp *u; + if ((u = getutid (ut))) + { + lseek (utmp_fd, -sizeof *ut, SEEK_CUR); + write (utmp_fd, ut, sizeof *ut); + } + else + locked_append (utmp_fd, ut, sizeof *ut); + /* The documentation says to return a pointer to this which implies that + this has to be cast from a const. That doesn't seem right but the + documentation seems pretty clear on this. */ + return (struct utmp *) ut; } - else - locked_append (utmp_fd, ut, sizeof *ut); - /* The documentation says to return a pointer to this which implies that - this has to be cast from a const. That doesn't seem right but the - documentation seems pretty clear on this. */ - return (struct utmp *) ut; + __except (EFAULT) {} + __endtry + return NULL; } extern "C" void @@ -4071,7 +4098,7 @@ endutxent () extern "C" struct utmpx * getutxent () { - /* UGH. Not thread safe. */ + /* POSIX: Not required to be thread safe. */ static struct utmpx utx; return copy_ut_to_utx (getutent (), &utx); } @@ -4079,40 +4106,49 @@ getutxent () extern "C" struct utmpx * getutxid (const struct utmpx *id) { - /* UGH. Not thread safe. */ + /* POSIX: Not required to be thread safe. */ static struct utmpx utx; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - ((struct utmpx *)id)->ut_time = id->ut_tv.tv_sec; - return copy_ut_to_utx (getutid ((struct utmp *) id), &utx); + __try + { + ((struct utmpx *)id)->ut_time = id->ut_tv.tv_sec; + return copy_ut_to_utx (getutid ((struct utmp *) id), &utx); + } + __except (EFAULT) {} + __endtry + return NULL; } extern "C" struct utmpx * getutxline (const struct utmpx *line) { - /* UGH. Not thread safe. */ + /* POSIX: Not required to be thread safe. */ static struct utmpx utx; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - ((struct utmpx *)line)->ut_time = line->ut_tv.tv_sec; - return copy_ut_to_utx (getutline ((struct utmp *) line), &utx); + __try + { + ((struct utmpx *)line)->ut_time = line->ut_tv.tv_sec; + return copy_ut_to_utx (getutline ((struct utmp *) line), &utx); + } + __except (EFAULT) {} + __endtry + return NULL; } extern "C" struct utmpx * pututxline (const struct utmpx *utmpx) { - /* UGH. Not thread safe. */ + /* POSIX: Not required to be thread safe. */ static struct utmpx utx; - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec; - return copy_ut_to_utx (pututline ((struct utmp *) utmpx), &utx); + __try + { + ((struct utmpx *)utmpx)->ut_time = utmpx->ut_tv.tv_sec; + return copy_ut_to_utx (pututline ((struct utmp *) utmpx), &utx); + } + __except (EFAULT) {} + __endtry + return NULL; } extern "C" void @@ -4476,51 +4512,57 @@ extern "C" int openat (int dirfd, const char *pathname, int flags, ...) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; - va_list ap; - mode_t mode; + va_list ap; + mode_t mode; - va_start (ap, flags); - mode = va_arg (ap, mode_t); - va_end (ap); - return open (path, flags, mode); + va_start (ap, flags); + mode = va_arg (ap, mode_t); + va_end (ap); + return open (path, flags, mode); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int faccessat (int dirfd, const char *pathname, int mode, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - int res = -1; - char *path = tp.c_get (); - if (!gen_full_path_at (path, dirfd, pathname)) + + __try { - if ((mode & ~(F_OK|R_OK|W_OK|X_OK)) - || (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EACCESS))) - set_errno (EINVAL); - else + char *path = tp.c_get (); + if (!gen_full_path_at (path, dirfd, pathname)) { - fhandler_base *fh = build_fh_name (path, (flags & AT_SYMLINK_NOFOLLOW - ? PC_SYM_NOFOLLOW - : PC_SYM_FOLLOW) - | PC_KEEP_HANDLE, - stat_suffixes); - if (fh) + if ((mode & ~(F_OK|R_OK|W_OK|X_OK)) + || (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EACCESS))) + set_errno (EINVAL); + else { - res = fh->fhaccess (mode, !!(flags & AT_EACCESS)); - delete fh; + fhandler_base *fh = build_fh_name (path, + (flags & AT_SYMLINK_NOFOLLOW + ? PC_SYM_NOFOLLOW + : PC_SYM_FOLLOW) + | PC_KEEP_HANDLE, + stat_suffixes); + if (fh) + { + res = fh->fhaccess (mode, !!(flags & AT_EACCESS)); + delete fh; + } } } } + __except (EFAULT) {} + __endtry debug_printf ("returning %d", res); return res; } @@ -4529,40 +4571,46 @@ extern "C" int fchmodat (int dirfd, const char *pathname, mode_t mode, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (flags) + __try { - /* BSD has lchmod, but Linux does not. POSIX says - AT_SYMLINK_NOFOLLOW is allowed to fail on symlinks; but Linux - blindly fails even for non-symlinks. */ - set_errno ((flags & ~AT_SYMLINK_NOFOLLOW) ? EINVAL : EOPNOTSUPP); - return -1; + if (flags) + { + /* BSD has lchmod, but Linux does not. POSIX says + AT_SYMLINK_NOFOLLOW is allowed to fail on symlinks; but Linux + blindly fails even for non-symlinks. */ + set_errno ((flags & ~AT_SYMLINK_NOFOLLOW) ? EINVAL : EOPNOTSUPP); + __leave; + } + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return chmod (path, mode); } - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return chmod (path, mode); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int fchownat (int dirfd, const char *pathname, uid_t uid, gid_t gid, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (flags & ~AT_SYMLINK_NOFOLLOW) + __try { - set_errno (EINVAL); - return -1; + if (flags & ~AT_SYMLINK_NOFOLLOW) + { + set_errno (EINVAL); + __leave; + } + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW) + ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid); } - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return chown_worker (path, (flags & AT_SYMLINK_NOFOLLOW) - ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW, uid, gid); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -4570,21 +4618,24 @@ fstatat (int dirfd, const char *__restrict pathname, struct stat *__restrict st, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (flags & ~AT_SYMLINK_NOFOLLOW) + __try { - set_errno (EINVAL); - return -1; + if (flags & ~AT_SYMLINK_NOFOLLOW) + { + set_errno (EINVAL); + __leave; + } + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW) + ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) + | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes); + return stat_worker (pc, st); } - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - path_conv pc (path, ((flags & AT_SYMLINK_NOFOLLOW) - ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) - | PC_POSIX | PC_KEEP_HANDLE, stat_suffixes); - return stat_worker (pc, st); + __except (EFAULT) {} + __endtry + return -1; } extern int utimens_worker (path_conv &, const struct timespec *); @@ -4594,34 +4645,40 @@ utimensat (int dirfd, const char *pathname, const struct timespec *times, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (flags & ~AT_SYMLINK_NOFOLLOW) + __try { - set_errno (EINVAL); - return -1; + char *path = tp.c_get (); + if (flags & ~AT_SYMLINK_NOFOLLOW) + { + set_errno (EINVAL); + __leave; + } + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + path_conv win32 (path, PC_POSIX | ((flags & AT_SYMLINK_NOFOLLOW) + ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW), + stat_suffixes); + return utimens_worker (win32, times); } - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - path_conv win32 (path, PC_POSIX | ((flags & AT_SYMLINK_NOFOLLOW) - ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW), - stat_suffixes); - return utimens_worker (win32, times); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int futimesat (int dirfd, const char *pathname, const struct timeval *times) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname, true)) - return -1; - return utimes (path, times); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname, true)) + __leave; + return utimes (path, times); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -4630,70 +4687,82 @@ linkat (int olddirfd, const char *oldpathname, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (flags & ~AT_SYMLINK_FOLLOW) + __try { - set_errno (EINVAL); - return -1; - } - char *oldpath = tp.c_get (); - if (gen_full_path_at (oldpath, olddirfd, oldpathname)) - return -1; - char *newpath = tp.c_get (); - if (gen_full_path_at (newpath, newdirfd, newpathname)) - return -1; - if (flags & AT_SYMLINK_FOLLOW) - { - path_conv old_name (oldpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); - if (old_name.error) + if (flags & ~AT_SYMLINK_FOLLOW) { - set_errno (old_name.error); - return -1; + set_errno (EINVAL); + __leave; } - strcpy (oldpath, old_name.normalized_path); + char *oldpath = tp.c_get (); + if (gen_full_path_at (oldpath, olddirfd, oldpathname)) + __leave; + char *newpath = tp.c_get (); + if (gen_full_path_at (newpath, newdirfd, newpathname)) + __leave; + if (flags & AT_SYMLINK_FOLLOW) + { + path_conv old_name (oldpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes); + if (old_name.error) + { + set_errno (old_name.error); + __leave; + } + strcpy (oldpath, old_name.normalized_path); + } + return link (oldpath, newpath); } - return link (oldpath, newpath); + __except (EFAULT) {} + __endtry + return -1; } extern "C" int mkdirat (int dirfd, const char *pathname, mode_t mode) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return mkdir (path, mode); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return mkdir (path, mode); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int mkfifoat (int dirfd, const char *pathname, mode_t mode) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return mkfifo (path, mode); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return mkfifo (path, mode); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int mknodat (int dirfd, const char *pathname, mode_t mode, dev_t dev) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return mknod32 (path, mode, dev); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return mknod32 (path, mode, dev); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" ssize_t @@ -4701,13 +4770,16 @@ readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf, size_t bufsize) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return readlink (path, buf, bufsize); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return readlink (path, buf, bufsize); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -4715,16 +4787,19 @@ renameat (int olddirfd, const char *oldpathname, int newdirfd, const char *newpathname) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *oldpath = tp.c_get (); - if (gen_full_path_at (oldpath, olddirfd, oldpathname)) - return -1; - char *newpath = tp.c_get (); - if (gen_full_path_at (newpath, newdirfd, newpathname)) - return -1; - return rename (oldpath, newpath); + __try + { + char *oldpath = tp.c_get (); + if (gen_full_path_at (oldpath, olddirfd, oldpathname)) + __leave; + char *newpath = tp.c_get (); + if (gen_full_path_at (newpath, newdirfd, newpathname)) + __leave; + return rename (oldpath, newpath); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -4733,42 +4808,51 @@ scandirat (int dirfd, const char *pathname, struct dirent ***namelist, int (*compar) (const struct dirent **, const struct dirent **)) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return scandir (pathname, namelist, select, compar); + __try + { + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return scandir (pathname, namelist, select, compar); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int symlinkat (const char *oldpath, int newdirfd, const char *newpathname) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - char *newpath = tp.c_get (); - if (gen_full_path_at (newpath, newdirfd, newpathname)) - return -1; - return symlink (oldpath, newpath); + __try + { + char *newpath = tp.c_get (); + if (gen_full_path_at (newpath, newdirfd, newpathname)) + __leave; + return symlink (oldpath, newpath); + } + __except (EFAULT) {} + __endtry + return -1; } extern "C" int unlinkat (int dirfd, const char *pathname, int flags) { tmp_pathbuf tp; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (flags & ~AT_REMOVEDIR) + __try { - set_errno (EINVAL); - return -1; + if (flags & ~AT_REMOVEDIR) + { + set_errno (EINVAL); + __leave; + } + char *path = tp.c_get (); + if (gen_full_path_at (path, dirfd, pathname)) + __leave; + return (flags & AT_REMOVEDIR) ? rmdir (path) : unlink (path); } - char *path = tp.c_get (); - if (gen_full_path_at (path, dirfd, pathname)) - return -1; - return (flags & AT_REMOVEDIR) ? rmdir (path) : unlink (path); + __except (EFAULT) {} + __endtry + return -1; } diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index dc4faac7c..f801d5962 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -1,7 +1,7 @@ /* thread.cc: Locking and threading module functions Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -38,12 +38,7 @@ extern "C" void __fp_lock_all (); extern "C" void __fp_unlock_all (); extern "C" int valid_sched_parameters(const struct sched_param *); extern "C" int sched_set_thread_priority(HANDLE thread, int priority); -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 7 -/* FIXME: Temporarily workaround gcc 4.7+ bug. */ -static verifyable_object_state -#else static inline verifyable_object_state -#endif verifyable_object_isvalid (void const * objectptr, thread_magic_t magic, void *static_ptr1 = NULL, void *static_ptr2 = NULL, @@ -122,28 +117,29 @@ __cygwin_lock_unlock (_LOCK_T *lock) paranoid_printf ("threadcount %d. unlocked", MT_INTERFACE->threadcount); } -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 7 -/* FIXME: Temporarily workaround gcc 4.7+ bug. */ -static verifyable_object_state -#else static inline verifyable_object_state -#endif verifyable_object_isvalid (void const *objectptr, thread_magic_t magic, void *static_ptr1, void *static_ptr2, void *static_ptr3) { - myfault efault; - if (efault.faulted (objectptr)) - return INVALID_OBJECT; + verifyable_object_state state = INVALID_OBJECT; - verifyable_object **object = (verifyable_object **) objectptr; + __try + { + if (!objectptr || !(*(const char **) objectptr)) + __leave; - if ((static_ptr1 && *object == static_ptr1) || - (static_ptr2 && *object == static_ptr2) || - (static_ptr3 && *object == static_ptr3)) - return VALID_STATIC_OBJECT; - if ((*object)->magic != magic) - return INVALID_OBJECT; - return VALID_OBJECT; + verifyable_object **object = (verifyable_object **) objectptr; + + if ((static_ptr1 && *object == static_ptr1) || + (static_ptr2 && *object == static_ptr2) || + (static_ptr3 && *object == static_ptr3)) + state = VALID_STATIC_OBJECT; + else if ((*object)->magic == magic) + state = VALID_OBJECT; + } + __except (NO_ERROR) {} + __endtry + return state; } /* static members */ @@ -2684,18 +2680,20 @@ pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) return EAGAIN; } - myfault efault; - if (efault.faulted ()) + int ret = 0; + + __try + { + *cond = new_cond; + } + __except (NO_ERROR) { delete new_cond; - cond_initialization_lock.unlock (); - return EINVAL; + ret = EINVAL; } - - *cond = new_cond; + __endtry cond_initialization_lock.unlock (); - - return 0; + return ret; } extern "C" int @@ -2747,45 +2745,47 @@ pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec tp; LARGE_INTEGER timeout; - myfault efault; - if (efault.faulted ()) - return EINVAL; - pthread_testcancel (); - int err = __pthread_cond_wait_init (cond, mutex); - if (err) - return err; - - /* According to SUSv3, the abstime value must be checked for validity. */ - if (abstime->tv_sec < 0 - || abstime->tv_nsec < 0 - || abstime->tv_nsec > 999999999) - return EINVAL; - - clock_gettime ((*cond)->clock_id, &tp); - - /* Check for immediate timeout before converting */ - if (tp.tv_sec > abstime->tv_sec - || (tp.tv_sec == abstime->tv_sec - && tp.tv_nsec > abstime->tv_nsec)) - return ETIMEDOUT; - - timeout.QuadPart = abstime->tv_sec * NSPERSEC - + (abstime->tv_nsec + 99LL) / 100LL; - - switch ((*cond)->clock_id) + __try { - case CLOCK_REALTIME: - timeout.QuadPart += FACTOR; - break; - default: - /* other clocks must be handled as relative timeout */ - timeout.QuadPart -= tp.tv_sec * NSPERSEC + tp.tv_nsec / 100LL; - timeout.QuadPart *= -1LL; - break; + int err = __pthread_cond_wait_init (cond, mutex); + if (err) + return err; + + /* According to SUSv3, the abstime value must be checked for validity. */ + if (abstime->tv_sec < 0 + || abstime->tv_nsec < 0 + || abstime->tv_nsec > 999999999) + __leave; + + clock_gettime ((*cond)->clock_id, &tp); + + /* Check for immediate timeout before converting */ + if (tp.tv_sec > abstime->tv_sec + || (tp.tv_sec == abstime->tv_sec + && tp.tv_nsec > abstime->tv_nsec)) + return ETIMEDOUT; + + timeout.QuadPart = abstime->tv_sec * NSPERSEC + + (abstime->tv_nsec + 99LL) / 100LL; + + switch ((*cond)->clock_id) + { + case CLOCK_REALTIME: + timeout.QuadPart += FACTOR; + break; + default: + /* other clocks must be handled as relative timeout */ + timeout.QuadPart -= tp.tv_sec * NSPERSEC + tp.tv_nsec / 100LL; + timeout.QuadPart *= -1LL; + break; + } + return (*cond)->wait (*mutex, &timeout); } - return (*cond)->wait (*mutex, &timeout); + __except (NO_ERROR) {} + __endtry + return EINVAL; } extern "C" int @@ -2910,18 +2910,20 @@ pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr return EAGAIN; } - myfault efault; - if (efault.faulted ()) + int ret = 0; + + __try + { + *rwlock = new_rwlock; + } + __except (NO_ERROR) { delete new_rwlock; - rwlock_initialization_lock.unlock (); - return EINVAL; + ret = EINVAL; } - - *rwlock = new_rwlock; + __endtry rwlock_initialization_lock.unlock (); - - return 0; + return ret; } extern "C" int @@ -3133,15 +3135,17 @@ pthread_mutex::init (pthread_mutex_t *mutex, new_mutex->type = PTHREAD_MUTEX_ERRORCHECK; } - myfault efault; - if (efault.faulted ()) + __try + { + *mutex = new_mutex; + } + __except (NO_ERROR) { delete new_mutex; mutex_initialization_lock.unlock (); return EINVAL; } - - *mutex = new_mutex; + __endtry } mutex_initialization_lock.unlock (); pthread_printf ("*mutex %p, attr %p, initializer %p", *mutex, attr, initializer); @@ -3230,16 +3234,17 @@ pthread_spinlock::init (pthread_spinlock_t *spinlock, int pshared) return EAGAIN; } - myfault efault; - if (efault.faulted ()) + __try + { + *spinlock = new_spinlock; + } + __except (NO_ERROR) { delete new_spinlock; return EINVAL; } - - *spinlock = new_spinlock; + __endtry pthread_printf ("*spinlock %p, pshared %d", *spinlock, pshared); - return 0; } @@ -3502,35 +3507,38 @@ semaphore::_timedwait (const struct timespec *abstime) { LARGE_INTEGER timeout; - myfault efault; - if (efault.faulted ()) + __try + { + timeout.QuadPart = abstime->tv_sec * NSPERSEC + + (abstime->tv_nsec + 99) / 100 + FACTOR; + + switch (cygwait (win32_obj_id, &timeout, cw_cancel | cw_cancel_self | cw_sig_eintr)) + { + case WAIT_OBJECT_0: + break; + case WAIT_SIGNALED: + set_errno (EINTR); + return -1; + case WAIT_TIMEOUT: + set_errno (ETIMEDOUT); + return -1; + default: + pthread_printf ("cygwait failed. %E"); + __seterrno (); + return -1; + } + } + __except (NO_ERROR) { /* According to SUSv3, abstime need not be checked for validity, if the semaphore can be locked immediately. */ - if (!_trywait ()) - return 0; - set_errno (EINVAL); - return -1; - } - - timeout.QuadPart = abstime->tv_sec * NSPERSEC - + (abstime->tv_nsec + 99) / 100 + FACTOR; - - switch (cygwait (win32_obj_id, &timeout, cw_cancel | cw_cancel_self | cw_sig_eintr)) - { - case WAIT_OBJECT_0: - break; - case WAIT_SIGNALED: - set_errno (EINTR); - return -1; - case WAIT_TIMEOUT: - set_errno (ETIMEDOUT); - return -1; - default: - pthread_printf ("cygwait failed. %E"); - __seterrno (); - return -1; + if (_trywait ()) + { + set_errno (EINVAL); + return -1; + } } + __endtry return 0; } @@ -3761,36 +3769,38 @@ semaphore::post (sem_t *sem) int semaphore::getvalue (sem_t *sem, int *sval) { - myfault efault; - if (efault.faulted () || !is_good_object (sem)) + __try { - set_errno (EINVAL); - return -1; + if (is_good_object (sem)) + return (*sem)->_getvalue (sval); } - - return (*sem)->_getvalue (sval); + __except (NO_ERROR) {} + __endtry + set_errno (EINVAL); + return -1; } int semaphore::getinternal (sem_t *sem, int *sfd, unsigned long long *shash, LUID *sluid, unsigned int *sval) { - myfault efault; - if (efault.faulted () || !is_good_object (sem)) + __try { - set_errno (EINVAL); - return -1; + if (!is_good_object (sem)) + __leave; + if ((*sfd = (*sem)->fd) < 0) + __leave; + *shash = (*sem)->hash; + *sluid = (*sem)->luid; + /* POSIX defines the value in calls to sem_init/sem_open as unsigned, + but the sem_getvalue gets a pointer to int to return the value. + Go figure! */ + return (*sem)->_getvalue ((int *)sval); } - if ((*sfd = (*sem)->fd) < 0) - { - set_errno (EINVAL); - return -1; - } - *shash = (*sem)->hash; - *sluid = (*sem)->luid; - /* POSIX defines the value in calls to sem_init/sem_open as unsigned, but - the sem_getvalue gets a pointer to int to return the value. Go figure! */ - return (*sem)->_getvalue ((int *)sval); + __except (NO_ERROR) {} + __endtry + set_errno (EINVAL); + return -1; } /* pthread_null */ diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index b1c6e27aa..bfa1495f6 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -1,6 +1,6 @@ /* timer.cc - Copyright 2004, 2005, 2006, 2008, 2010, 2011, 2012, 2013 Red Hat, Inc. + Copyright 2004, 2005, 2006, 2008, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -219,45 +219,49 @@ it_bad (const timespec& t) int timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalue) { - if (!value) + int ret = -1; + + __try { - set_errno (EINVAL); - return -1; - } + if (!value) + { + set_errno (EINVAL); + __leave; + } - myfault efault; - if (efault.faulted (EFAULT) - || it_bad (value->it_value) - || it_bad (value->it_interval)) - return -1; + if (it_bad (value->it_value) || it_bad (value->it_interval)) + __leave; - long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs (); + long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs (); - lock_timer_tracker here; - cancel (); + lock_timer_tracker here; + cancel (); - if (ovalue) - gettime (ovalue); + if (ovalue) + gettime (ovalue); - if (!value->it_value.tv_sec && !value->it_value.tv_nsec) - interval_us = sleepto_us = 0; - else - { - sleepto_us = now + to_us (value->it_value); - interval_us = to_us (value->it_interval); - it_interval = value->it_interval; - if (!hcancel) - hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + if (!value->it_value.tv_sec && !value->it_value.tv_nsec) + interval_us = sleepto_us = 0; else - ResetEvent (hcancel); - if (!syncthread) - syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); - else - ResetEvent (syncthread); - new cygthread (timer_thread, this, "itimer", syncthread); + { + sleepto_us = now + to_us (value->it_value); + interval_us = to_us (value->it_interval); + it_interval = value->it_interval; + if (!hcancel) + hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + else + ResetEvent (hcancel); + if (!syncthread) + syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + else + ResetEvent (syncthread); + new cygthread (timer_thread, this, "itimer", syncthread); + } + ret = 0; } - - return 0; + __except (EFAULT) {} + __endtry + return ret; } void @@ -280,43 +284,51 @@ timer_tracker::gettime (itimerspec *ovalue) extern "C" int timer_gettime (timer_t timerid, struct itimerspec *ovalue) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; + int ret = -1; - timer_tracker *tt = (timer_tracker *) timerid; - if (tt->magic != TT_MAGIC) + __try { - set_errno (EINVAL); - return -1; - } + timer_tracker *tt = (timer_tracker *) timerid; + if (tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + return -1; + } - tt->gettime (ovalue); - return 0; + tt->gettime (ovalue); + ret = 0; + } + __except (EFAULT) {} + __endtry + return ret; } extern "C" int timer_create (clockid_t clock_id, struct sigevent *__restrict evp, timer_t *__restrict timerid) { - myfault efault; - if (efault.faulted (EFAULT)) - return -1; + int ret = -1; - if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) + __try { - set_errno (ENOTSUP); - return -1; - } + if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) + { + set_errno (ENOTSUP); + return -1; + } - if (clock_id != CLOCK_REALTIME) - { - set_errno (EINVAL); - return -1; - } + if (clock_id != CLOCK_REALTIME) + { + set_errno (EINVAL); + return -1; + } - *timerid = (timer_t) new timer_tracker (clock_id, evp); - return 0; + *timerid = (timer_t) new timer_tracker (clock_id, evp); + ret = 0; + } + __except (EFAULT) {} + __endtry + return ret; } extern "C" int @@ -324,42 +336,52 @@ timer_settime (timer_t timerid, int flags, const struct itimerspec *__restrict value, struct itimerspec *__restrict ovalue) { - timer_tracker *tt = (timer_tracker *) timerid; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (tt->magic != TT_MAGIC) - { - set_errno (EINVAL); - return -1; - } + int ret = -1; - return tt->settime (flags, value, ovalue); + __try + { + timer_tracker *tt = (timer_tracker *) timerid; + if (tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + __leave; + } + ret = tt->settime (flags, value, ovalue); + } + __except (EFAULT) {} + __endtry + return ret; } extern "C" int timer_delete (timer_t timerid) { - timer_tracker *in_tt = (timer_tracker *) timerid; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - if (in_tt->magic != TT_MAGIC) - { - set_errno (EINVAL); - return -1; - } + int ret = -1; - lock_timer_tracker here; - for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next) - if (tt->next == in_tt) - { - tt->next = in_tt->next; - delete in_tt; - return 0; - } - set_errno (EINVAL); - return 0; + __try + { + timer_tracker *in_tt = (timer_tracker *) timerid; + if (in_tt->magic != TT_MAGIC) + { + set_errno (EINVAL); + __leave; + } + + lock_timer_tracker here; + for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next) + if (tt->next == in_tt) + { + tt->next = in_tt->next; + delete in_tt; + ret = 0; + __leave; + } + set_errno (EINVAL); + ret = 0; + } + __except (EFAULT) {} + __endtry + return ret; } void @@ -412,18 +434,13 @@ setitimer (int which, const struct itimerval *__restrict value, extern "C" int getitimer (int which, struct itimerval *ovalue) { - int ret; + int ret = -1; + if (which != ITIMER_REAL) - { - set_errno (EINVAL); - ret = -1; - } + set_errno (EINVAL); else { - myfault efault; - if (efault.faulted (EFAULT)) - ret = -1; - else + __try { struct itimerspec spec_ovalue; ret = timer_gettime ((timer_t) &ttstart, &spec_ovalue); @@ -435,6 +452,8 @@ getitimer (int which, struct itimerval *ovalue) ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000; } } + __except (EFAULT) {} + __endtry } syscall_printf ("%R = getitimer()", ret); return ret; diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc index 55157d389..bd8f1cf0e 100644 --- a/winsup/cygwin/times.cc +++ b/winsup/cygwin/times.cc @@ -1,7 +1,7 @@ /* times.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -59,35 +59,39 @@ __to_clock_t (PLARGE_INTEGER src, int flag) extern "C" clock_t times (struct tms *buf) { - myfault efault; - if (efault.faulted (EFAULT)) - return ((clock_t) -1); - static SYSTEM_TIMEOFDAY_INFORMATION stodi; KERNEL_USER_TIMES kut; LARGE_INTEGER ticks; + clock_t tc = (clock_t) -1; - /* Fetch boot time if we haven't already. */ - if (!stodi.BootTime.QuadPart) - NtQuerySystemInformation (SystemTimeOfDayInformation, - &stodi, sizeof stodi, NULL); + __try + { + /* Fetch boot time if we haven't already. */ + if (!stodi.BootTime.QuadPart) + NtQuerySystemInformation (SystemTimeOfDayInformation, + &stodi, sizeof stodi, NULL); - NtQueryInformationProcess (NtCurrentProcess (), ProcessTimes, - &kut, sizeof kut, NULL); - get_system_time (&ticks); + NtQueryInformationProcess (NtCurrentProcess (), ProcessTimes, + &kut, sizeof kut, NULL); + get_system_time (&ticks); - /* uptime */ - ticks.QuadPart -= stodi.BootTime.QuadPart; - /* ticks is in in 100ns, convert to clock ticks. */ - clock_t tc = (clock_t) (ticks.QuadPart * CLOCKS_PER_SEC / NSPERSEC); - - buf->tms_stime = __to_clock_t (&kut.KernelTime, 0); - buf->tms_utime = __to_clock_t (&kut.UserTime, 0); - timeval_to_filetime (&myself->rusage_children.ru_stime, &kut.KernelTime); - buf->tms_cstime = __to_clock_t (&kut.KernelTime, 1); - timeval_to_filetime (&myself->rusage_children.ru_utime, &kut.UserTime); - buf->tms_cutime = __to_clock_t (&kut.UserTime, 1); + /* uptime */ + ticks.QuadPart -= stodi.BootTime.QuadPart; + /* ticks is in in 100ns, convert to clock ticks. */ + tc = (clock_t) (ticks.QuadPart * CLOCKS_PER_SEC / NSPERSEC); + buf->tms_stime = __to_clock_t (&kut.KernelTime, 0); + buf->tms_utime = __to_clock_t (&kut.UserTime, 0); + timeval_to_filetime (&myself->rusage_children.ru_stime, &kut.KernelTime); + buf->tms_cstime = __to_clock_t (&kut.KernelTime, 1); + timeval_to_filetime (&myself->rusage_children.ru_utime, &kut.UserTime); + buf->tms_cutime = __to_clock_t (&kut.UserTime, 1); + } + __except (EFAULT) + { + tc = (clock_t) -1; + } + __endtry syscall_printf ("%D = times(%p)", tc, buf); return tc; } @@ -100,38 +104,49 @@ settimeofday (const struct timeval *tv, const struct timezone *tz) { SYSTEMTIME st; struct tm *ptm; - int res; + int res = -1; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - if (tv->tv_usec < 0 || tv->tv_usec >= 1000000) + __try { - set_errno (EINVAL); - return -1; + if (tv->tv_usec < 0 || tv->tv_usec >= 1000000) + { + set_errno (EINVAL); + return -1; + } + + ptm = gmtime (&tv->tv_sec); + st.wYear = ptm->tm_year + 1900; + st.wMonth = ptm->tm_mon + 1; + st.wDayOfWeek = ptm->tm_wday; + st.wDay = ptm->tm_mday; + st.wHour = ptm->tm_hour; + st.wMinute = ptm->tm_min; + st.wSecond = ptm->tm_sec; + st.wMilliseconds = tv->tv_usec / 1000; + + res = -!SetSystemTime (&st); + gtod.reset (); + + if (res) + set_errno (EPERM); } - - ptm = gmtime (&tv->tv_sec); - st.wYear = ptm->tm_year + 1900; - st.wMonth = ptm->tm_mon + 1; - st.wDayOfWeek = ptm->tm_wday; - st.wDay = ptm->tm_mday; - st.wHour = ptm->tm_hour; - st.wMinute = ptm->tm_min; - st.wSecond = ptm->tm_sec; - st.wMilliseconds = tv->tv_usec / 1000; - - res = -!SetSystemTime (&st); - gtod.reset (); - - if (res) - set_errno (EPERM); - + __except (EFAULT) + { + res = -1; + } + __endtry syscall_printf ("%R = settimeofday(%p, %p)", res, tv, tz); return res; } +/* stime: SVr4 */ +extern "C" int +stime (const time_t *t) +{ + struct timeval tv = { *t, 0 }; + return settimeofday(&tv, NULL); +} + /* timezone: standards? */ extern "C" char * timezone (void) diff --git a/winsup/cygwin/tls_pbuf.cc b/winsup/cygwin/tls_pbuf.cc index 2f817ec92..9c12a2df8 100644 --- a/winsup/cygwin/tls_pbuf.cc +++ b/winsup/cygwin/tls_pbuf.cc @@ -8,7 +8,6 @@ details. */ #include #include -#include "cygtls.h" #include "tls_pbuf.h" #define tls_pbuf _my_tls.locals.pathbufs @@ -16,23 +15,12 @@ details. */ void tls_pathbuf::destroy () { - for (unsigned i = 0; i < TP_NUM_C_BUFS && c_buf[i]; ++i) + for (uint32_t i = 0; i < TP_NUM_C_BUFS && c_buf[i]; ++i) free (c_buf[i]); - for (unsigned i = 0; i < TP_NUM_W_BUFS && w_buf[i]; ++i) + for (uint32_t i = 0; i < TP_NUM_W_BUFS && w_buf[i]; ++i) free (w_buf[i]); } -tmp_pathbuf::tmp_pathbuf () -: c_buf_old (tls_pbuf.c_cnt), - w_buf_old (tls_pbuf.w_cnt) -{} - -tmp_pathbuf::~tmp_pathbuf () -{ - tls_pbuf.c_cnt = c_buf_old; - tls_pbuf.w_cnt = w_buf_old; -} - char * tmp_pathbuf::c_get () { diff --git a/winsup/cygwin/tls_pbuf.h b/winsup/cygwin/tls_pbuf.h index 33ee20b79..e8526bab0 100644 --- a/winsup/cygwin/tls_pbuf.h +++ b/winsup/cygwin/tls_pbuf.h @@ -6,15 +6,24 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ +#pragma once + class tmp_pathbuf { - unsigned c_buf_old; - unsigned w_buf_old; + uint32_t c_buf_old; + uint32_t w_buf_old; public: - tmp_pathbuf (); - ~tmp_pathbuf (); + tmp_pathbuf () __attribute__ ((always_inline)) + : c_buf_old (_my_tls.locals.pathbufs.c_cnt), + w_buf_old (_my_tls.locals.pathbufs.w_cnt) + {} + ~tmp_pathbuf () __attribute__ ((always_inline)) + { + _my_tls.locals.pathbufs.c_cnt = c_buf_old; + _my_tls.locals.pathbufs.w_cnt = w_buf_old; + } - inline bool check_usage (unsigned c_need, unsigned w_need) + inline bool check_usage (uint32_t c_need, uint32_t w_need) { return c_need + c_buf_old < TP_NUM_C_BUFS && w_need + w_buf_old < TP_NUM_W_BUFS; diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 4ca901f35..bb75850a0 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -191,10 +191,15 @@ getlogin_r (char *name, size_t namesize) size_t len = strlen (login) + 1; if (len > namesize) return ERANGE; - myfault efault; - if (efault.faulted ()) - return EFAULT; - strncpy (name, login, len); + __try + { + strncpy (name, login, len); + } + __except (NO_ERROR) + { + return EFAULT; + } + __endtry return 0; } diff --git a/winsup/cygwin/uname.cc b/winsup/cygwin/uname.cc index c571850b4..2990a01da 100644 --- a/winsup/cygwin/uname.cc +++ b/winsup/cygwin/uname.cc @@ -1,7 +1,7 @@ /* uname.cc Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, - 2006, 2007, 2008, 2013 Red Hat, Inc. + 2006, 2007, 2008, 2013, 2014 Red Hat, Inc. Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com Rewritten by Geoffrey Noer of Cygnus Solutions, noer@cygnus.com @@ -22,74 +22,82 @@ uname (struct utsname *name) { SYSTEM_INFO sysinfo; - myfault efault; - if (efault.faulted (EFAULT)) - return -1; - - char *snp = strstr (cygwin_version.dll_build_date, "SNP"); - - memset (name, 0, sizeof (*name)); - __small_sprintf (name->sysname, "CYGWIN_%s", wincap.osname ()); - - /* Add a hint to the sysname, that we're running under WOW64. This might - give an early clue if somebody encounters problems. */ - if (wincap.is_wow64 ()) - strncat (name->sysname, "-WOW64", - sizeof name->sysname - strlen (name->sysname) - 1); - - GetSystemInfo (&sysinfo); - - /* Computer name */ - cygwin_gethostname (name->nodename, sizeof (name->nodename) - 1); - - /* Cygwin dll release */ - __small_sprintf (name->release, "%d.%d.%d%s(%d.%d/%d/%d)", - cygwin_version.dll_major / 1000, - cygwin_version.dll_major % 1000, - cygwin_version.dll_minor, - snp ? "s" : "", - cygwin_version.api_major, - cygwin_version.api_minor, - cygwin_version.shared_data, - cygwin_version.mount_registry); - - /* Cygwin "version" aka build date */ - strcpy (name->version, cygwin_version.dll_build_date); - if (snp) - name->version[snp - cygwin_version.dll_build_date] = '\0'; - - /* CPU type */ - switch (sysinfo.wProcessorArchitecture) + __try { - case PROCESSOR_ARCHITECTURE_INTEL: - unsigned int ptype; - if (sysinfo.wProcessorLevel < 3) /* Shouldn't happen. */ - ptype = 3; - else if (sysinfo.wProcessorLevel > 9) /* P4 */ - ptype = 6; - else - ptype = sysinfo.wProcessorLevel; - __small_sprintf (name->machine, "i%d86", ptype); - break; - case PROCESSOR_ARCHITECTURE_IA64: - strcpy (name->machine, "ia64"); - break; - case PROCESSOR_ARCHITECTURE_AMD64: - strcpy (name->machine, "x86_64"); - break; - case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: - strcpy (name->machine, "ia32-win64"); - break; - case PROCESSOR_ARCHITECTURE_ALPHA: - strcpy (name->machine, "alpha"); - break; - case PROCESSOR_ARCHITECTURE_MIPS: - strcpy (name->machine, "mips"); - break; - default: - strcpy (name->machine, "unknown"); - break; - } + char *snp = strstr (cygwin_version.dll_build_date, "SNP"); + memset (name, 0, sizeof (*name)); + __small_sprintf (name->sysname, "CYGWIN_%s", wincap.osname ()); + + /* Add a hint to the sysname, that we're running under WOW64. This might + give an early clue if somebody encounters problems. */ + if (wincap.is_wow64 ()) + strncat (name->sysname, "-WOW64", + sizeof name->sysname - strlen (name->sysname) - 1); + + GetSystemInfo (&sysinfo); + + /* Computer name */ + cygwin_gethostname (name->nodename, sizeof (name->nodename) - 1); + + /* Cygwin dll release */ + __small_sprintf (name->release, "%d.%d.%d%s(%d.%d/%d/%d)", + cygwin_version.dll_major / 1000, + cygwin_version.dll_major % 1000, + cygwin_version.dll_minor, +#if 0 + snp ? "s" : "", +#else + /* Add a hint to allow to recognize updates */ + "-2", +#endif + cygwin_version.api_major, + cygwin_version.api_minor, + cygwin_version.shared_data, + cygwin_version.mount_registry); + + /* Cygwin "version" aka build date */ + strcpy (name->version, cygwin_version.dll_build_date); + if (snp) + name->version[snp - cygwin_version.dll_build_date] = '\0'; + + /* CPU type */ + switch (sysinfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: + unsigned int ptype; + if (sysinfo.wProcessorLevel < 3) /* Shouldn't happen. */ + ptype = 3; + else if (sysinfo.wProcessorLevel > 9) /* P4 */ + ptype = 6; + else + ptype = sysinfo.wProcessorLevel; + __small_sprintf (name->machine, "i%d86", ptype); + break; + case PROCESSOR_ARCHITECTURE_IA64: + strcpy (name->machine, "ia64"); + break; + case PROCESSOR_ARCHITECTURE_AMD64: + strcpy (name->machine, "x86_64"); + break; + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: + strcpy (name->machine, "ia32-win64"); + break; + case PROCESSOR_ARCHITECTURE_ALPHA: + strcpy (name->machine, "alpha"); + break; + case PROCESSOR_ARCHITECTURE_MIPS: + strcpy (name->machine, "mips"); + break; + default: + strcpy (name->machine, "unknown"); + break; + } + } + __except (EFAULT) + { + return -1; + } + __endtry return 0; } diff --git a/winsup/cygwin/winlean.h b/winsup/cygwin/winlean.h index c50cf747a..64e689dd4 100644 --- a/winsup/cygwin/winlean.h +++ b/winsup/cygwin/winlean.h @@ -58,11 +58,6 @@ details. */ #include #include -/* Temporary kludge for missing flag in Mingw64's w32api. */ -#ifndef PIPE_REJECT_REMOTE_CLIENTS -#define PIPE_REJECT_REMOTE_CLIENTS 8 -#endif - #ifdef __undef_IN #undef IN #endif @@ -79,6 +74,17 @@ details. */ #undef CRITICAL #endif +/* So-called "Microsoft Account" SIDs (S-1-11-...) have a netbios domain name + "MicrosoftAccounts". The new "Application Container SIDs" (S-1-15-...) + have a netbios domain name "APPLICATION PACKAGE AUTHORITY" + + The problem is, DNLEN is 15, but these domain names have a length of 16 + resp. 29 chars :-P So we override DNLEN here to be 31, so that calls + to LookupAccountSid/Name don't fail if the buffer is based on DNLEN. + Hope that's enough for a while... */ +#undef DNLEN +#define DNLEN 31 + /* When Terminal Services are installed, the GetWindowsDirectory function does not return the system installation dir, but a user specific directory instead. That's not what we have in mind when calling GetWindowsDirectory diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index eb7a423dd..99b3e0763 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -1,7 +1,7 @@ /* winsup.h: main Cygwin header file. Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -26,6 +26,11 @@ details. */ #define _NO_W32_PSEUDO_MODIFIERS +/* Newlib's guarding functions more diligently based on their origin, starting + since 2013. To be sure to get everything and the kitchen sink, we have to + define _GNU_SOURCE. */ +#define _GNU_SOURCE 1 + #include #include diff --git a/winsup/doc/ChangeLog b/winsup/doc/ChangeLog index 521be48e7..4deed1e4f 100644 --- a/winsup/doc/ChangeLog +++ b/winsup/doc/ChangeLog @@ -1,3 +1,39 @@ +2014-11-05 Corinna Vinschen + + * new-features.xml (ov-new1.7.33): Document atexit. + +2014-10-28 Corinna Vinschen + + * cygwinenv.xml: Change default setting of dosfilewarning. + * new-features.xml (ov-new1.7.33): Document aforementioned change. + +2014-10-27 Corinna Vinschen + + * new-features.xml (ov-new1.7.33): Document empty $PATH handling. + +2014-10-22 Corinna Vinschen + + * posix.xml (std-gnu): Add ffsl, ffsll, quotactl. + (std-notes): Add restrictions of quotactl. + +2014-10-22 Yaakov Selkowitz + + * new-features.xml (ov-new1.7.33): Document stime. + * posix.xml (std-deprec): Add stime. + +2014-10-22 Corinna Vinschen + + * new-features.xml (ov-new1.7.33): s/Linux/glibc. + +2014-10-22 Corinna Vinschen + + * new-features.xml (ov-new1.7.33): Update to current state. + +2014-08-13 Corinna Vinschen + + * new-features.xml (ov-new1.7.33): Add new section. + (ov-new1.7.32): Reflect intermediate 1.7.32 release. + 2014-08-03 Yaakov Selkowitz * faq-what.xml (faq.what.who): Remove mention of retired setup diff --git a/winsup/doc/cygwinenv.xml b/winsup/doc/cygwinenv.xml index 5a3e999b2..6c3442f2c 100644 --- a/winsup/doc/cygwinenv.xml +++ b/winsup/doc/cygwinenv.xml @@ -28,7 +28,7 @@ down every thread and socket creation! (no)dosfilewarning - If set, Cygwin will warn the first time a user uses an "MS-DOS" style path name rather than a POSIX-style -path name. Defaults to set. +path name. Defaults to off. diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index ebf4a176f..fdee13f59 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -2,7 +2,75 @@ -What's new and what changed in Cygwin 1.7 +What's new and what changed in Cygwin + +What's new and what changed in 1.7.33 + + + + +/proc/cygdrive is a new symlink pointing to the current cygdrive prefix. +This can be utilized in scripts to access paths via cygdrive prefix, +even if the cygdrive prefix has been changed by the user. + + + +/proc/partitions now prints the windows mount points the device is +mounted on. This allows to recognize the underlying Windows devices of +the Cygwin raw device names. + + + +New API: quotactl, designed after the Linux/BSD function, but severely +restricted: Windows only supports user block quotas on NTFS, no group +quotas, no inode quotas, no time constraints. + + + +New APIs: ffsl, ffsll (glibc extensions). + + + +New API: stime (SVr4). + + + +Provide Cygwin documentation (PDFs and HTML) for offline usage in +/usr/share/doc/cygwin-${version}. + + + +New internal exception handling based on SEH on 64 bit Cygwin. + + + +When exec'ing applications, check if $PATH exists and is non-empty. If +not, add PATH variable with Cygwin installation directory as content to +Windows environment to allow loading of Cygwin system DLLs. + + + +Disable CYGWIN "dosfilewarning" option by default. + + + +Improve various header files for C++- and standards-compliance. + + + +Doug Lea malloc implementation update from 2.8.3 to the latest 2.8.6. + + + +atexit(3) is now exported as statically linked function from libcygwin.a. +This allows reliable access to the DSO handle of the caller for newly +built executables. The former atexit entry point into the DLL remains +for backward compatibility only. + + + + + What's new and what changed in 1.7.32 @@ -26,7 +94,7 @@ Support more recent CPU flags in /proc/cpuinfo. -What's new and what changed from 1.7.30 to 1.7.31 +What's new and what changed in 1.7.31 @@ -53,7 +121,7 @@ as on Linux. -What's new and what changed from 1.7.28 to 1.7.29 +What's new and what changed in 1.7.29 @@ -70,7 +138,7 @@ Console screen clearing works more like xterm or mintty. -What's new and what changed from 1.7.27 to 1.7.28 +What's new and what changed in 1.7.28 @@ -93,7 +161,7 @@ if it only has been read from. -What's new and what changed from 1.7.26 to 1.7.27 +What's new and what changed in 1.7.27 @@ -108,7 +176,7 @@ points to a native NTFS symlink with a target path prefixed with "\\?\". -What's new and what changed from 1.7.25 to 1.7.26 +What's new and what changed in 1.7.26 @@ -148,7 +216,7 @@ New associated header /usr/include/spawn.h. -What's new and what changed from 1.7.24 to 1.7.25 +What's new and what changed in 1.7.25 @@ -166,7 +234,7 @@ partitions on a tape. -What's new and what changed from 1.7.23 to 1.7.24 +What's new and what changed in 1.7.24 @@ -178,7 +246,7 @@ Allow application override of posix_memalign. -What's new and what changed from 1.7.22 to 1.7.23 +What's new and what changed in 1.7.23 @@ -191,7 +259,7 @@ send the full windows command line to any subprocesses. -What's new and what changed from 1.7.21 to 1.7.22 +What's new and what changed in 1.7.22 @@ -215,7 +283,7 @@ containing arbitrary byte values as GLibc's regcomp. -What's new and what changed from 1.7.20 to 1.7.21 +What's new and what changed in 1.7.21 @@ -227,7 +295,7 @@ New API: rawmemchr. -What's new and what changed from 1.7.18 to 1.7.19 +What's new and what changed in 1.7.19 @@ -263,7 +331,7 @@ arc4random_buf, arc4random_stir, arc4random_uniform. -What's new and what changed from 1.7.17 to 1.7.18 +What's new and what changed in 1.7.18 @@ -299,7 +367,7 @@ New API: cfsetspeed. -What's new and what changed from 1.7.16 to 1.7.17 +What's new and what changed in 1.7.17 @@ -317,7 +385,7 @@ allows to open the file with the O_EXCL flag set. -What's new and what changed from 1.7.15 to 1.7.16 +What's new and what changed in 1.7.16 @@ -333,7 +401,7 @@ Recognize ReFS filesystem. -What's new and what changed from 1.7.14 to 1.7.15 +What's new and what changed in 1.7.15 @@ -345,7 +413,7 @@ CYGWIN=pipe_byte option now forces the opening of pipes in byte mode rather than -What's new and what changed from 1.7.13 to 1.7.14 +What's new and what changed in 1.7.14 @@ -357,7 +425,7 @@ Add mouse reporting modes 1005, 1006 and 1015 to console window. -What's new and what changed from 1.7.12 to 1.7.13 +What's new and what changed in 1.7.13 @@ -375,7 +443,7 @@ is now properly flushed. -What's new and what changed from 1.7.11 to 1.7.12 +What's new and what changed in 1.7.12 @@ -410,7 +478,7 @@ finding potential BLODAs. -What's new and what changed from 1.7.10 to 1.7.11 +What's new and what changed in 1.7.11 @@ -437,7 +505,7 @@ changed using the pthread_attr_setstacksize call. -What's new and what changed from 1.7.9 to 1.7.10 +What's new and what changed in 1.7.10 @@ -561,7 +629,7 @@ pthread_sigqueue, sysinfo. -What's new and what changed from 1.7.8 to 1.7.9 +What's new and what changed in 1.7.9 @@ -573,7 +641,7 @@ New API: strchrnul. -What's new and what changed from 1.7.7 to 1.7.8 +What's new and what changed in 1.7.8 @@ -641,7 +709,7 @@ Support TIOCGPGRP, TIOCSPGRP ioctls. -What's new and what changed from 1.7.6 to 1.7.7 +What's new and what changed in 1.7.7 @@ -668,7 +736,7 @@ See for details. -What's new and what changed from 1.7.5 to 1.7.6 +What's new and what changed in 1.7.6 @@ -725,7 +793,7 @@ be out of the way. [...] -What's new and what changed from 1.7.3 to 1.7.5 +What's new and what changed in 1.7.5 @@ -738,7 +806,7 @@ in Windows console. -What's new and what changed from 1.7.2 to 1.7.3 +What's new and what changed in 1.7.3 @@ -756,7 +824,7 @@ Modification and access timestamps of devices reflect the current time. -What's new and what changed from 1.7.1 to 1.7.2 +What's new and what changed in 1.7.2 @@ -908,7 +976,9 @@ Procedure Call (RPC) and NFS. -OS related changes +What's new and what changed from 1.5 to 1.7 + +OS related changes @@ -923,9 +993,9 @@ Add support for Windows 7 and Windows Server 2008 R2. - + -File Access related changes +File Access related changes @@ -1111,9 +1181,9 @@ eaccess, euidaccess, canonicalize_file_name, fexecve, execvpe. - + -Network related changes +Network related changes @@ -1179,9 +1249,9 @@ Add /proc/net/if_inet6. - + -Device related changes +Device related changes @@ -1230,9 +1300,9 @@ get_phys_pages, posix_openpt. - + -Other POSIX related changes +Other POSIX related changes @@ -1294,9 +1364,9 @@ vfwscanf, vswscanf. - + -Security related changes +Security related changes @@ -1327,9 +1397,9 @@ options have a slightly changed behaviour. - + -Miscellaneous +Miscellaneous @@ -1424,6 +1494,8 @@ having multiple concurrent Cygwin installations. + + diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog index f61161c17..8559de18d 100644 --- a/winsup/utils/ChangeLog +++ b/winsup/utils/ChangeLog @@ -1,3 +1,9 @@ +2014-10-21 Corinna Vinschen + + * cygcheck.cc (CYGLSA64_DLL): Remove unused macro. + (dump_sysinfo): If COMSPEC isn't set in the MSVCRT environment, set it. + Explain why. + 2014-06-16 Corinna Vinschen * passwd.c (main): Fix typo in error output. diff --git a/winsup/utils/cygcheck.cc b/winsup/utils/cygcheck.cc index 465bc7861..cc400519e 100644 --- a/winsup/utils/cygcheck.cc +++ b/winsup/utils/cygcheck.cc @@ -1,7 +1,7 @@ /* cygcheck.cc Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. + 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. This file is part of Cygwin. @@ -587,9 +587,6 @@ cygwin_info (HANDLE h) return; } -/* Special case. Don't complain about this one. */ -#define CYGLSA64_DLL "\\cyglsa64.dll" - static void dll_info (const char *path, HANDLE fh, int lvl, int recurse) { @@ -1436,6 +1433,20 @@ dump_sysinfo () DWORD obcaseinsensitive = 1; HKEY key; + /* MSVCRT popen (called by pretty_id and dump_sysinfo_services) SEGVs if + COMSPEC isn't set correctly. Simply enforce it here. Using + Get/SetEnvironmentVariable to set the dir does *not* help, btw. + Apparently MSVCRT keeps its own copy of the environment and changing + that requires to use _wputenv. */ + if (!_wgetenv (L"COMSPEC")) + { + WCHAR comspec[MAX_PATH + 17]; + wcscpy (comspec, L"COMSPEC="); + GetSystemDirectoryW (comspec + 8, MAX_PATH); + wcsncat (comspec, L"\\cmd.exe", sizeof comspec); + _wputenv (comspec); + } + printf ("\nCygwin Configuration Diagnostics\n"); time (&now); printf ("Current System Time: %s\n", ctime (&now));