From a3904c655efec2edc27f72b68280afb4a144cc9a Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 5 Mar 2012 11:50:25 +0000 Subject: [PATCH] * cygheap.h (enum fcwd_version_t): Move here from path.cc. (class fcwd_access_t): Ditto. Only declare methods. (class cwdstuff): Move fast_cwd_ptr and fast_cwd_version from shared DLL section here. * path.cc: Keep fcwd_access_t method definitions. (fcwd_access_t::fast_cwd_version): New method. (find_fast_cwd_pointer): Change comment. Mention test on W8CP. (cwdstuff::init): Initialize fast_cwd_ptr and fast_cwd_version. --- winsup/cygwin/ChangeLog | 11 ++ winsup/cygwin/cygheap.h | 92 +++++++++++- winsup/cygwin/path.cc | 319 ++++++++++++++++------------------------ 3 files changed, 228 insertions(+), 194 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 36347c950..b4ae19ced 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,14 @@ +2012-03-05 Corinna Vinschen + + * cygheap.h (enum fcwd_version_t): Move here from path.cc. + (class fcwd_access_t): Ditto. Only declare methods. + (class cwdstuff): Move fast_cwd_ptr and fast_cwd_version from shared + DLL section here. + * path.cc: Keep fcwd_access_t method definitions. + (fcwd_access_t::fast_cwd_version): New method. + (find_fast_cwd_pointer): Change comment. Mention test on W8CP. + (cwdstuff::init): Initialize fast_cwd_ptr and fast_cwd_version. + 2012-03-05 Corinna Vinschen * dll_init.cc (dll_list::operator[]): Extend comment a bit more to diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 054297bd7..154ad6a84 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -204,7 +204,87 @@ public: /* cwd cache stuff. */ -class muto; +enum fcwd_version_t { + FCWD_OLD, + FCWD_W7, + FCWD_W8 +}; + +/* This class 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. + The class fcwd_access_t is supposed to encapsulate the gory implementation + details depending on OS version from the calling functions. */ +class fcwd_access_t { + /* This is the layout used in Windows 8 developer preview. */ + struct FAST_CWD_8 { + 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. */ + LONG FSCharacteristics; /* Taken from FileFsDeviceInformation */ + WCHAR Buffer[MAX_PATH]; + }; + /* This is the layout used in Windows 7 and Vista. */ + struct FAST_CWD_7 { + UNICODE_STRING Path; /* Path's Buffer member always refers + to the following Buffer array. */ + HANDLE DirectoryHandle; + LONG FSCharacteristics; /* Taken from FileFsDeviceInformation */ + LONG ReferenceCount; /* Only release when this is 0. */ + ULONG OldDismountCount; /* Reflects the system DismountCount + at the time the CWD has been set. */ + WCHAR Buffer[MAX_PATH]; + }; + /* This is the old FAST_CWD structure up to the patch from KB 2393802, + release in February 2011. */ + struct FAST_CWD_OLD { + 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]; + }; + union { + FAST_CWD_OLD fold; + FAST_CWD_7 f7; + FAST_CWD_8 f8; + }; + +#define IMPLEMENT(type, name) \ + type name () { \ + switch (fast_cwd_version ()) { \ + case FCWD_OLD: \ + default: \ + return fold.name; \ + case FCWD_W7: \ + return f7.name; \ + case FCWD_W8: \ + return f8.name; \ + } \ + } + IMPLEMENT (LONG &, ReferenceCount) + IMPLEMENT (HANDLE &, DirectoryHandle) + IMPLEMENT (ULONG &, OldDismountCount) + IMPLEMENT (UNICODE_STRING &, Path) + IMPLEMENT (WCHAR *, Buffer) + void SetFSCharacteristics (LONG val); + static fcwd_version_t &fast_cwd_version (void); + +public: + void CopyPath (UNICODE_STRING &target); + void Free (PVOID heap); + void FillIn (HANDLE dir, PUNICODE_STRING name, ULONG old_dismount_count); + static void SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir); + static void SetVersionFromPointer (PBYTE buf_p, bool is_buffer); +}; class cwdstuff { @@ -217,6 +297,16 @@ private: a native Win32 application. See cwdstuff::set for how it gets set. See child_info_spawn::worker for how it's evaluated. */ + + friend class fcwd_access_t; + /* fast_cwd_ptr is a pointer to the global RtlpCurDirRef pointer in + ntdll.dll pointing to the FAST_CWD structure which constitutes the CWD. + Unfortunately RtlpCurDirRef is not exported from ntdll.dll. */ + fcwd_access_t **fast_cwd_ptr; + /* Type of FAST_CWD used on this system. Keeping this information + available in shared memory avoids to test for the version every time + around. Default to new version. */ + fcwd_version_t fast_cwd_version; void override_win32_cwd (bool, ULONG); public: diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 4bc47e100..aa0521d4f 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3510,205 +3510,132 @@ copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src) USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* This class 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. - The class fcwd_access_t is supposed to encapsulate the gory implementation - details depending on OS version from the calling functions. */ -class fcwd_access_t { - /* This is the layout used in Windows 8 developer preview. */ - struct FAST_CWD_8 { - 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. */ - LONG FSCharacteristics; /* Taken from FileFsDeviceInformation */ - WCHAR Buffer[MAX_PATH]; - }; - /* This is the layout used in Windows 7 and Vista. */ - struct FAST_CWD_7 { - UNICODE_STRING Path; /* Path's Buffer member always refers - to the following Buffer array. */ - HANDLE DirectoryHandle; - LONG FSCharacteristics; /* Taken from FileFsDeviceInformation */ - LONG ReferenceCount; /* Only release when this is 0. */ - ULONG OldDismountCount; /* Reflects the system DismountCount - at the time the CWD has been set. */ - WCHAR Buffer[MAX_PATH]; - }; - /* This is the old FAST_CWD structure up to the patch from KB 2393802, - release in February 2011. */ - struct FAST_CWD_OLD { - 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]; - }; - union { - FAST_CWD_OLD fold; - FAST_CWD_7 f7; - FAST_CWD_8 f8; - }; - - /* Type of FAST_CWD used on this system. Keeping this information available - in shared memory avoids to test for the version every time around. - Default to new version. */ - enum fcwd_version_t { - FCWD_OLD, - FCWD_W7, - FCWD_W8 - }; - static fcwd_version_t fast_cwd_version; - -#define IMPLEMENT(type, name) \ - type name () { \ - switch (fast_cwd_version) { \ - case FCWD_OLD: \ - default: \ - return fold.name; \ - case FCWD_W7: \ - return f7.name; \ - case FCWD_W8: \ - return f8.name; \ - } \ - } - IMPLEMENT (LONG &, ReferenceCount) - IMPLEMENT (HANDLE &, DirectoryHandle) - IMPLEMENT (ULONG &, OldDismountCount) - IMPLEMENT (UNICODE_STRING &, Path) - IMPLEMENT (WCHAR *, Buffer) +void +fcwd_access_t::SetFSCharacteristics (LONG val) +{ /* Special case FSCharacteristics. Didn't exist originally. */ - void SetFSCharacteristics (LONG val) + switch (fast_cwd_version ()) { - switch (fast_cwd_version) - { - case FCWD_OLD: - break; - case FCWD_W7: - f7.FSCharacteristics = val; - break; - case FCWD_W8: - f8.FSCharacteristics = val; - break; - } - } -public: - void CopyPath (UNICODE_STRING &target) - { - /* Copy the Path contents over into the UNICODE_STRING referenced by - target. This is used to set the CurrentDirectoryName in the - user parameter block. */ - target = Path (); - } - void Free (PVOID heap) - { - /* Decrement the reference count. If it's down to 0, free - structure from heap. */ - if (this && InterlockedDecrement (&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. */ - HANDLE h = DirectoryHandle (); - if (h) - NtClose (h); - RtlFreeHeap (heap, 0, this); - } - } - void FillIn (HANDLE dir, PUNICODE_STRING name, ULONG old_dismount_count) - { - /* Fill in all values into this FAST_CWD structure. */ - DirectoryHandle () = dir; - ReferenceCount () = 1; - OldDismountCount () = old_dismount_count; - /* The new structure stores the device characteristics of the - volume holding the dir. RtlGetCurrentDirectory_U checks - if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if - the volume is still the same as the one used when opening - the directory handle. - We don't call NtQueryVolumeInformationFile for the \\?\PIPE, - though. It just returns STATUS_INVALID_HANDLE anyway. */ - if (fast_cwd_version != FCWD_OLD) - { - SetFSCharacteristics (0); - if (name != &ro_u_pipedir) - { - IO_STATUS_BLOCK io; - FILE_FS_DEVICE_INFORMATION ffdi; - if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi, - sizeof ffdi, FileFsDeviceInformation))) - SetFSCharacteristics (ffdi.Characteristics); - } - } - RtlInitEmptyUnicodeString (&Path (), Buffer (), - MAX_PATH * sizeof (WCHAR)); - copy_cwd_str (&Path (), name); + case FCWD_OLD: + break; + case FCWD_W7: + f7.FSCharacteristics = val; + break; + case FCWD_W8: + f8.FSCharacteristics = val; + break; } +} - static void SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir) +fcwd_version_t & +fcwd_access_t::fast_cwd_version () +{ + return cygheap->cwd.fast_cwd_version; +} + +void +fcwd_access_t::CopyPath (UNICODE_STRING &target) +{ + /* Copy the Path contents over into the UNICODE_STRING referenced by + target. This is used to set the CurrentDirectoryName in the + user parameter block. */ + target = Path (); +} + +void +fcwd_access_t::Free (PVOID heap) +{ + /* Decrement the reference count. If it's down to 0, free + structure from heap. */ + if (this && InterlockedDecrement (&ReferenceCount ()) == 0) { - /* Input: The buffer pointer as it's stored in the user parameter block - and a directory handle. - This function computes the address to the FAST_CWD structure based - on the version and overwrites the directory handle. It is only - used if we couldn't figure out the address of fast_cwd_ptr. */ - fcwd_access_t *f_cwd; - switch (fast_cwd_version) + /* 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. */ + HANDLE h = DirectoryHandle (); + if (h) + NtClose (h); + RtlFreeHeap (heap, 0, this); + } +} + +void +fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name, + ULONG old_dismount_count) +{ + /* Fill in all values into this FAST_CWD structure. */ + DirectoryHandle () = dir; + ReferenceCount () = 1; + OldDismountCount () = old_dismount_count; + /* The new structure stores the device characteristics of the + volume holding the dir. RtlGetCurrentDirectory_U checks + if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if + the volume is still the same as the one used when opening + the directory handle. + We don't call NtQueryVolumeInformationFile for the \\?\PIPE, + though. It just returns STATUS_INVALID_HANDLE anyway. */ + if (fast_cwd_version () != FCWD_OLD) + { + SetFSCharacteristics (0); + if (name != &ro_u_pipedir) { - case FCWD_OLD: - default: - f_cwd = (fcwd_access_t *) - ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_OLD, Buffer)); - case FCWD_W7: - f_cwd = (fcwd_access_t *) - ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_7, Buffer)); - case FCWD_W8: - f_cwd = (fcwd_access_t *) - ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_8, Buffer)); + IO_STATUS_BLOCK io; + FILE_FS_DEVICE_INFORMATION ffdi; + if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi, + sizeof ffdi, FileFsDeviceInformation))) + SetFSCharacteristics (ffdi.Characteristics); } - f_cwd->DirectoryHandle () = dir; } - static void SetVersionFromPointer (PBYTE buf_p, bool is_buffer) + RtlInitEmptyUnicodeString (&Path (), Buffer (), + MAX_PATH * sizeof (WCHAR)); + copy_cwd_str (&Path (), name); +} + +void +fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir) +{ + /* Input: The buffer pointer as it's stored in the user parameter block + and a directory handle. + This function computes the address to the FAST_CWD structure based + on the version and overwrites the directory handle. It is only + used if we couldn't figure out the address of fast_cwd_ptr. */ + fcwd_access_t *f_cwd; + switch (fast_cwd_version ()) { - /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a - pointer to the Buffer within (is_buffer == true), this function - computes the FAST_CWD version by checking that Path.MaximumLength - equals MAX_PATH, and that Path.Buffer == Buffer. */ - if (is_buffer) - buf_p -= __builtin_offsetof (FAST_CWD_8, Buffer); - fcwd_access_t *f_cwd = (fcwd_access_t *) buf_p; - if (f_cwd->f8.Path.MaximumLength == MAX_PATH * sizeof (WCHAR) - && f_cwd->f8.Path.Buffer == f_cwd->f8.Buffer) - fast_cwd_version = FCWD_W8; - else if (f_cwd->f7.Path.MaximumLength == MAX_PATH * sizeof (WCHAR) - && f_cwd->f7.Path.Buffer == f_cwd->f7.Buffer) - fast_cwd_version = FCWD_W7; - else - fast_cwd_version = FCWD_OLD; + case FCWD_OLD: + default: + f_cwd = (fcwd_access_t *) + ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_OLD, Buffer)); + case FCWD_W7: + f_cwd = (fcwd_access_t *) + ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_7, Buffer)); + case FCWD_W8: + f_cwd = (fcwd_access_t *) + ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_8, Buffer)); } -}; -fcwd_access_t::fcwd_version_t fcwd_access_t::fast_cwd_version - __attribute__((section (".cygwin_dll_common"), shared)) - = fcwd_access_t::FCWD_W7; -/* fast_cwd_ptr is a pointer to the global RtlpCurDirRef pointer in - ntdll.dll pointing to the FAST_CWD structure which constitutes the CWD. - Unfortunately RtlpCurDirRef is not exported from ntdll.dll. - We put the pointer into the common shared DLL segment. This allows to - restrict the call to find_fast_cwd_pointer() to once per Cygwin session - per user session. This works, because ASLR randomizes the load address - of DLLs only once at boot time. */ -static fcwd_access_t **fast_cwd_ptr - __attribute__((section (".cygwin_dll_common"), shared)) - = (fcwd_access_t **) -1; + f_cwd->DirectoryHandle () = dir; +} + +void +fcwd_access_t::SetVersionFromPointer (PBYTE buf_p, bool is_buffer) +{ + /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a + pointer to the Buffer within (is_buffer == true), this function + computes the FAST_CWD version by checking that Path.MaximumLength + equals MAX_PATH, and that Path.Buffer == Buffer. */ + if (is_buffer) + buf_p -= __builtin_offsetof (FAST_CWD_8, Buffer); + fcwd_access_t *f_cwd = (fcwd_access_t *) buf_p; + if (f_cwd->f8.Path.MaximumLength == MAX_PATH * sizeof (WCHAR) + && f_cwd->f8.Path.Buffer == f_cwd->f8.Buffer) + fast_cwd_version () = FCWD_W8; + else if (f_cwd->f7.Path.MaximumLength == MAX_PATH * sizeof (WCHAR) + && f_cwd->f7.Path.Buffer == f_cwd->f7.Buffer) + fast_cwd_version () = FCWD_W7; + else + fast_cwd_version () = FCWD_OLD; +} #define peek32(x) (*(uint32_t *)(x)) @@ -3718,8 +3645,9 @@ static fcwd_access_t **fast_cwd_ptr 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... */ + Windows 7 32/64 bit, Server 2008 R2 (which is only 64 bit anyway), + and W8CP 32/64 bit. There's some hope this will still work for + Windows 8 RTM... */ static fcwd_access_t ** find_fast_cwd_pointer () { @@ -3950,8 +3878,13 @@ cwdstuff::init () if (win32.Buffer) override_win32_cwd (true, SharedUserData.DismountCount); else - /* Initially re-open the cwd to allow POSIX semantics. */ - set (NULL, NULL); + { + /* Initialize fast_cwd stuff. */ + fast_cwd_ptr = (fcwd_access_t **) -1; + fast_cwd_version = FCWD_W7; + /* Initially re-open the cwd to allow POSIX semantics. */ + set (NULL, NULL); + } } /* Chdir and fill out the elements of a cwdstuff struct. */