Fixes The Rising of The Shield Hero: Relieve the Animation hangs on start (and maybe something else with ENIGMA protector).
The protector implements virtual file system which hotpatches a lot of file related functions, in particular, NtOpenFile(). When NtOpenFile() is called from RtlSetCurrentDirectory_U() with peb lock held the hotpatcher takes its own lock and that can deadlock with other threads performing file / path operations ending up in get_full_path_helper() which also takes PEB lock. Once the hotpatcher is initialized the game only sets "." or the same full path as current and may do that rather often.
It seems that reopening the file handle for the same directory is redundant anyway, so probably makes sense to just avoid that part.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/path.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c index cccd000a6c6..519263c2c6a 100644 --- a/dlls/ntdll/path.c +++ b/dlls/ntdll/path.c @@ -905,13 +905,13 @@ ULONG WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf) NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) { FILE_FS_DEVICE_INFORMATION device_info; + ULONG size, compare_size; OBJECT_ATTRIBUTES attr; UNICODE_STRING newdir; IO_STATUS_BLOCK io; CURDIR *curdir; HANDLE handle; NTSTATUS nts; - ULONG size; PWSTR ptr;
newdir.Buffer = NULL; @@ -929,6 +929,22 @@ NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) goto out; }
+ size = newdir.Length / sizeof(WCHAR); + ptr = newdir.Buffer; + ptr += 4; /* skip ??\ prefix */ + size -= 4; + + if (size && ptr[size - 1] == '\') compare_size = size - 1; + else compare_size = size; + + if (curdir->DosPath.Length == (compare_size + 1) * sizeof(WCHAR) + && !memcmp( curdir->DosPath.Buffer, ptr, compare_size * sizeof(WCHAR) )) + { + TRACE( "dir %s is the same as current.\n", debugstr_us(dir) ); + nts = STATUS_SUCCESS; + goto out; + } + attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.Attributes = OBJ_CASE_INSENSITIVE; @@ -953,10 +969,6 @@ NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) curdir->Handle = handle;
/* append trailing \ if missing */ - size = newdir.Length / sizeof(WCHAR); - ptr = newdir.Buffer; - ptr += 4; /* skip ??\ prefix */ - size -= 4; if (size && ptr[size - 1] != '\') ptr[size++] = '\';
/* convert ??\UNC\ path to \ prefix */
Paul Gofman wine@gitlab.winehq.org wrote:
- if (curdir->DosPath.Length == (compare_size + 1) * sizeof(WCHAR)
&& !memcmp( curdir->DosPath.Buffer, ptr, compare_size * sizeof(WCHAR) ))
- {
TRACE( "dir %s is the same as current.\n", debugstr_us(dir) );
nts = STATUS_SUCCESS;
goto out;
- }
Comparison probably should be case insensitive.