diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index ba0f2227e..afd9cd1e9 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,11 @@ +2011-02-13 Corinna Vinschen + + * path.cc (struct _FAST_CWD): Redefine to new layout used since patch + for KB 2393802. Adjust comments throughout. + (struct _FAST_CWD_OLD): Rename former definition. + (cwdstuff::override_win32_cwd): Check if the OS is using the old or the + new FAST_CWD structure layout and handle accordingly. + 2011-02-11 Christopher Faylor * mkstatic: Make sure that we are not cd'ed to temporary directory on diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index e4075410f..a1f36b7cd 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -3432,6 +3432,20 @@ cygwin_split_path (const char *path, char *dir, char *file) minimal locking and it's much more multi-thread friendly. Presumably it minimizes contention when accessing the CWD. */ typedef struct _FAST_CWD { + 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]; +} FAST_CWD, *PFAST_CWD; + +/* This is the old FAST_CWD structure up to the patch from KB 2393802, + release in February 2011. Hopefully it's not used for long anymore, + but for quite some time we can't rely on this fact. */ +typedef struct _FAST_CWD_OLD { LONG ReferenceCount; /* Only release when this is 0. */ HANDLE DirectoryHandle; ULONG OldDismountCount; /* Reflects the system DismountCount @@ -3439,7 +3453,7 @@ typedef struct _FAST_CWD { UNICODE_STRING Path; /* Path's Buffer member always refers to the following Buffer array. */ WCHAR Buffer[MAX_PATH]; -} FAST_CWD, *PFAST_CWD; +} FAST_CWD_OLD, *PFAST_CWD_OLD; /* fast_cwd_ptr is a pointer to the global pointer in ntdll.dll pointing to the FAST_CWD structure which constitutes the CWD. @@ -3556,7 +3570,10 @@ cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count) 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. */ + /* First allocate a new FAST_CWD structure on the heap. + The new FAST_CWD structure is 4 byte bigger than the old one, + but we simply don't care, so we allocate always room for the + new one. */ PFAST_CWD f_cwd = (PFAST_CWD) RtlAllocateHeap (heap, 0, sizeof (FAST_CWD)); if (!f_cwd) @@ -3564,35 +3581,82 @@ cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count) 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) + /* Fill in the values. Fortunately it's simple to check for the + new structure. Path.MaximumLength takes the same space as the + high word of the old ReferenceCount. We know that MaximumLength + is always MAX_PATH. For the ref count this would account for a + pratically impossible value between 34078720 and 34079240. */ + if ((*fast_cwd_ptr)->Path.MaximumLength == MAX_PATH * sizeof (WCHAR)) { - /* 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); + /* New FAST_CWD structure. */ + IO_STATUS_BLOCK io; + FILE_FS_DEVICE_INFORMATION ffdi; + + RtlInitEmptyUnicodeString (&f_cwd->Path, f_cwd->Buffer, + MAX_PATH * sizeof (WCHAR)); + f_cwd->DirectoryHandle = dir; + /* 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. */ + f_cwd->FSCharacteristics = + (!error + && NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi, + sizeof ffdi, FileFsDeviceInformation))) + ? ffdi.Characteristics : 0; + f_cwd->ReferenceCount = 1; + f_cwd->OldDismountCount = old_dismount_count; + 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 (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 + { + /* Old FAST_CWD structure. Otherwise same procedure as above, + except for the non-existant FSCharacteristics member. */ + PFAST_CWD_OLD f_cwd_old = (PFAST_CWD_OLD) f_cwd; + f_cwd_old->ReferenceCount = 1; + f_cwd_old->DirectoryHandle = dir; + f_cwd_old->OldDismountCount = old_dismount_count; + RtlInitEmptyUnicodeString (&f_cwd_old->Path, f_cwd_old->Buffer, + MAX_PATH * sizeof (WCHAR)); + copy_cwd_str (&f_cwd_old->Path, error ? &ro_u_pipedir : &win32); + RtlEnterCriticalSection (peb.FastPebLock); + PFAST_CWD_OLD old_cwd = (PFAST_CWD_OLD) *fast_cwd_ptr; + *fast_cwd_ptr = (PFAST_CWD) f_cwd_old; + upp_cwd_str = f_cwd_old->Path; + upp_cwd_hdl = dir; + RtlLeaveCriticalSection (peb.FastPebLock); + if (InterlockedDecrement (&old_cwd->ReferenceCount) == 0) + { + if (old_cwd->DirectoryHandle) + NtClose (old_cwd->DirectoryHandle); + RtlFreeHeap (heap, 0, old_cwd); + } } } else @@ -3636,8 +3700,12 @@ cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count) PFAST_CWD f_cwd = (PFAST_CWD) ((PBYTE) upp_cwd_str.Buffer - __builtin_offsetof (struct _FAST_CWD, Buffer)); + if (f_cwd->Path.MaximumLength == MAX_PATH * sizeof (WCHAR)) + f_cwd->DirectoryHandle = dir; + else + ((PFAST_CWD_OLD) f_cwd)->DirectoryHandle = dir; h = upp_cwd_hdl; - f_cwd->DirectoryHandle = upp_cwd_hdl = dir; + 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