* 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.
This commit is contained in:
parent
d8c5f616ef
commit
9c154abedb
|
@ -1,3 +1,32 @@
|
||||||
|
2010-09-08 John Carey <aeolus@electric-cloud.com>
|
||||||
|
Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* 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 <marco_atzeri@yahoo.it>
|
2010-09-08 Marco Atzeri <marco_atzeri@yahoo.it>
|
||||||
Corinna Vinschen <corinna@vinschen.de>
|
Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
|
|
@ -217,6 +217,8 @@ private:
|
||||||
a native Win32 application. See cwdstuff::set for
|
a native Win32 application. See cwdstuff::set for
|
||||||
how it gets set. See spawn_guts for how it's
|
how it gets set. See spawn_guts for how it's
|
||||||
evaluated. */
|
evaluated. */
|
||||||
|
void override_win32_cwd (bool, ULONG);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UNICODE_STRING win32;
|
UNICODE_STRING win32;
|
||||||
static muto cwd_lock;
|
static muto cwd_lock;
|
||||||
|
|
|
@ -584,7 +584,10 @@ typedef struct _PEB
|
||||||
BYTE Reserved2[9];
|
BYTE Reserved2[9];
|
||||||
PVOID LoaderData;
|
PVOID LoaderData;
|
||||||
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
||||||
BYTE Reserved3[448];
|
BYTE Reserved3[4];
|
||||||
|
PVOID ProcessHeap;
|
||||||
|
PRTL_CRITICAL_SECTION FastPebLock;
|
||||||
|
BYTE Reserved4[436];
|
||||||
ULONG SessionId;
|
ULONG SessionId;
|
||||||
} PEB, *PPEB;
|
} PEB, *PPEB;
|
||||||
|
|
||||||
|
@ -596,6 +599,13 @@ typedef struct _TEB
|
||||||
/* A lot more follows... */
|
/* A lot more follows... */
|
||||||
} TEB, *PTEB;
|
} 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
|
typedef struct _PROCESS_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
NTSTATUS ExitStatus;
|
NTSTATUS ExitStatus;
|
||||||
|
@ -979,10 +989,11 @@ extern "C"
|
||||||
NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
|
NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
|
||||||
PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER,
|
PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER,
|
||||||
PULONG);
|
PULONG);
|
||||||
|
VOID NTAPI RtlAcquirePebLock ();
|
||||||
|
PVOID NTAPI RtlAllocateHeap (PVOID, ULONG, SIZE_T);
|
||||||
NTSTATUS NTAPI RtlAppendUnicodeToString (PUNICODE_STRING, PCWSTR);
|
NTSTATUS NTAPI RtlAppendUnicodeToString (PUNICODE_STRING, PCWSTR);
|
||||||
NTSTATUS NTAPI RtlAppendUnicodeStringToString (PUNICODE_STRING,
|
NTSTATUS NTAPI RtlAppendUnicodeStringToString (PUNICODE_STRING,
|
||||||
PUNICODE_STRING);
|
PUNICODE_STRING);
|
||||||
VOID NTAPI RtlAcquirePebLock ();
|
|
||||||
NTSTATUS NTAPI RtlAnsiStringToUnicodeString (PUNICODE_STRING, PANSI_STRING,
|
NTSTATUS NTAPI RtlAnsiStringToUnicodeString (PUNICODE_STRING, PANSI_STRING,
|
||||||
BOOLEAN);
|
BOOLEAN);
|
||||||
LONG NTAPI RtlCompareUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
LONG NTAPI RtlCompareUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
||||||
|
@ -992,9 +1003,11 @@ extern "C"
|
||||||
BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz (PUNICODE_STRING, PCSTR);
|
BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz (PUNICODE_STRING, PCSTR);
|
||||||
NTSTATUS NTAPI RtlDowncaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
NTSTATUS NTAPI RtlDowncaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
||||||
BOOLEAN);
|
BOOLEAN);
|
||||||
|
NTSTATUS NTAPI RtlEnterCriticalSection (PRTL_CRITICAL_SECTION);
|
||||||
BOOLEAN NTAPI RtlEqualUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
BOOLEAN NTAPI RtlEqualUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
|
||||||
BOOLEAN);
|
BOOLEAN);
|
||||||
VOID NTAPI RtlFreeAnsiString (PANSI_STRING);
|
VOID NTAPI RtlFreeAnsiString (PANSI_STRING);
|
||||||
|
BOOLEAN NTAPI RtlFreeHeap (PVOID, ULONG, PVOID);
|
||||||
VOID NTAPI RtlFreeOemString (POEM_STRING);
|
VOID NTAPI RtlFreeOemString (POEM_STRING);
|
||||||
VOID NTAPI RtlFreeUnicodeString (PUNICODE_STRING);
|
VOID NTAPI RtlFreeUnicodeString (PUNICODE_STRING);
|
||||||
HANDLE NTAPI RtlGetCurrentTransaction ();
|
HANDLE NTAPI RtlGetCurrentTransaction ();
|
||||||
|
@ -1002,6 +1015,7 @@ extern "C"
|
||||||
VOID NTAPI RtlInitUnicodeString (PUNICODE_STRING, PCWSTR);
|
VOID NTAPI RtlInitUnicodeString (PUNICODE_STRING, PCWSTR);
|
||||||
NTSTATUS NTAPI RtlIntegerToUnicodeString (ULONG, ULONG, PUNICODE_STRING);
|
NTSTATUS NTAPI RtlIntegerToUnicodeString (ULONG, ULONG, PUNICODE_STRING);
|
||||||
ULONG NTAPI RtlIsDosDeviceName_U (PCWSTR);
|
ULONG NTAPI RtlIsDosDeviceName_U (PCWSTR);
|
||||||
|
NTSTATUS NTAPI RtlLeaveCriticalSection (PRTL_CRITICAL_SECTION);
|
||||||
ULONG NTAPI RtlNtStatusToDosError (NTSTATUS);
|
ULONG NTAPI RtlNtStatusToDosError (NTSTATUS);
|
||||||
NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING,
|
NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING,
|
||||||
BOOLEAN);
|
BOOLEAN);
|
||||||
|
|
|
@ -3353,10 +3353,282 @@ cygwin_split_path (const char *path, char *dir, char *file)
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static inline PRTL_USER_PROCESS_PARAMETERS
|
/* The find_fast_cwd_pointers function and parts of the
|
||||||
get_user_proc_parms ()
|
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. */
|
/* Initialize cygcwd 'muto' for serializing access to cwd info. */
|
||||||
|
@ -3366,12 +3638,13 @@ cwdstuff::init ()
|
||||||
cwd_lock.init ("cwd_lock");
|
cwd_lock.init ("cwd_lock");
|
||||||
|
|
||||||
/* Cygwin processes inherit the cwd from their parent. If the win32 path
|
/* 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)
|
if (win32.Buffer)
|
||||||
return;
|
override_win32_cwd (true, SharedUserData.DismountCount);
|
||||||
|
else
|
||||||
/* Initially re-open the cwd to allow POSIX semantics. */
|
/* Initially re-open the cwd to allow POSIX semantics. */
|
||||||
set (NULL, NULL);
|
set (NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chdir and fill out the elements of a cwdstuff struct. */
|
/* 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;
|
NTSTATUS status;
|
||||||
UNICODE_STRING upath;
|
UNICODE_STRING upath;
|
||||||
|
PEB &peb = *NtCurrentTeb ()->Peb;
|
||||||
bool virtual_path = false;
|
bool virtual_path = false;
|
||||||
bool unc_path = false;
|
bool unc_path = false;
|
||||||
bool inaccessible_path = false;
|
bool inaccessible_path = false;
|
||||||
|
@ -3430,6 +3704,13 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
|
||||||
virtual_path = true;
|
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
|
/* 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,
|
sharing flags set. The handle is right now used in exceptions.cc only,
|
||||||
but that might change in future. */
|
but that might change in future. */
|
||||||
|
@ -3447,7 +3728,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
|
||||||
RtlInitUnicodeString (&upath, L"");
|
RtlInitUnicodeString (&upath, L"");
|
||||||
InitializeObjectAttributes (&attr, &upath,
|
InitializeObjectAttributes (&attr, &upath,
|
||||||
OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
|
OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
|
||||||
get_user_proc_parms ()->CurrentDirectoryHandle, NULL);
|
peb.ProcessParameters->CurrentDirectoryHandle, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
InitializeObjectAttributes (&attr, &upath,
|
InitializeObjectAttributes (&attr, &upath,
|
||||||
|
@ -3477,9 +3758,10 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Set new handle. It's only used when creating stackdumps so far. */
|
/* Set new handle. Note that we simply overwrite the old handle here
|
||||||
if (dir)
|
without closing it. The handle is also used as Win32 CWD handle in
|
||||||
NtClose (dir);
|
the user parameter block, and it will be closed in override_win32_cwd,
|
||||||
|
if required. */
|
||||||
dir = h;
|
dir = h;
|
||||||
|
|
||||||
if (!nat_cwd)
|
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
|
/* 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
|
the PEB without lock, because no other thread can change the CWD
|
||||||
at that time. */
|
at that time. */
|
||||||
PUNICODE_STRING pdir = &get_user_proc_parms ()->CurrentDirectoryName;
|
PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
|
||||||
RtlInitEmptyUnicodeString (&win32,
|
RtlInitEmptyUnicodeString (&win32,
|
||||||
(PWCHAR) crealloc_abort (win32.Buffer,
|
(PWCHAR) crealloc_abort (win32.Buffer,
|
||||||
pdir->Length
|
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
|
/* Keep the Win32 CWD in sync. Don't check for error, other than for
|
||||||
strace output. Try to keep overhead low. */
|
strace output. Try to keep overhead low. */
|
||||||
if (nat_cwd)
|
override_win32_cwd (!nat_cwd, old_dismount_count);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eventually, create POSIX path if it's not set on entry. */
|
/* Eventually, create POSIX path if it's not set on entry. */
|
||||||
tmp_pathbuf tp;
|
tmp_pathbuf tp;
|
||||||
|
|
|
@ -60,6 +60,7 @@ wincaps wincap_nt4sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
|
||||||
has_localenames:false,
|
has_localenames:false,
|
||||||
has_mwmo_inputavailable:false,
|
has_mwmo_inputavailable:false,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:false,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:true,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:true,
|
has_buggy_thread_startup:true,
|
||||||
|
has_fast_cwd:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
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_localenames:true,
|
||||||
has_mwmo_inputavailable:true,
|
has_mwmo_inputavailable:true,
|
||||||
has_buggy_thread_startup:false,
|
has_buggy_thread_startup:false,
|
||||||
|
has_fast_cwd:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
|
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct wincaps
|
||||||
unsigned has_localenames : 1;
|
unsigned has_localenames : 1;
|
||||||
unsigned has_mwmo_inputavailable : 1;
|
unsigned has_mwmo_inputavailable : 1;
|
||||||
unsigned has_buggy_thread_startup : 1;
|
unsigned has_buggy_thread_startup : 1;
|
||||||
|
unsigned has_fast_cwd : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class wincapc
|
class wincapc
|
||||||
|
@ -105,6 +106,7 @@ public:
|
||||||
bool IMPLEMENT (has_localenames)
|
bool IMPLEMENT (has_localenames)
|
||||||
bool IMPLEMENT (has_mwmo_inputavailable)
|
bool IMPLEMENT (has_mwmo_inputavailable)
|
||||||
bool IMPLEMENT (has_buggy_thread_startup)
|
bool IMPLEMENT (has_buggy_thread_startup)
|
||||||
|
bool IMPLEMENT (has_fast_cwd)
|
||||||
|
|
||||||
#undef IMPLEMENT
|
#undef IMPLEMENT
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue