diff options
Diffstat (limited to 'winsup/cygwin/path.cc')
-rw-r--r-- | winsup/cygwin/path.cc | 315 |
1 files changed, 124 insertions, 191 deletions
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); - } + case FCWD_OLD: + break; + case FCWD_W7: + f7.FSCharacteristics = val; + break; + case FCWD_W8: + f8.FSCharacteristics = val; + break; } - void FillIn (HANDLE dir, PUNICODE_STRING name, ULONG old_dismount_count) +} + +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) { - /* 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); + /* 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); } +} - static void SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir) +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) { - /* 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) + 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. */ |