From 54c31d5332a71d785db1b73cf3be41818adeab8a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 15 Oct 2020 20:17:46 +0200 Subject: [PATCH] ntdll: Make NtGetNlsSectionPtr() into a proper Nt syscall. Signed-off-by: Alexandre Julliard --- dlls/kernel32/tests/locale.c | 12 +-- dlls/ntdll/locale.c | 184 +---------------------------------- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/unix/env.c | 153 ++++++++++++++++++++++++++--- 4 files changed, 151 insertions(+), 202 deletions(-) diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 5b2f4c2d946..3fdfa251144 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -4131,7 +4131,7 @@ static void test_GetCPInfo(void) status = pNtGetNlsSectionPtr( i, 9999, NULL, &ptr, &size ); switch (i) { - case 9: /* unknown */ + case 9: /* sortkeys */ case 13: /* unknown */ ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_INVALID_PARAMETER_3, /* vista */ "%u: failed %x\n", i, status ); @@ -4160,9 +4160,9 @@ static void test_GetCPInfo(void) status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr2, &size ); ok( ptr != ptr2, "got same pointer\n" ); ret = UnmapViewOfFile( ptr ); - todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); + ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); ret = UnmapViewOfFile( ptr2 ); - todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); + ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); } /* codepage tables */ @@ -4182,7 +4182,7 @@ static void test_GetCPInfo(void) ok( !table.DBCSCodePage, "wrong dbcs %u\n", table.DBCSCodePage ); } ret = UnmapViewOfFile( ptr ); - todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); + ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); status = pNtGetNlsSectionPtr( 11, 936, NULL, &ptr, &size ); ok( !status, "failed %x\n", status ); @@ -4220,7 +4220,7 @@ static void test_GetCPInfo(void) } } ret = UnmapViewOfFile( ptr ); - todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); + ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); /* normalization tables */ @@ -4238,7 +4238,7 @@ static void test_GetCPInfo(void) if (status) break; ok( size > 0x8000 && size <= 0x30000 , "wrong size %lx\n", size ); ret = UnmapViewOfFile( ptr ); - todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); + ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() ); break; default: ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %x\n", i, status ); diff --git a/dlls/ntdll/locale.c b/dlls/ntdll/locale.c index 81263f4616a..e3cc0d183c4 100644 --- a/dlls/ntdll/locale.c +++ b/dlls/ntdll/locale.c @@ -228,13 +228,13 @@ static NTSTATUS load_norm_table( ULONG form, const struct norm_table **info ) } if (InterlockedCompareExchangePointer( (void **)&norm_tables[form], data, NULL )) - RtlFreeHeap( GetProcessHeap(), 0, data ); + NtUnmapViewOfSection( GetCurrentProcess(), data ); } *info = norm_tables[form]; return STATUS_SUCCESS; invalid: - RtlFreeHeap( GetProcessHeap(), 0, data ); + NtUnmapViewOfSection( GetCurrentProcess(), data ); return STATUS_INVALID_PARAMETER; } @@ -534,156 +534,6 @@ static unsigned int compose_string( const struct norm_table *info, WCHAR *str, u } -static NTSTATUS open_nls_data_file( ULONG type, ULONG id, HANDLE *file ) -{ - static const WCHAR pathfmtW[] = {'\\','?','?','\\','%','s','%','s',0}; - static const WCHAR keyfmtW[] = - {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'C','o','n','t','r','o','l','\\','N','l','s','\\','%','s',0}; - static const WCHAR sortdirW[] = {'C',':','\\','w','i','n','d','o','w','s','\\', - 'g','l','o','b','a','l','i','z','a','t','i','o','n','\\', - 's','o','r','t','i','n','g','\\',0}; - static const WCHAR cpW[] = {'C','o','d','e','p','a','g','e',0}; - static const WCHAR normW[] = {'N','o','r','m','a','l','i','z','a','t','i','o','n',0}; - static const WCHAR langW[] = {'L','a','n','g','u','a','g','e',0}; - static const WCHAR cpfmtW[] = {'%','u',0}; - static const WCHAR normfmtW[] = {'%','x',0}; - static const WCHAR langfmtW[] = {'%','0','4','x',0}; - static const WCHAR winedatadirW[] = {'W','I','N','E','D','A','T','A','D','I','R',0}; - static const WCHAR winebuilddirW[] = {'W','I','N','E','B','U','I','L','D','D','I','R',0}; - static const WCHAR dataprefixW[] = {'\\','n','l','s','\\',0}; - static const WCHAR cpdefaultW[] = {'c','_','%','0','3','d','.','n','l','s',0}; - static const WCHAR intlW[] = {'l','_','i','n','t','l','.','n','l','s',0}; - static const WCHAR normnfcW[] = {'n','o','r','m','n','f','c','.','n','l','s',0}; - static const WCHAR normnfdW[] = {'n','o','r','m','n','f','d','.','n','l','s',0}; - static const WCHAR normnfkcW[] = {'n','o','r','m','n','f','k','c','.','n','l','s',0}; - static const WCHAR normnfkdW[] = {'n','o','r','m','n','f','k','d','.','n','l','s',0}; - static const WCHAR normidnaW[] = {'n','o','r','m','i','d','n','a','.','n','l','s',0}; - static const WCHAR sortkeysW[] = {'s','o','r','t','d','e','f','a','u','l','t','.','n','l','s',0}; - - DWORD size; - HANDLE handle; - NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; - IO_STATUS_BLOCK io; - OBJECT_ATTRIBUTES attr; - UNICODE_STRING nameW, valueW; - WCHAR buffer[MAX_PATH], value[10]; - const WCHAR *name = NULL, *dir = system_dir; - KEY_VALUE_PARTIAL_INFORMATION *info; - - /* get filename from registry */ - - switch (type) - { - case NLS_SECTION_SORTKEYS: - if (id) return STATUS_INVALID_PARAMETER_1; - buffer[0] = 0; - break; - case NLS_SECTION_CASEMAP: - if (id) return STATUS_UNSUCCESSFUL; - swprintf( buffer, ARRAY_SIZE(buffer), keyfmtW, langW ); - swprintf( value, ARRAY_SIZE(value), langfmtW, LANGIDFROMLCID(system_lcid) ); - break; - case NLS_SECTION_CODEPAGE: - swprintf( buffer, ARRAY_SIZE(buffer), keyfmtW, cpW ); - swprintf( value, ARRAY_SIZE(value), cpfmtW, id ); - break; - case NLS_SECTION_NORMALIZE: - swprintf( buffer, ARRAY_SIZE(buffer), keyfmtW, normW ); - swprintf( value, ARRAY_SIZE(value), normfmtW, id ); - break; - default: - return STATUS_INVALID_PARAMETER_1; - } - - if (buffer[0]) - { - RtlInitUnicodeString( &nameW, buffer ); - RtlInitUnicodeString( &valueW, value ); - InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL ); - if (!(status = NtOpenKey( &handle, KEY_READ, &attr ))) - { - info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; - size = sizeof(buffer) - sizeof(WCHAR); - if (!(status = NtQueryValueKey( handle, &valueW, KeyValuePartialInformation, info, size, &size ))) - { - ((WCHAR *)info->Data)[info->DataLength / sizeof(WCHAR)] = 0; - name = (WCHAR *)info->Data; - } - NtClose( handle ); - } - } - - if (!name || !*name) /* otherwise some hardcoded defaults */ - { - switch (type) - { - case NLS_SECTION_SORTKEYS: - name = sortkeysW; - dir = sortdirW; - break; - case NLS_SECTION_CASEMAP: - name = intlW; - break; - case NLS_SECTION_CODEPAGE: - swprintf( buffer, ARRAY_SIZE(buffer), cpdefaultW, id ); - name = buffer; - break; - case NLS_SECTION_NORMALIZE: - switch (id) - { - case NormalizationC: name = normnfcW; break; - case NormalizationD: name = normnfdW; break; - case NormalizationKC: name = normnfkcW; break; - case NormalizationKD: name = normnfkdW; break; - case 13: name = normidnaW; break; - } - break; - } - if (!name) return status; - } - - /* try to open file in system dir */ - - valueW.MaximumLength = (wcslen(name) + wcslen(dir) + 5) * sizeof(WCHAR); - if (!(valueW.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, valueW.MaximumLength ))) - return STATUS_NO_MEMORY; - valueW.Length = swprintf( valueW.Buffer, valueW.MaximumLength/sizeof(WCHAR), - pathfmtW, dir, name ) * sizeof(WCHAR); - InitializeObjectAttributes( &attr, &valueW, 0, 0, NULL ); - status = NtOpenFile( file, GENERIC_READ, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT ); - if (!status) TRACE( "found %s\n", debugstr_w( valueW.Buffer )); - RtlFreeUnicodeString( &valueW ); - if (status != STATUS_OBJECT_NAME_NOT_FOUND && status != STATUS_OBJECT_PATH_NOT_FOUND) return status; - - /* not found, try in build or data dir */ - - RtlInitUnicodeString( &nameW, winebuilddirW ); - valueW.MaximumLength = 0; - if (RtlQueryEnvironmentVariable_U( NULL, &nameW, &valueW ) != STATUS_BUFFER_TOO_SMALL) - { - RtlInitUnicodeString( &nameW, winedatadirW ); - if (RtlQueryEnvironmentVariable_U( NULL, &nameW, &valueW ) != STATUS_BUFFER_TOO_SMALL) - return status; - } - valueW.MaximumLength = valueW.Length + sizeof(dataprefixW) + wcslen(name) * sizeof(WCHAR); - if (!(valueW.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, valueW.MaximumLength ))) - return STATUS_NO_MEMORY; - if (!RtlQueryEnvironmentVariable_U( NULL, &nameW, &valueW )) - { - wcscat( valueW.Buffer, dataprefixW ); - wcscat( valueW.Buffer, name ); - valueW.Length = wcslen(valueW.Buffer) * sizeof(WCHAR); - InitializeObjectAttributes( &attr, &valueW, 0, 0, NULL ); - status = NtOpenFile( file, GENERIC_READ, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT ); - if (!status) TRACE( "found %s\n", debugstr_w( valueW.Buffer )); - } - RtlFreeUnicodeString( &valueW ); - return status; -} - - void init_unix_codepage(void) { USHORT *data = unix_funcs->get_unix_codepage_data(); @@ -925,36 +775,6 @@ NTSTATUS WINAPI RtlSetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, } -/************************************************************************** - * NtGetNlsSectionPtr (NTDLL.@) - */ -NTSTATUS WINAPI NtGetNlsSectionPtr( ULONG type, ULONG id, void *unknown, void **ptr, SIZE_T *size ) -{ - FILE_END_OF_FILE_INFORMATION info; - IO_STATUS_BLOCK io; - HANDLE file; - NTSTATUS status; - - if ((status = open_nls_data_file( type, id, &file ))) return status; - - if ((status = NtQueryInformationFile( file, &io, &info, sizeof(info), FileEndOfFileInformation ))) - goto done; - /* FIXME: return a heap block instead of a file mapping for now */ - if (!(*ptr = RtlAllocateHeap( GetProcessHeap(), 0, info.EndOfFile.QuadPart ))) - { - status = STATUS_NO_MEMORY; - goto done; - } - status = NtReadFile( file, 0, NULL, NULL, &io, *ptr, info.EndOfFile.QuadPart, NULL, NULL ); - if (!status && io.Information != info.EndOfFile.QuadPart) status = STATUS_INVALID_FILE_FOR_SECTION; - if (!status) *size = io.Information; - else RtlFreeHeap( GetProcessHeap(), 0, *ptr ); -done: - NtClose( file ); - return status; -} - - /****************************************************************** * RtlInitCodePageTable (NTDLL.@) */ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 6f44f499825..ca427c46c04 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -222,7 +222,7 @@ @ stdcall -norelay -syscall NtGetContextThread(long ptr) @ stdcall -syscall NtGetCurrentProcessorNumber() # @ stub NtGetDevicePowerState -@ stdcall NtGetNlsSectionPtr(long long long ptr ptr) +@ stdcall -syscall NtGetNlsSectionPtr(long long long ptr ptr) @ stub NtGetPlugPlayEvent @ stdcall NtGetTickCount() @ stdcall -syscall NtGetWriteWatch(long long ptr long ptr ptr ptr) @@ -1226,7 +1226,7 @@ @ stdcall -private -norelay -syscall ZwGetContextThread(long ptr) NtGetContextThread @ stdcall -private -syscall ZwGetCurrentProcessorNumber() NtGetCurrentProcessorNumber # @ stub ZwGetDevicePowerState -@ stdcall -private ZwGetNlsSectionPtr(long long long ptr ptr) NtGetNlsSectionPtr +@ stdcall -private -syscall ZwGetNlsSectionPtr(long long long ptr ptr) NtGetNlsSectionPtr @ stub ZwGetPlugPlayEvent @ stdcall -private ZwGetTickCount() NtGetTickCount @ stdcall -private -syscall ZwGetWriteWatch(long long ptr long ptr ptr ptr) NtGetWriteWatch diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c index 876dcf0192d..f6775fc37a4 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c @@ -84,16 +84,49 @@ static struct void *wctable; } unix_cp; -static void *read_nls_file( const char *name ) +enum nls_section_type +{ + NLS_SECTION_SORTKEYS = 9, + NLS_SECTION_CASEMAP = 10, + NLS_SECTION_CODEPAGE = 11, + NLS_SECTION_NORMALIZE = 12 +}; + +static char *get_nls_file_path( ULONG type, ULONG id ) { const char *dir = build_dir ? build_dir : data_dir; + const char *name = NULL; + char *path, tmp[16]; + + switch (type) + { + case NLS_SECTION_SORTKEYS: name = "sortdefault"; break; + case NLS_SECTION_CASEMAP: name = "l_intl"; break; + case NLS_SECTION_CODEPAGE: name = tmp; sprintf( tmp, "c_%03u", id ); break; + case NLS_SECTION_NORMALIZE: + switch (id) + { + case NormalizationC: name = "normnfc"; break; + case NormalizationD: name = "normnfd"; break; + case NormalizationKC: name = "normnfkc"; break; + case NormalizationKD: name = "normnfkd"; break; + case 13: name = "normidna"; break; + } + break; + } + if (!name) return NULL; + if (!(path = malloc( strlen(dir) + strlen(name) + 10 ))) return NULL; + sprintf( path, "%s/nls/%s.nls", dir, name ); + return path; +} + +static void *read_nls_file( ULONG type, ULONG id ) +{ + char *path = get_nls_file_path( type, id ); struct stat st; - char *path; void *data, *ret = NULL; int fd; - if (!(path = malloc( strlen(dir) + 22 ))) return NULL; - sprintf( path, "%s/nls/%s.nls", dir, name ); if ((fd = open( path, O_RDONLY )) != -1) { fstat( fd, &st ); @@ -109,11 +142,73 @@ static void *read_nls_file( const char *name ) } close( fd ); } - else ERR( "failed to load %s\n", path ); + else ERR( "failed to load %u/%u\n", type, id ); free( path ); return ret; } +static NTSTATUS open_nls_data_file( ULONG type, ULONG id, HANDLE *file ) +{ + static const WCHAR systemdirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\', + 's','y','s','t','e','m','3','2','\\',0}; + static const WCHAR sortdirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\', + 'g','l','o','b','a','l','i','z','a','t','i','o','n','\\', + 's','o','r','t','i','n','g','\\',0}; + + NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; + IO_STATUS_BLOCK io; + OBJECT_ATTRIBUTES attr = { sizeof(attr) }; + UNICODE_STRING valueW; + WCHAR buffer[ARRAY_SIZE(sortdirW) + 16]; + char *p, *path = get_nls_file_path( type, id ); + + if (!path) return STATUS_OBJECT_NAME_NOT_FOUND; + + status = open_unix_file( file, path, GENERIC_READ, &attr, 0, FILE_SHARE_READ, + FILE_OPEN, FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 ); + if (status == STATUS_NO_SUCH_FILE) + { + /* try to open file in system dir */ + ntdll_wcscpy( buffer, type == NLS_SECTION_SORTKEYS ? sortdirW : systemdirW ); + p = strrchr( path, '/' ) + 1; + ascii_to_unicode( buffer + ntdll_wcslen(buffer), p, strlen(p) + 1 ); + valueW.Buffer = buffer; + valueW.Length = ntdll_wcslen( buffer ) * sizeof(WCHAR); + valueW.MaximumLength = sizeof( buffer ); + InitializeObjectAttributes( &attr, &valueW, 0, 0, NULL ); + status = NtOpenFile( file, GENERIC_READ, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT ); + } + free( path ); + return status; +} + +static NTSTATUS get_nls_section_name( ULONG type, ULONG id, WCHAR name[32] ) +{ + char buffer[32]; + + switch (type) + { + case NLS_SECTION_SORTKEYS: + if (id) return STATUS_INVALID_PARAMETER_1; + strcpy( buffer, "\\NLS\\NlsSectionSORTDEFAULT" ); + break; + case NLS_SECTION_CASEMAP: + if (id) return STATUS_UNSUCCESSFUL; + strcpy( buffer, "\\NLS\\NlsSectionLANG_INTL" ); + break; + case NLS_SECTION_CODEPAGE: + sprintf( buffer, "\\NLS\\NlsSectionCP%03u", id); + break; + case NLS_SECTION_NORMALIZE: + sprintf( buffer, "\\NLS\\NlsSectionNORM%08x", id); + break; + default: + return STATUS_INVALID_PARAMETER_1; + } + ascii_to_unicode( name, buffer, strlen(buffer) + 1 ); + return STATUS_SUCCESS; +} + static int get_utf16( const WCHAR *src, unsigned int srclen, unsigned int *ch ) { @@ -167,7 +262,7 @@ static struct norm_table *nfc_table; static void init_unix_codepage(void) { - nfc_table = read_nls_file( "normnfc" ); + nfc_table = read_nls_file( NLS_SECTION_NORMALIZE, NormalizationC ); } static void put_utf16( WCHAR *dst, unsigned int ch ) @@ -398,11 +493,8 @@ static void init_unix_codepage(void) { if (charset_names[pos].cp != CP_UTF8) { - char name[16]; - void *data; - - sprintf( name, "c_%03u", charset_names[pos].cp ); - if ((data = read_nls_file( name ))) init_unix_cptable( data ); + void *data = read_nls_file( NLS_SECTION_CODEPAGE, charset_names[pos].cp ); + if (data) init_unix_cptable( data ); } return; } @@ -958,7 +1050,7 @@ void init_environment( int argc, char *argv[], char *envp[] ) init_unix_codepage(); init_locale(); - if ((case_table = read_nls_file( "l_intl" ))) + if ((case_table = read_nls_file( NLS_SECTION_CASEMAP, 0 ))) { uctable = case_table + 2; lctable = case_table + case_table[1] + 2; @@ -1283,6 +1375,43 @@ void CDECL get_locales( WCHAR *sys, WCHAR *user ) } +/************************************************************************** + * NtGetNlsSectionPtr (NTDLL.@) + */ +NTSTATUS WINAPI NtGetNlsSectionPtr( ULONG type, ULONG id, void *unknown, void **ptr, SIZE_T *size ) +{ + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + WCHAR name[32]; + HANDLE handle, file; + NTSTATUS status; + + if ((status = get_nls_section_name( type, id, name ))) return status; + + nameW.Buffer = name; + nameW.Length = ntdll_wcslen(name) * sizeof(WCHAR); + nameW.MaximumLength = sizeof(name); + InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL ); + if ((status = NtOpenSection( &handle, SECTION_MAP_READ, &attr ))) + { + if ((status = open_nls_data_file( type, id, &file ))) return status; + attr.Attributes = OBJ_OPENIF | OBJ_PERMANENT; + status = NtCreateSection( &handle, SECTION_MAP_READ, &attr, NULL, PAGE_READONLY, SEC_COMMIT, file ); + NtClose( file ); + if (status == STATUS_OBJECT_NAME_EXISTS) status = STATUS_SUCCESS; + } + if (!status) + { + *ptr = NULL; + *size = 0; + status = NtMapViewOfSection( handle, GetCurrentProcess(), ptr, 0, 0, NULL, size, + ViewShare, 0, PAGE_READONLY ); + } + NtClose( handle ); + return status; +} + + /********************************************************************** * NtQueryDefaultLocale (NTDLL.@) */