[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.
This commit is contained in:
Katayama Hirofumi MZ 2025-05-06 12:38:54 +09:00 committed by GitHub
parent 2c475add8c
commit 530d26a1f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 246 additions and 2 deletions

View file

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

View file

@ -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);

View file

@ -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);
}