From 530d26a1f420c8a5799d0008493aebe155e5ebad Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 6 May 2025 12:38:54 +0900 Subject: [PATCH] [NTGDI][NTUSER] Initial support of NtGdiRemoveFontResourceW (#7877) Enable the users to delete fonts. JIRA issue: CORE-17684 - Add IntDeleteRegFontEntry helper function. - Add RegDeleteValueW and RegEnumValueW helper functions in win32ss/user/ntuser/misc/registry.c. - Add some code to IntGdiRemoveFontResourceSingle function. --- win32ss/gdi/ntgdi/freetype.c | 119 +++++++++++++++++++++++++++- win32ss/gdi/ntgdi/misc.h | 13 +++ win32ss/user/ntuser/misc/registry.c | 116 +++++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 2 deletions(-) diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c index 6b17ef73b65..8e74f9f87f1 100644 --- a/win32ss/gdi/ntgdi/freetype.c +++ b/win32ss/gdi/ntgdi/freetype.c @@ -2086,6 +2086,8 @@ NameFromCharSet(BYTE CharSet) } } +static const UNICODE_STRING DosPathPrefix = RTL_CONSTANT_STRING(L"\\??\\"); + /* Adds the font resource from the specified file to the system */ static INT FASTCALL IntGdiAddFontResourceSingle( @@ -2108,7 +2110,6 @@ IntGdiAddFontResourceSingle( LPWSTR pszBuffer; PFILE_OBJECT FileObject; static const UNICODE_STRING TrueTypePostfix = RTL_CONSTANT_STRING(L" (TrueType)"); - static const UNICODE_STRING DosPathPrefix = RTL_CONSTANT_STRING(L"\\??\\"); /* Build PathName */ if (dwFlags & AFRX_DOS_DEVICE_PATH) @@ -2326,12 +2327,126 @@ IntGdiAddFontResourceEx( return ret; } +/* Borrowed from shlwapi */ +static PWSTR +PathFindFileNameW(_In_ PCWSTR pszPath) +{ + PCWSTR lastSlash = pszPath; + while (*pszPath) + { + if ((*pszPath == L'\\' || *pszPath == L'/' || *pszPath == L':') && + pszPath[1] && pszPath[1] != '\\' && pszPath[1] != L'/') + { + lastSlash = pszPath + 1; + } + pszPath++; + } + return (PWSTR)lastSlash; +} + +/* Delete registry font entries */ +static VOID +IntDeleteRegFontEntries(_In_ PCWSTR pszFileName, _In_ DWORD dwFlags) +{ + NTSTATUS Status; + HKEY hKey; + WCHAR szName[MAX_PATH], szValue[MAX_PATH]; + ULONG dwIndex, NameLength, ValueSize, dwType; + + Status = RegOpenKey(g_FontRegPath.Buffer, &hKey); + if (!NT_SUCCESS(Status)) + return; + + for (dwIndex = 0;;) + { + NameLength = RTL_NUMBER_OF(szName); + ValueSize = sizeof(szValue); + Status = RegEnumValueW(hKey, dwIndex, szName, &NameLength, &dwType, szValue, &ValueSize); + if (!NT_SUCCESS(Status)) + break; + + if (dwType != REG_SZ || _wcsicmp(szValue, pszFileName) != 0) + { + ++dwIndex; + continue; + } + + /* Delete the found value */ + Status = RegDeleteValueW(hKey, szName); + if (!NT_SUCCESS(Status)) + break; + } + + ZwClose(hKey); +} + static BOOL FASTCALL IntGdiRemoveFontResourceSingle( _In_ PCUNICODE_STRING FileName, _In_ DWORD dwFlags) { - return FALSE; // FIXME + BOOL ret = FALSE; + UNICODE_STRING PathName; + PLIST_ENTRY CurrentEntry, NextEntry; + PFONT_ENTRY FontEntry; + PFONTGDI FontGDI; + PWSTR pszBuffer, pszFileTitle; + SIZE_T Length; + NTSTATUS Status; + + /* Build PathName */ + if (dwFlags & AFRX_DOS_DEVICE_PATH) + { + Length = DosPathPrefix.Length + FileName->Length + sizeof(UNICODE_NULL); + pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR); + if (!pszBuffer) + return FALSE; /* Failure */ + + RtlInitEmptyUnicodeString(&PathName, pszBuffer, Length); + RtlAppendUnicodeStringToString(&PathName, &DosPathPrefix); + RtlAppendUnicodeStringToString(&PathName, FileName); + } + else + { + Status = DuplicateUnicodeString(FileName, &PathName); + if (!NT_SUCCESS(Status)) + return FALSE; /* Failure */ + } + + pszFileTitle = PathName.Buffer; + if (!(dwFlags & AFRX_ALTERNATIVE_PATH)) + pszFileTitle = PathFindFileNameW(PathName.Buffer); + + if (!pszFileTitle || !*pszFileTitle) + { + RtlFreeUnicodeString(&PathName); + return FALSE; /* Failure */ + } + + /* Delete font entries that matches PathName */ + IntLockFreeType(); + for (CurrentEntry = g_FontListHead.Flink; + CurrentEntry != &g_FontListHead; + CurrentEntry = NextEntry) + { + FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_ENTRY, ListEntry); + NextEntry = CurrentEntry->Flink; + + FontGDI = FontEntry->Font; + ASSERT(FontGDI); + if (FontGDI->Filename && _wcsicmp(FontGDI->Filename, pszFileTitle) == 0) + { + RemoveEntryList(&FontEntry->ListEntry); + CleanupFontEntry(FontEntry); + if (dwFlags & AFRX_WRITE_REGISTRY) + IntDeleteRegFontEntries(pszFileTitle, dwFlags); + ret = TRUE; + } + } + IntUnLockFreeType(); + + RtlFreeUnicodeString(&PathName); + return ret; } BOOL FASTCALL diff --git a/win32ss/gdi/ntgdi/misc.h b/win32ss/gdi/ntgdi/misc.h index da0b043a1f5..81420cae73e 100644 --- a/win32ss/gdi/ntgdi/misc.h +++ b/win32ss/gdi/ntgdi/misc.h @@ -60,6 +60,19 @@ DWORD NTAPI RegGetSectionDWORD(LPCWSTR pszSection, PCWSTR pszValue, DWORD dwDefault); +NTSTATUS NTAPI +RegDeleteValueW(_In_ HKEY hKey, _In_ LPCWSTR pszValueName); + +NTSTATUS NTAPI +RegEnumValueW( + _In_ HKEY hKey, + _In_ ULONG Index, + _Out_opt_ LPWSTR Name, + _Out_opt_ PULONG NameLength, + _Out_opt_ PULONG Type, + _Out_opt_ PVOID Data, + _Out_opt_ PULONG DataLength); + VOID FASTCALL SetLastNtError(_In_ NTSTATUS Status); diff --git a/win32ss/user/ntuser/misc/registry.c b/win32ss/user/ntuser/misc/registry.c index 8a1cf574981..729b2392fc7 100644 --- a/win32ss/user/ntuser/misc/registry.c +++ b/win32ss/user/ntuser/misc/registry.c @@ -380,3 +380,119 @@ RegWriteUserSetting( return NT_SUCCESS(Status); } +static inline BOOL +IsStringType(ULONG Type) +{ + return (Type == REG_SZ) || (Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ); +} + +NTSTATUS +NTAPI +RegEnumValueW( + _In_ HKEY hKey, + _In_ ULONG Index, + _Out_opt_ LPWSTR Name, + _Out_opt_ PULONG NameLength, + _Out_opt_ PULONG Type, + _Out_opt_ PVOID Data, + _Out_opt_ PULONG DataLength) +{ + PKEY_VALUE_FULL_INFORMATION ValueInfo = NULL; + ULONG BufferLength = 0; + ULONG ReturnedLength; + NTSTATUS Status; + + /* Calculate the required buffer length */ + BufferLength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); + BufferLength += (MAX_PATH + 1) * sizeof(WCHAR); + if (Data != NULL) + BufferLength += *DataLength; + + /* Allocate the value buffer */ + ValueInfo = ExAllocatePoolWithTag(PagedPool, BufferLength, TAG_TEMP); + if (ValueInfo == NULL) + return STATUS_NO_MEMORY; + + /* Enumerate the value*/ + Status = ZwEnumerateValueKey(hKey, + Index, + KeyValueFullInformation, + ValueInfo, + BufferLength, + &ReturnedLength); + if (NT_SUCCESS(Status)) + { + if (Name) + { + /* Check if the name fits */ + if ((ValueInfo->NameLength + sizeof(WCHAR)) <= (*NameLength * sizeof(WCHAR))) + { + /* Copy it */ + RtlMoveMemory(Name, ValueInfo->Name, ValueInfo->NameLength); + + /* Terminate the string */ + Name[ValueInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + /* Otherwise, we ran out of buffer space */ + Status = STATUS_BUFFER_OVERFLOW; + goto done; + } + } + + if (Data) + { + /* Check if the data fits */ + if (ValueInfo->DataLength <= *DataLength) + { + /* Copy it */ + RtlMoveMemory(Data, + (PVOID)((ULONG_PTR)ValueInfo + ValueInfo->DataOffset), + ValueInfo->DataLength); + + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (IsStringType(ValueInfo->Type) && + ValueInfo->DataLength <= *DataLength - sizeof(WCHAR)) + { + WCHAR *ptr = (WCHAR *)((ULONG_PTR)Data + ValueInfo->DataLength); + if ((ptr > (WCHAR *)Data) && ptr[-1]) + *ptr = UNICODE_NULL; + } + } + else + { + Status = STATUS_BUFFER_OVERFLOW; + goto done; + } + } + } + +done: + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW)) + { + if (Type) + *Type = ValueInfo->Type; + + if (NameLength) + *NameLength = ValueInfo->NameLength; + + if (DataLength) + *DataLength = ValueInfo->DataLength; + } + + /* Free the buffer and return status */ + if (ValueInfo) + ExFreePoolWithTag(ValueInfo, TAG_TEMP); + + return Status; +} + +NTSTATUS NTAPI +RegDeleteValueW(_In_ HKEY hKey, _In_ LPCWSTR pszValueName) +{ + UNICODE_STRING ustrName; + RtlInitUnicodeString(&ustrName, pszValueName); + return ZwDeleteValueKey(hKey, &ustrName); +}