From 9c154abedb4cbf713808e2fbe625f6d80939dcc3 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sat, 9 Oct 2010 10:54:13 +0000 Subject: [PATCH] * cygheap.h (cwdstuff::override_win32_cwd): Declare. * ntdll.h (struct _PEB): Add members accessed by the fast cwd method starting with Vista. (struct _KUSER_SHARED_DATA): Define with only the DismountCount. (RtlAllocateHeap): Declare. (RtlEnterCriticalSection): Declare. (RtlFreeHeap): Declare. (RtlLeaveCriticalSection): Declare. * path.cc (get_user_proc_parms): Remove. (struct _FAST_CWD): New structure. (fast_cwd_ptr): Define. (SharedUserData): Define. (peek32): Define. (find_fast_cwd_pointers): New function to find the global pointer to the current FAST_CWD structure. (copy_cwd_str): New helper function. (cwdstuff::override_win32_cwd): New method to set the Win32 CWD. (cwdstuff::init): Just call override_win32_cwd from here when started from native Win32 parent. (cwdstuff::set): Access Win32 CWD via PEB reference instead of using get_user_proc_parms function. Memorize old DismountCount before opening directory handle. Call override_win32_cwd to set up Win32 CWD. Be more verbose in comments. * wincap.h (wincaps::has_fast_cwd): New element. * wincap.cc: Implement has_fast_cwd element throughout. --- winsup/cygwin/ChangeLog | 29 ++++ winsup/cygwin/cygheap.h | 2 + winsup/cygwin/ntdll.h | 18 ++- winsup/cygwin/path.cc | 316 +++++++++++++++++++++++++++++++++++++--- winsup/cygwin/wincap.cc | 9 ++ winsup/cygwin/wincap.h | 2 + 6 files changed, 354 insertions(+), 22 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 5f56837e3..3d803dd69 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,32 @@ +2010-09-08 John Carey + Corinna Vinschen + + * cygheap.h (cwdstuff::override_win32_cwd): Declare. + * ntdll.h (struct _PEB): Add members accessed by the fast cwd method + starting with Vista. + (struct _KUSER_SHARED_DATA): Define with only the DismountCount. + (RtlAllocateHeap): Declare. + (RtlEnterCriticalSection): Declare. + (RtlFreeHeap): Declare. + (RtlLeaveCriticalSection): Declare. + * path.cc (get_user_proc_parms): Remove. + (struct _FAST_CWD): New structure. + (fast_cwd_ptr): Define. + (SharedUserData): Define. + (peek32): Define. + (find_fast_cwd_pointers): New function to find the global pointer + to the current FAST_CWD structure. + (copy_cwd_str): New helper function. + (cwdstuff::override_win32_cwd): New method to set the Win32 CWD. + (cwdstuff::init): Just call override_win32_cwd from here when + started from native Win32 parent. + (cwdstuff::set): Access Win32 CWD via PEB reference instead of using + get_user_proc_parms function. Memorize old DismountCount before + opening directory handle. Call override_win32_cwd to set up Win32 CWD. + Be more verbose in comments. + * wincap.h (wincaps::has_fast_cwd): New element. + * wincap.cc: Implement has_fast_cwd element throughout. + 2010-09-08 Marco Atzeri Corinna Vinschen diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 46e3c711b..1096175fa 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -217,6 +217,8 @@ private: a native Win32 application. See cwdstuff::set for how it gets set. See spawn_guts for how it's evaluated. */ + void override_win32_cwd (bool, ULONG); + public: UNICODE_STRING win32; static muto cwd_lock; diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 41c84a2ce..54c56f027 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -584,7 +584,10 @@ typedef struct _PEB BYTE Reserved2[9]; PVOID LoaderData; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; - BYTE Reserved3[448]; + BYTE Reserved3[4]; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + BYTE Reserved4[436]; ULONG SessionId; } PEB, *PPEB; @@ -596,6 +599,13 @@ typedef struct _TEB /* A lot more follows... */ } TEB, *PTEB; +typedef struct _KUSER_SHARED_DATA +{ + BYTE Reserved1[0x2dc]; + ULONG DismountCount; + /* A lot more follows... */ +} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; + typedef struct _PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; @@ -979,10 +989,11 @@ extern "C" NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG); + VOID NTAPI RtlAcquirePebLock (); + PVOID NTAPI RtlAllocateHeap (PVOID, ULONG, SIZE_T); NTSTATUS NTAPI RtlAppendUnicodeToString (PUNICODE_STRING, PCWSTR); NTSTATUS NTAPI RtlAppendUnicodeStringToString (PUNICODE_STRING, PUNICODE_STRING); - VOID NTAPI RtlAcquirePebLock (); NTSTATUS NTAPI RtlAnsiStringToUnicodeString (PUNICODE_STRING, PANSI_STRING, BOOLEAN); LONG NTAPI RtlCompareUnicodeString (PUNICODE_STRING, PUNICODE_STRING, @@ -992,9 +1003,11 @@ extern "C" BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz (PUNICODE_STRING, PCSTR); NTSTATUS NTAPI RtlDowncaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING, BOOLEAN); + NTSTATUS NTAPI RtlEnterCriticalSection (PRTL_CRITICAL_SECTION); BOOLEAN NTAPI RtlEqualUnicodeString (PUNICODE_STRING, PUNICODE_STRING, BOOLEAN); VOID NTAPI RtlFreeAnsiString (PANSI_STRING); + BOOLEAN NTAPI RtlFreeHeap (PVOID, ULONG, PVOID); VOID NTAPI RtlFreeOemString (POEM_STRING); VOID NTAPI RtlFreeUnicodeString (PUNICODE_STRING); HANDLE NTAPI RtlGetCurrentTransaction (); @@ -1002,6 +1015,7 @@ extern "C" VOID NTAPI RtlInitUnicodeString (PUNICODE_STRING, PCWSTR); NTSTATUS NTAPI RtlIntegerToUnicodeString (ULONG, ULONG, PUNICODE_STRING); ULONG NTAPI RtlIsDosDeviceName_U (PCWSTR); + NTSTATUS NTAPI RtlLeaveCriticalSection (PRTL_CRITICAL_SECTION); ULONG NTAPI RtlNtStatusToDosError (NTSTATUS); NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING, BOOLEAN); diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 02ec2b398..468570166 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3353,10 +3353,282 @@ cygwin_split_path (const char *path, char *dir, char *file) /*****************************************************************************/ -static inline PRTL_USER_PROCESS_PARAMETERS -get_user_proc_parms () +/* The find_fast_cwd_pointers function and parts of the + cwdstuff::override_win32_cwd method are based on code using the + following license: + + Copyright 2010 John Carey. All rights reserved. + + 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. + + THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``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 JOHN CAREY 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. */ + +/* This structure is used to store the CWD starting with Windows Vista. + The CWD storage in the RTL_USER_PROCESS_PARAMETERS block is only + an afterthought now. The actual CWD storage is a FAST_CWD structure + which is allocated on the process heap. The new method only requires + minimal locking and it's much more multi-thread friendly. Presumably + it minimizes contention when accessing the CWD. */ +typedef struct _FAST_CWD { + LONG ReferenceCount; /* Only release when this is 0. */ + HANDLE DirectoryHandle; + ULONG OldDismountCount; /* Reflects the system DismountCount + at the time the CWD has been set. */ + UNICODE_STRING Path; /* Path's Buffer member always refers + to the following Buffer array. */ + WCHAR Buffer[MAX_PATH]; +} FAST_CWD, *PFAST_CWD; + +/* fast_cwd_ptr is a pointer to the global pointer in ntdll.dll pointing + to the FAST_CWD structure which constitutes the CWD. + + We put the pointer into the common shared DLL segment. This allows to + restrict the call to find_fast_cwd_pointers() to once per Cygwin session + per user session. This works, because ASLR randomizes the load address + of DLLs only once at boot time. */ +static PFAST_CWD *fast_cwd_ptr + __attribute__((section (".cygwin_dll_common"), shared)) = (PFAST_CWD *) -1; + +/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit + user address space. We need it here to access the current DismountCount. */ +static KUSER_SHARED_DATA &SharedUserData + = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000; + +#define peek32(x) (*(uint32_t *)(x)) + +/* This function scans the code in ntdll.dll to find the address of the + global variable used to access the CWD starting with Vista. While the + pointer is global, it's not exported from the DLL, unfortunately. + Therefore we have to use some knowledge to figure out the address. + + This code has been tested on Vista 32/64 bit, Server 2008 32/64 bit, + Windows 7 32/64 bit, and Server 2008 R2 (which is only 64 bit anyway). + There's some hope that this will still work for Windows 8... */ +static void +find_fast_cwd_pointers () { - return NtCurrentTeb ()->Peb->ProcessParameters; + /* Note that we have been called. */ + fast_cwd_ptr = NULL; + /* Fetch entry points of relevant functions in ntdll.dll. */ + HMODULE ntdll = GetModuleHandle ("ntdll.dll"); + if (!ntdll) + return; + const uint8_t *get_dir = (const uint8_t *) + GetProcAddress (ntdll, "RtlGetCurrentDirectory_U"); + const uint8_t *ent_crit = (const uint8_t *) + GetProcAddress (ntdll, "RtlEnterCriticalSection"); + if (!get_dir || !ent_crit) + return; + /* Search first relative call instruction in RtlGetCurrentDirectory_U. */ + const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 32); + if (!rcall) + return; + /* Fetch offset from instruction and compute address of called function. + This function actually fetches the current FAST_CWD instance and + performs some other actions, not important to us. */ + ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1); + const uint8_t *use_cwd = rcall + 5 + offset; + /* Find first "push edi" instruction. */ + const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32); + /* ...which should be followed by "mov edi, crit-sect-addr" then + "push edi". */ + const uint8_t *movedi = pushedi + 1; + if (movedi[0] != 0xbf || movedi[5] != 0x57) + return; + /* Compare the address used for the critical section with the known + PEB lock as stored in the PEB. */ + if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1) + != NtCurrentTeb ()->Peb->FastPebLock) + return; + /* To check we are seeing the right code, we check our expectation that + the next instruction is a relative call into RtlEnterCriticalSection. */ + rcall = movedi + 6; + if (rcall[0] != 0xe8) + return; + /* Check that this is a relative call to RtlEnterCriticalSection. */ + offset = (ptrdiff_t) peek32 (rcall + 1); + if (rcall + 5 + offset != ent_crit) + return; + /* After locking the critical section, the code should read the global + PFAST_CWD * pointer that is guarded by that critical section. */ + const uint8_t *movesi = rcall + 5; + if (movesi[0] != 0x8b) + return; + fast_cwd_ptr = (PFAST_CWD *) peek32 (movesi + 2); +#ifdef DEBUGGING + system_printf ("fast_cwd_ptr: %p", fast_cwd_ptr); +#endif +} + +static inline void +copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src) +{ + RtlCopyUnicodeString (tgt, src); + if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\') + { + tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\'; + tgt->Length += sizeof (WCHAR); + } +} + +void +cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count) +{ + NTSTATUS status; + HANDLE h = NULL; + + PEB &peb = *NtCurrentTeb ()->Peb; + UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName; + HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle; + + if (wincap.has_fast_cwd ()) + { + if (fast_cwd_ptr == (PFAST_CWD *) -1) + { + find_fast_cwd_pointers (); + if (!fast_cwd_ptr) + system_printf ("WARNING: Couldn't compute FAST_CWD pointer. " + "Please report this problem to\nthe public mailing " + "list cygwin@cygwin.com"); + } + if (fast_cwd_ptr) + { + /* Default method starting with Vista. If we got a valid value for + fast_cwd_ptr, we can simply replace the RtlSetCurrentDirectory_U + function entirely, just as on pre-Vista. */ + PVOID heap = peb.ProcessHeap; + /* First allocate a new FAST_CWD strcuture on the heap. */ + PFAST_CWD f_cwd = (PFAST_CWD) + RtlAllocateHeap (heap, 0, sizeof (FAST_CWD)); + if (!f_cwd) + { + debug_printf ("RtlAllocateHeap failed"); + return; + } + /* Fill in the values. */ + f_cwd->ReferenceCount = 1; + f_cwd->DirectoryHandle = dir; + f_cwd->OldDismountCount = old_dismount_count; + RtlInitEmptyUnicodeString (&f_cwd->Path, f_cwd->Buffer, + MAX_PATH * sizeof (WCHAR)); + copy_cwd_str (&f_cwd->Path, error ? &ro_u_pipedir : &win32); + /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD + structure and writing the CWD to the user process parameter + block. This is equivalent to calling RtlAcquirePebLock/ + RtlReleasePebLock, but without having to go through the FS + selector again. */ + RtlEnterCriticalSection (peb.FastPebLock); + PFAST_CWD old_cwd = *fast_cwd_ptr; + *fast_cwd_ptr = f_cwd; + upp_cwd_str = f_cwd->Path; + upp_cwd_hdl = dir; + RtlLeaveCriticalSection (peb.FastPebLock); + /* Decrement the reference count. If it's down to 0, free structure + from heap. */ + if (old_cwd && InterlockedDecrement (&old_cwd->ReferenceCount) == 0) + { + /* In contrast to pre-Vista, the handle on init is always a fresh + one and not the handle inherited from the parent process. So + we always have to close it here. However, the handle could + be NULL, if we cd'ed into a virtual dir. */ + if (old_cwd->DirectoryHandle) + NtClose (old_cwd->DirectoryHandle); + RtlFreeHeap (heap, 0, old_cwd); + } + } + else + { + /* This is more a hack, and it's only used on Vista and later if we + failed to find the fast_cwd_ptr value. What we do here is to call + RtlSetCurrentDirectory_U and let it set up a new FAST_CWD + structure. Afterwards, compute the address of that structure + utilizing the fact that the buffer address in the user process + parameter block is actually pointing to the buffer in that + FAST_CWD structure. Then replace the directory handle in that + structure with our own handle and close the original one. + + Note that the call to RtlSetCurrentDirectory_U also closes our + old dir handle, so there won't be any handle left open. + + This method is prone to two race conditions: + + - Due to the way RtlSetCurrentDirectory_U opens the directory + handle, the directory is locked against deletion or renaming + between the RtlSetCurrentDirectory_U and the subsequent NtClose + call. + + - When another thread calls SetCurrentDirectory at exactly the + same time, a crash might occur, or worse, unrelated data could + be overwritten or NtClose could be called on an unrelated handle. + + Therefore, use this *only* as a fallback. */ + if (!init) + { + status = RtlSetCurrentDirectory_U (error ? &ro_u_pipedir + : &win32); + if (!NT_SUCCESS (status)) + { + debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p", + error ? &ro_u_pipedir : &win32, status); + return; + } + } + RtlEnterCriticalSection (peb.FastPebLock); + PFAST_CWD f_cwd = (PFAST_CWD) + ((PBYTE) upp_cwd_str.Buffer + - __builtin_offsetof (struct _FAST_CWD, Buffer)); + h = upp_cwd_hdl; + f_cwd->DirectoryHandle = upp_cwd_hdl = dir; + RtlLeaveCriticalSection (peb.FastPebLock); + /* In contrast to pre-Vista, the handle on init is always a fresh one + and not the handle inherited from the parent process. So we always + have to close it here. */ + NtClose (h); + } + } + else + { + /* This method is used for all pre-Vista OSes. We simply set the values + for the CWD in the user process parameter block entirely by ourselves + under PEB lock condition. This is how RtlSetCurrentDirectory_U worked + in these older OSes, so we're safe. + + Note that we can't just RtlEnterCriticalSection (peb.FastPebLock) + on pre-Vista. RtlAcquirePebLock was way more complicated back then. */ + RtlAcquirePebLock (); + if (!init) + copy_cwd_str (&upp_cwd_str, error ? &ro_u_pipedir : &win32); + h = upp_cwd_hdl; + upp_cwd_hdl = dir; + RtlReleasePebLock (); + /* Only on init, the handle is potentially a native handle. However, + if it's identical to dir, it's the inherited handle from a Cygwin + parent process and must not be closed. */ + if (h && h != dir) + NtClose (h); + } } /* Initialize cygcwd 'muto' for serializing access to cwd info. */ @@ -3366,12 +3638,13 @@ cwdstuff::init () cwd_lock.init ("cwd_lock"); /* Cygwin processes inherit the cwd from their parent. If the win32 path - buffer is not NULL, the cwd struct is already set up. */ + buffer is not NULL, the cwd struct is already set up, and we only + have to override the Win32 CWD with ours. */ if (win32.Buffer) - return; - - /* Initially re-open the cwd to allow POSIX semantics. */ - set (NULL, NULL); + override_win32_cwd (true, SharedUserData.DismountCount); + else + /* Initially re-open the cwd to allow POSIX semantics. */ + set (NULL, NULL); } /* Chdir and fill out the elements of a cwdstuff struct. */ @@ -3380,6 +3653,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) { NTSTATUS status; UNICODE_STRING upath; + PEB &peb = *NtCurrentTeb ()->Peb; bool virtual_path = false; bool unc_path = false; bool inaccessible_path = false; @@ -3430,6 +3704,13 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) virtual_path = true; } + /* Memorize old DismountCount before opening the dir. This value is + stored in the FAST_CWD structure on Vista and later. It would be + simpler to fetch the old DismountCount in override_win32_cwd, but + Windows also fetches it before opening the directory handle. It's + not quite clear if that's really required, but since we don't know + the side effects of this action, we better follow Windows' lead. */ + ULONG old_dismount_count = SharedUserData.DismountCount; /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all sharing flags set. The handle is right now used in exceptions.cc only, but that might change in future. */ @@ -3447,7 +3728,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) RtlInitUnicodeString (&upath, L""); InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT, - get_user_proc_parms ()->CurrentDirectoryHandle, NULL); + peb.ProcessParameters->CurrentDirectoryHandle, NULL); } else InitializeObjectAttributes (&attr, &upath, @@ -3477,9 +3758,10 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) return -1; } } - /* Set new handle. It's only used when creating stackdumps so far. */ - if (dir) - NtClose (dir); + /* Set new handle. Note that we simply overwrite the old handle here + without closing it. The handle is also used as Win32 CWD handle in + the user parameter block, and it will be closed in override_win32_cwd, + if required. */ dir = h; if (!nat_cwd) @@ -3487,7 +3769,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) /* On init, just fetch the Win32 dir from the PEB. We can access the PEB without lock, because no other thread can change the CWD at that time. */ - PUNICODE_STRING pdir = &get_user_proc_parms ()->CurrentDirectoryName; + PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName; RtlInitEmptyUnicodeString (&win32, (PWCHAR) crealloc_abort (win32.Buffer, pdir->Length @@ -3568,13 +3850,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd) } /* Keep the Win32 CWD in sync. Don't check for error, other than for strace output. Try to keep overhead low. */ - if (nat_cwd) - { - status = RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32); - if (!NT_SUCCESS (status)) - debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p", - error ? &ro_u_pipedir : &win32, status); - } + override_win32_cwd (!nat_cwd, old_dismount_count); /* Eventually, create POSIX path if it's not set on entry. */ tmp_pathbuf tp; diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 2cf562c57..7f1688516 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -60,6 +60,7 @@ wincaps wincap_nt4sp4 __attribute__((section (".cygwin_dll_common"), shared)) = has_localenames:false, has_mwmo_inputavailable:false, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -99,6 +100,7 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -138,6 +140,7 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -177,6 +180,7 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -216,6 +220,7 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -255,6 +260,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -294,6 +300,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:false, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:false, }; wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -333,6 +340,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:true, has_mwmo_inputavailable:true, has_buggy_thread_startup:true, + has_fast_cwd:true, }; wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -372,6 +380,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { has_localenames:true, has_mwmo_inputavailable:true, has_buggy_thread_startup:false, + has_fast_cwd:true, }; wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 315d4d51e..decfc1e50 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -50,6 +50,7 @@ struct wincaps unsigned has_localenames : 1; unsigned has_mwmo_inputavailable : 1; unsigned has_buggy_thread_startup : 1; + unsigned has_fast_cwd : 1; }; class wincapc @@ -105,6 +106,7 @@ public: bool IMPLEMENT (has_localenames) bool IMPLEMENT (has_mwmo_inputavailable) bool IMPLEMENT (has_buggy_thread_startup) + bool IMPLEMENT (has_fast_cwd) #undef IMPLEMENT };