* 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.
This commit is contained in:
Corinna Vinschen 2012-03-05 11:50:25 +00:00
parent 75effa37fc
commit a3904c655e
3 changed files with 228 additions and 194 deletions

View File

@ -1,3 +1,14 @@
2012-03-05 Corinna Vinschen <corinna@vinschen.de>
* 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 <corinna@vinschen.de>
* dll_init.cc (dll_list::operator[]): Extend comment a bit more to

View File

@ -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:

View File

@ -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. */