Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1585 lines
47 KiB

/*****************************************************************************
* Module Name: fntcache.cxx
*
* Font Cahce API's for NT font engine.
*
* History:
* 4-15-99 We need to add the code for WTS (Hydra)
* There are 3 major changes,
* (1) We can not write to fntcache.dat in read mode. So we have modified the code
* to make sure there there is no writing to fntcache in read mode.
* (2) Implement a lock algorithm so than no remote session can open the fntcache.dat
* during write mode of console.
* (3) Check the time stamp of win32k.sys and atmfd.dll
* 4-3-98 Yung-Jen Tony Tsai Wrote it.
*
* Copyright (c) 1998-1999 Microsoft Corporation
*****************************************************************************/
#include "precomp.hxx"
#include <ntverp.h>
FLONG gflFntCacheState;
FNTCACHE *gFntCache;
HSEMAPHORE ghsemFntCache = NULL;
extern "C" gbJpn98FixPitch;
extern BOOL G_fConsole;
#define FNTCACHEPATH L"\\SystemRoot\\system32\\FNTCACHE.DAT"
#define WIN32KPATH L"\\SystemRoot\\system32\\win32k.sys"
#define ATMFDPATH L"\\SystemRoot\\system32\\atmfd.dll"
#define DISABLE_REMOTE_FONT_BOOT_CACHE L"DisableRemoteFontBootCache"
#define LASTBOOTTIME_FONT_CACHE_STATE L"LastBootTimeFontCacheState"
#define FNT_CACHE_EXTRA_SIZE (16*512)
#define RESERVE_LINKS 200
#if DBG
VOID DebugGreTrackRemoveMapView(PVOID ViewBase);
#define FNTCACHE_DBG_LEVEL_0 0
#define FNTCACHE_DBG_LEVEL_1 1
#define FNTCACHE_DBG_LEVEL_2 2
#define FNTCACHE_DBG_LEVEL_3 3
ULONG gFntTest = FNTCACHE_DBG_LEVEL_3;
#define FNT_KdBreakPoint(d, s1) \
{ \
if (d >= gFntTest) \
{ \
DbgPrint s1; \
\
if (d >= FNTCACHE_DBG_LEVEL_1) \
DbgBreakPoint(); \
} \
}
#else
#define FNT_KdBreakPoint(d, s1)
#endif
extern "C" ULONG ComputeFileviewCheckSum(PVOID, ULONG);
#define FNTCacheFileCheckSum(pTableView, cjView) ComputeFileviewCheckSum((PVOID)((PBYTE) pTableView + 4), (cjView -4))
#define FNTINDEX_INVALID 0xffffffff
ULONG ComupteFNTCacheFastCheckSum(ULONG cwc, PWSZ pwsz, PFONTFILEVIEW *ppfv,ULONG cFiles, DESIGNVECTOR *pdv, ULONG cjDV);
VOID SearchFNTCacheHlink(ULONG ulFastCheckSum, FNTHLINK **ppfntHLink, FNTCACHEHEADER *pTable);
BOOL bFntCacheCreateHLink(ULONG ulFastCheckSum);
VOID PutFNTCacheIFI(ULONG ulFastCheckSum, PBYTE pIfi, ULONG ulSize);
FNTHLINK * SearchFntCacheNewLink(ULONG ulFastCheckSum);
BOOL bInitCacheTable(ULONG ulTTFonts, ULONG ulT1FOnts, LARGE_INTEGER FntRegLWT, LARGE_INTEGER T1RegLWT, ULONG CodePage);
// Here is only for performance evaluation
#define KEY_GRE_INITIALIZE L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Gre_Initialize"
#define KEY_NT_CURRENTVERSION L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion"
#define FNT_DISABLEFONTCACHE L"DisableFontBootCache"
#define CURRENT_BUILDNUMBER L"CurrentBuildNumber"
BOOL bQueryValueKey(PWSZ pwszValue, HANDLE RegistryKey, PKEY_VALUE_PARTIAL_INFORMATION ValueKeyInfo, ULONG ValueLength);
BOOL bOpenKey(PWSZ pwszKey, HANDLE *pRegistryKey);
VOID vUnmapFontCacheFile(VOID);
#define EXTRA_BUFFER 48
#define BUFF_LENGTH (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + EXTRA_BUFFER)
typedef union _KVINFO
{
KEY_VALUE_PARTIAL_INFORMATION kv;
BYTE aj[BUFF_LENGTH];
} KVINFO;
VOID vCleanUpFntCache(VOID)
{
if (ghsemFntCache == NULL)
return;
HSEMAPHORE hsemTmp = ghsemFntCache;
{
SEMOBJ so(ghsemFntCache);
if (gFntCache)
{
if (gFntCache->pTable)
{
vUnmapFontCacheFile();
}
VFREEMEM((PVOID) gFntCache);
gFntCache = NULL;
}
gflFntCacheState = 0;
ghsemFntCache = NULL;
}
// delete the semaphore, no longer needed
GreDeleteSemaphore(hsemTmp);
}
BOOL bFntCacheDriverLWT( PCWSTR pcwFontDriverFileName, LARGE_INTEGER *pLastWriteTime)
{
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS NtStatus;
HANDLE FileHandle = 0;
IO_STATUS_BLOCK IoStatusBlock;
FILE_BASIC_INFORMATION FileBasicInfo;
BOOL bRet = FALSE;
pLastWriteTime->QuadPart = 0;
RtlInitUnicodeString(&UnicodeString, pcwFontDriverFileName);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
0,
0);
NtStatus = IoCreateFile(
&FileHandle,
FILE_GENERIC_READ
| FILE_GENERIC_EXECUTE
| SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN, // Flag for file open.
FILE_SYNCHRONOUS_IO_ALERT,
0,
0,
CreateFileTypeNone,
NULL,
IO_FORCE_ACCESS_CHECK | // Ensure the user has access to the file
IO_NO_PARAMETER_CHECKING | // All of the buffers are kernel buffers
IO_CHECK_CREATE_PARAMETERS);
if (!NT_SUCCESS(NtStatus))
{
return(FALSE);
}
// Get the time stamp
NtStatus = ZwQueryInformationFile(
FileHandle,
&IoStatusBlock,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation);
ZwClose(FileHandle);
if (NT_SUCCESS(NtStatus))
{
*pLastWriteTime = FileBasicInfo.LastWriteTime;
bRet = TRUE;
}
return bRet;
}
VOID vGetFontDriverLWT(LARGE_INTEGER *pWin32kLWT, LARGE_INTEGER *pAtmfdLWT)
{
LARGE_INTEGER LastWriteTime;
if (bFntCacheDriverLWT( WIN32KPATH, &LastWriteTime))
{
pWin32kLWT->QuadPart = LastWriteTime.QuadPart;
}
if (bFntCacheDriverLWT( ATMFDPATH, &LastWriteTime))
{
pAtmfdLWT->QuadPart = LastWriteTime.QuadPart;
}
}
NTSTATUS GetGreRegKey(HANDLE *phkRegistry, ACCESS_MASK dwDesiredAccess, PCWSTR pcwsz)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
RtlInitUnicodeString(&UnicodeString, pcwsz);
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
return ZwOpenKey(phkRegistry, dwDesiredAccess, &ObjectAttributes);
}
BOOL bSetFntCacheReg(PCWSTR pcwsz, DWORD dwValue)
{
HANDLE hkRegistry;
UNICODE_STRING UnicodeString;
NTSTATUS status;
BOOL bRet = FALSE;
status = GetGreRegKey(&hkRegistry, GENERIC_WRITE, KEY_GRE_INITIALIZE);
if (NT_SUCCESS(status))
{
RtlInitUnicodeString(&UnicodeString, pcwsz);
status = ZwSetValueKey(hkRegistry,
&UnicodeString,
0,
REG_DWORD,
&dwValue,
sizeof(DWORD));
if (NT_SUCCESS(status))
bRet = TRUE;
else
WARNING(" Failed to set DisableRemoteFontBootCache registry");
ZwCloseKey(hkRegistry);
}
return bRet;
}
DWORD bQueryFntCacheReg (HANDLE hkRegistry, PCWSTR pcwsz, DWORD *pdwDisableMode)
{
UNICODE_STRING UnicodeString;
NTSTATUS status;
DWORD Length;
PKEY_VALUE_FULL_INFORMATION Information;
BOOL bRet = FALSE;
RtlInitUnicodeString(&UnicodeString, pcwsz);
Length = sizeof(KEY_VALUE_FULL_INFORMATION) + (wcslen(pcwsz) + 1) * 2 +
sizeof(DWORD);
Information = (PKEY_VALUE_FULL_INFORMATION) PALLOCMEM(Length, 'CFTT');
if (Information)
{
status = ZwQueryValueKey(hkRegistry,
&UnicodeString,
KeyValueFullInformation,
Information,
Length,
&Length);
if (NT_SUCCESS(status))
{
*pdwDisableMode = *(LPDWORD) ((((PUCHAR)Information) +
Information->DataOffset));
bRet = TRUE;
}
VFREEMEM(Information);
}
return bRet;
}
VOID vGetLastBootTimeStatus(void)
{
HANDLE hkRegistry;
NTSTATUS status;
DWORD dwReg = 0;
status = GetGreRegKey(&hkRegistry, GENERIC_READ, KEY_GRE_INITIALIZE);
gFntCache->flPrevBoot = 0;
if (NT_SUCCESS(status))
{
if (bQueryFntCacheReg(hkRegistry, LASTBOOTTIME_FONT_CACHE_STATE, &dwReg))
{
gFntCache->flPrevBoot = (FLONG) dwReg;
}
ZwCloseKey(hkRegistry);
}
// If we are going to be opening the fntcache.dat in read mode than current boot time state
// will be the same as the previous boot time state. But flThisBoot will change
// if we will be opening the file CREATE (write) mode.
gFntCache->flThisBoot = gFntCache->flPrevBoot;
}
/*****************************************************************************
* BOOL bFntCacheDisabled()
*
* Tempary routine for performance evaluation
*
* History
* 10-15-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
BOOL bFntCacheDisabled()
{
HANDLE hkRegistry;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
NTSTATUS status;
DWORD Length;
PKEY_VALUE_FULL_INFORMATION Information;
DWORD dwDisableMode = 0;
BOOL bRet = FALSE;
status = GetGreRegKey(&hkRegistry, GENERIC_READ, KEY_GRE_INITIALIZE);
if (NT_SUCCESS(status))
{
// let us check if somebody wanted to disable fontcache.dat by setting the
// DisableFontBootCache in the registry:
if (bQueryFntCacheReg(hkRegistry, L"DisableFontBootCache", &dwDisableMode))
{
if (dwDisableMode)
bRet = TRUE;
}
if (!bRet && !G_fConsole)
{
// we may still want to disable the use of font cache for this remote hydra session.
// We would do this if the console session is writing to the font cache at this time
// (the console session would set the dwDisableMode to 1 in the registry so that
// remote sessions do not try to access the cache)
// or
// if the font cache is in a suspcious state, so that whoever read from or wrote to
// the cache before set the dwDisableMode to 1 in the registry
if (bQueryFntCacheReg(hkRegistry, DISABLE_REMOTE_FONT_BOOT_CACHE, &dwDisableMode))
{
if (dwDisableMode)
bRet = TRUE;
}
else
{
// for some reason, to read the registry failed. So it would be safe to
// disable the font cache.
bRet = TRUE;
}
}
ZwCloseKey(hkRegistry);
}
return bRet;
}
/*****************************************************************************
* BOOL bFntCacheDisabled()
*
* This routine to get the registry only for JPN fix pitch compatible width
*
* History
* 2-3-2000 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID vGetJpn98FixPitch()
{
HANDLE hkRegistry;
NTSTATUS status;
DWORD dwFixPitch = 0;
status = GetGreRegKey(&hkRegistry, GENERIC_READ, KEY_GRE_INITIALIZE);
if (NT_SUCCESS(status))
{
if (bQueryFntCacheReg(hkRegistry, L"Jpn98FixPitch", &dwFixPitch))
{
if (dwFixPitch)
gbJpn98FixPitch = TRUE;
else
gbJpn98FixPitch = FALSE;
}
ZwCloseKey(hkRegistry);
}
}
/*****************************************************************************
* VOID FntCacheHDEV()
*
* Cache the font driver handle, include TT, OT, BMP and VT
*
* History
* 10-15-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID FntCacheHDEV(PPDEV hdev, ULONG ulDrv)
{
// There is no cache
if (!(gflFntCacheState & FNT_CACHE_MASK))
{
return;
}
ASSERTGDI (ulDrv && hdev, " Fnt Cache HDEV is wrong \n");
if (ulDrv)
gFntCache->hDev[ulDrv] = hdev;
}
/*****************************************************************************
* extern "C" VOID EngFntCacheFault(ULONG ulFastCheckSum, FLONG fl)
*
* Fault reprot for Engine font cache.
*
* History
* 10-15-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
extern "C" VOID EngFntCacheFault(ULONG ulFastCheckSum, FLONG fl)
{
FNTHLINK *pFntHlink = NULL;
BOOL bExcept = FALSE;
// There is no cache
if (ghsemFntCache == NULL)
return;
SEMOBJ so(ghsemFntCache);
if (!(gflFntCacheState & FNT_CACHE_MASK))
{
return;
}
BOOL bUpdate = FALSE;
switch (fl)
{
case ENG_FNT_CACHE_READ_FAULT:
case ENG_FNT_CACHE_WRITE_FAULT:
// if we have already marked the font cache as bad, do not need to do it again
if (!(gFntCache->flThisBoot & FNT_CACHE_STATE_ERROR))
bUpdate = TRUE;
break;
default:
break;
}
if (bUpdate)
{
// we do need to mark the cache invalid
gFntCache->flThisBoot |= FNT_CACHE_STATE_ERROR;
bSetFntCacheReg (LASTBOOTTIME_FONT_CACHE_STATE, gFntCache->flThisBoot);
}
}
/*****************************************************************************
* VOID PutFntCacheDrv(ULONG ulFastCheckSum, PPDEV hdev)
*
* Each font file is mapped to one font driver and cache it.
*
* History
* 10-15-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID PutFntCacheDrv(ULONG ulFastCheckSum, PPDEV hdev)
{
if (ghsemFntCache == NULL)
return;
SEMOBJ so(ghsemFntCache);
// There is no cache
if (!(gflFntCacheState & FNT_CACHE_MASK))
{
return;
}
// No checksum mean nothing we can do
if (ulFastCheckSum)
{
ULONG i, ulMode;
ulMode = FNT_DUMMY_DRV;
// Serched the cached device handle
for (i = FNT_TT_DRV; i <= FNT_OT_DRV; i++)
{
if (hdev == gFntCache->hDev[i])
{
ulMode = i;
break;
}
}
// some unknown font driver has been used for the system, and we will not cache it.
if (ulMode == FNT_DUMMY_DRV)
return;
// We cached it when FNTCache is in write mode
if (gflFntCacheState & FNT_CACHE_CREATE_MODE)
{
FNTHLINK *pFntHlink = NULL;
pFntHlink = SearchFntCacheNewLink(ulFastCheckSum);
if (pFntHlink)
{
// If fast check sum is in conflict, we can not cache it.
if (pFntHlink->ulDrvMode == FNT_DUMMY_DRV) // uninitialized link
{
pFntHlink->ulDrvMode = ulMode;
}
else
{
// Ok, fast checksum conflict
WARNING("Checksum conflict in PutFntCacheDrv");
pFntHlink->flLink |= FNT_CACHE_CHECKSUM_CONFLICT;
}
}
else
{
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
}
gFntCache->bWrite = TRUE;
}
else
{
ASSERTGDI(gflFntCacheState & FNT_CACHE_LOOKUP_MODE,
"PutFntCacheDrv: gflFntCacheState\n");
// attempting to write during the read mode.
// This may happen if somebody overwrote the file in the fonts directory
// without updating the [Fonts] section in the registry. In this (infrequent) case we
// want to force the rebuild of the cache at the next boot time.
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
}
}
}
VOID vUnmapFontCacheFile(VOID)
{
NTSTATUS ntStatus;
ASSERTGDI(gFntCache->pSection, "vUnmapFontCacheFile: gFntCache->pSection is NULL\n");
ASSERTGDI(gFntCache->pTable, "vUnmapFontCacheFile: gFntCache->pTable is NULL\n");
#if defined(_GDIPLUS_)
ntStatus = UnmapViewInProcessSpace(gFntCache->pTable);
#elif defined(_HYDRA_)
// MmUnmapViewInSessionSpace is internally promoted to
// MmUnmapViewInSystemSpace on non-Hydra systems.
ntStatus = Win32UnmapViewInSessionSpace((PVOID) gFntCache->pTable );
#else
ntStatus = MmUnmapViewInSystemSpace((PVOID)gFntCache->pTable)));
#endif
#if DBG
if (!NT_SUCCESS(ntStatus))
RIP(" Font cache file remove Map View failed \n");
#endif
#if DBG && defined(_HYDRA_)
if ((!G_fConsole) && (NT_SUCCESS(ntStatus)))
{
DebugGreTrackRemoveMapView((PVOID) gFntCache->pTable);
}
#endif
DEREFERENCE_FONTVIEW_SECTION(gFntCache->pSection);
gFntCache->pSection = NULL;
gFntCache->pTable = NULL;
return;
}
/*****************************************************************************
* VOID CloseFNTCache()
*
* Clean font cache after system boot
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
extern "C" VOID CloseFNTCache()
{
// do paranoid check
HSEMAPHORE hsemTmp = ghsemFntCache;
if (ghsemFntCache == NULL)
return;
{
SEMOBJ so(ghsemFntCache);
if (!(gflFntCacheState & FNT_CACHE_MASK))
{
gflFntCacheState = 0;
return;
}
if (gflFntCacheState & FNT_CACHE_CREATE_MODE)
{
// Close the file, we are done recreating it
if (gFntCache->pTable)
{
if (gFntCache->bWrite)
{
gFntCache->pTable->ulTotalLinks = gFntCache->ulCurrentHlink;
gFntCache->pTable->cjDataUsed = (ULONG)(gFntCache->pCacheBuf - gFntCache->pCacheBufStart);
gFntCache->pTable->CheckSum = FNTCacheFileCheckSum(gFntCache->pTable, gFntCache->pTable->ulFileSize);
}
}
}
if (gFntCache->pTable)
{
vUnmapFontCacheFile();
}
// now that the file is closed set the registry to indicate that it is ok for remote
// sessions to read from the cache file
if (gflFntCacheState & FNT_CACHE_CREATE_MODE)
{
if (gFntCache->flPrevBoot != gFntCache->flThisBoot)
bSetFntCacheReg(LASTBOOTTIME_FONT_CACHE_STATE, gFntCache->flThisBoot);
// Unlock fnt cache file, say that it is ok to read from it
bSetFntCacheReg(DISABLE_REMOTE_FONT_BOOT_CACHE, 0);
}
else
{
if (gFntCache->flThisBoot & (FNT_CACHE_STATE_ERROR | FNT_CACHE_STATE_FULL))
bSetFntCacheReg(LASTBOOTTIME_FONT_CACHE_STATE, gFntCache->flThisBoot);
}
VFREEMEM((PVOID) gFntCache);
gFntCache = NULL;
gflFntCacheState = 0;
ghsemFntCache = NULL;
}
// delete the semaphore, no longer needed
GreDeleteSemaphore(hsemTmp);
}
/*****************************************************************************
* BOOL bReAllocCacheFile(ULONG ulSize)
*
* ReAlloc font cache buffer
*
* History
* 10-14-98 modified [YungT]
* 8-22-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
BOOL bReAllocCacheFile(ULONG ulSize)
{
BOOL bOK = FALSE;
ULONG ulSizeExtraOrg;
ULONG ulFileSizeOrg;
ULONG ulSizeExtra;
ULONG ulCurrentSize;
ULONG ulFileSize;
DWORD dpOffsetStart;
FILEVIEW FileView;
// OVERFLOW means that we would like to get a bigger cache file but the OS would not give it to us.
// In this case we just close the cache file, update the checksum (bWrite set to true) and
// let remote sessions that are to be started later use the partial cache file
if (gFntCache->flThisBoot & FNT_CACHE_STATE_OVERFLOW)
{
// we tried this once before and it did not work, so we do not bother to do it again
return bOK;
}
ulFileSizeOrg = gFntCache->pTable->ulFileSize;
ulCurrentSize = (ULONG) (gFntCache->pCacheBufEnd - gFntCache->pCacheBuf);
// Calculate the extra cache we need
ulSizeExtra = QWORD_ALIGN(ulSize - ulCurrentSize) + FNT_CACHE_EXTRA_SIZE;
ulFileSize = ulFileSizeOrg + ulSizeExtra;
dpOffsetStart = (DWORD) (gFntCache->pCacheBufStart - (PBYTE) gFntCache->pTable);
if (gFntCache->pTable)
{
vUnmapFontCacheFile();
}
RtlZeroMemory(&FileView, sizeof(FILEVIEW));
if (bMapFile(FNTCACHEPATH, &FileView, ulFileSize, NULL))
{
DWORD dpOffset;
gFntCache->pTable = (FNTCACHEHEADER *) FileView.pvKView;
gFntCache->pSection = FileView.pSection;
gFntCache->pTable->ulFileSize = ulFileSize;
gFntCache->pTable->cjDataExtra += ulSizeExtra;
dpOffset = (ULONG)(gFntCache->pCacheBuf - gFntCache->pCacheBufStart);
// Got a new Table and got to re-calculate the buffer end pointer
gFntCache->pCacheBufStart = (PBYTE) gFntCache->pTable + dpOffsetStart;
gFntCache->pCacheBuf = gFntCache->pCacheBufStart + dpOffset;
gFntCache->pCacheBufEnd = gFntCache->pCacheBufStart + gFntCache->pTable->cjDataAll +
gFntCache->pTable->cjDataExtra;
bOK = TRUE;
}
else
{
// Something wrong, so we do not change anything
RtlZeroMemory(&FileView, sizeof(FILEVIEW));
if (bMapFile(FNTCACHEPATH, &FileView, ulFileSizeOrg, NULL))
{
gFntCache->pTable = (FNTCACHEHEADER *) FileView.pvKView;
gFntCache->pSection = FileView.pSection;
// we want the cache properly closed, with check sum recomputed etc.
gFntCache->bWrite = TRUE;
// Force rebuild on the next boot, but for this boot let remote sessions
// use the partial cache file.
gFntCache->flThisBoot |= (FNT_CACHE_STATE_OVERFLOW | FNT_CACHE_STATE_FULL);
}
else
{
// Something wrong here. We need to set it to no cache mode.
WARNING("bReAllocCacheFile failed to allocate more buffer \n");
gFntCache->flThisBoot |= FNT_CACHE_STATE_ERROR;
}
}
return bOK;
}
/*****************************************************************************
* ULONG QueryFontReg(PWCHAR pwcRegKeyPath, LARGE_INTEGER *pFontRegLastWriteTime)
*
* Helper function for query fonts information from TT and T1 fonts registry
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
BOOL QueryFontReg(PWCHAR pwcRegKeyPath, LARGE_INTEGER *pFontRegLastWriteTime, ULONG * pulFonts)
{
NTSTATUS Status;
KEY_FULL_INFORMATION KeyInfo;
HANDLE hkey = NULL;
DWORD Length;
*pulFonts = 0;
Status = GetGreRegKey(&hkey,KEY_READ, pwcRegKeyPath);
if (NT_SUCCESS(Status))
{
// get the number of entries in the [Fonts] section and get the last write time
Status = ZwQueryKey(hkey,
KeyFullInformation,
&KeyInfo,
sizeof(KeyInfo),
&Length);
if (NT_SUCCESS(Status))
{
// for additional fonts that do not load from fonts section of the registry
*pulFonts = KeyInfo.Values;
pFontRegLastWriteTime->QuadPart = KeyInfo.LastWriteTime.QuadPart;
FNT_KdBreakPoint(FNTCACHE_DBG_LEVEL_0, (" %d items in Font key \n", *pulFonts));
}
ZwCloseKey(hkey);
}
return NT_SUCCESS(Status);
}
/*****************************************************************************
* PVOID EngFntCacheAlloc(ULONG ulFastCheckSum, ULONG ulSize)
*
* Alloc the cached buffer for font driver
*
* History
* 10-5-98 rewrite [YungT]
* 8-22-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
extern "C" PVOID EngFntCacheAlloc(ULONG ulFastCheckSum, ULONG ulSize)
{
PVOID pvIfi = NULL;
{
if (ghsemFntCache == NULL)
return pvIfi;
SEMOBJ so(ghsemFntCache);
if (gflFntCacheState & FNT_CACHE_CREATE_MODE)
{
if ( (gFntCache->pCacheBuf + QWORD_ALIGN(ulSize) < gFntCache->pCacheBufEnd)
|| bReAllocCacheFile(ulSize))
{
FNTHLINK *pFntHlink = NULL;
if (pFntHlink = SearchFntCacheNewLink(ulFastCheckSum))
{
// If fast check sum is conflict, we can not cache it.
if (pFntHlink->cjData == 0 && pFntHlink->dpData == 0)
{
pvIfi = (PVOID) gFntCache->pCacheBuf;
// Gaurantee the cache pointer is at 8 byte boundary
gFntCache->pCacheBuf = gFntCache->pCacheBuf + QWORD_ALIGN(ulSize);
pFntHlink->cjData = ulSize;
pFntHlink->dpData = (DWORD) ((PBYTE) pvIfi - gFntCache->pCacheBufStart);
}
else
{
WARNING("Checksum conflict in EngFntCacheAlloc");
pFntHlink->flLink |= FNT_CACHE_CHECKSUM_CONFLICT;
}
gFntCache->bWrite = TRUE;
}
}
}
else
{
ASSERTGDI(gflFntCacheState & FNT_CACHE_LOOKUP_MODE,
"EngFntCacheAlloc: gflFntCacheState\n");
// During read mode, the remote session still wants to write into fntcache.dat.
// This could happen if RemoteSession1 adds more fonts to both registry and on the disk.
// Then later, the RemoteSession2 may attepmpt during its initialization to add these fonts
// to the font cache, but we will reject this and ask that on the next boot the cache file
// be rebuilt. also, the files could have been overwritten on the disk without
// registry entries being updated, so we just force rebuild next time to be safe.
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
}
}
if (gFntCache->flThisBoot & FNT_CACHE_STATE_ERROR)
{
CloseFNTCache();
pvIfi = NULL;
}
return pvIfi;
}
/*****************************************************************************
* PVOID EngFntCacheLookUp(ULONG FastCheckSum, ULONG *pcjData)
*
* Lookup font cache
*
* History
* 10-5-98 rewrite [YungT]
* 8-22-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
extern "C" PVOID EngFntCacheLookUp(ULONG FastCheckSum, ULONG *pcjData)
{
FNTHLINK *pFntHlink;
PBYTE pCache = NULL;
*pcjData = 0;
pFntHlink = NULL;
if (ghsemFntCache == NULL)
return (PVOID) pCache;
SEMOBJ so(ghsemFntCache);
if (gflFntCacheState & FNT_CACHE_LOOKUP_MODE)
{
if (gFntCache->pTable)
{
// Search the cache table
SearchFNTCacheHlink( FastCheckSum, &pFntHlink, gFntCache->pTable);
if(pFntHlink && !(pFntHlink->flLink & FNT_CACHE_CHECKSUM_CONFLICT))
{
*pcjData = pFntHlink->cjData;
if (*pcjData)
{
pCache = gFntCache->pCacheBufStart + pFntHlink->dpData;
}
}
#if DBG
else
{
if (pFntHlink && (pFntHlink->flLink & FNT_CACHE_CHECKSUM_CONFLICT))
WARNING("Catch the checksum conflict in EngFntCacheLookUp \n");
}
#endif
}
}
return (PVOID) pCache;
}
/*****************************************************************************
* VOID InitNewCacheTable()
*
* Initialize font cache, open the fntcache,dat file and create hash table
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
BOOL bInitCacheTable(ULONG ulTTFonts, ULONG ulT1Fonts, LARGE_INTEGER FntRegLWT, LARGE_INTEGER T1RegLWT,
LARGE_INTEGER Win32kLWT, LARGE_INTEGER AtmfdLWT, ULONG CodePage)
{
ULONG ulSize, ulIfiSize;
BOOL bOk = FALSE;
ULONG ulMaxFonts;
FILEVIEW FileView;
ulMaxFonts = ulTTFonts + ulT1Fonts + RESERVE_LINKS;
ulSize = SZ_FNTCACHE(ulMaxFonts) + SZ_FNTIFICACHE(ulTTFonts, ulT1Fonts);
if (gFntCache->pTable)
{
vUnmapFontCacheFile();
}
RtlZeroMemory(&FileView, sizeof(FILEVIEW));
if(bMapFile(FNTCACHEPATH, &FileView, ulSize, NULL))
{
gFntCache->pTable = (FNTCACHEHEADER *) FileView.pvKView;
gFntCache->pSection = FileView.pSection;
RtlFillMemory((PBYTE) gFntCache->pTable->aiBuckets,
FNTCACHE_MAX_BUCKETS * sizeof(DWORD), 0xFF);
RtlZeroMemory((PBYTE) gFntCache->pTable->ahlnk,
ulMaxFonts * sizeof(FNTHLINK));
gFntCache->pTable->ulCodePage = (ULONG) CodePage;
gFntCache->pTable->ulMaxFonts = ulMaxFonts;
gFntCache->pTable->ulTotalLinks = 0;
gFntCache->pTable->CheckSum = 0;
gFntCache->pTable->FntRegLWT.QuadPart = FntRegLWT.QuadPart;
gFntCache->pTable->T1RegLWT.QuadPart = T1RegLWT.QuadPart;
gFntCache->pTable->Win32kLWT.QuadPart = Win32kLWT.QuadPart;
gFntCache->pTable->AtmfdLWT.QuadPart = AtmfdLWT.QuadPart;
gFntCache->pTable->ulFileSize = ulSize;
gFntCache->pTable->cjDataAll = SZ_FNTIFICACHE(ulTTFonts, ulT1Fonts);
gFntCache->pTable->cjDataExtra = 0;
gFntCache->pTable->cjDataUsed = 0;
bOk = TRUE;
}
return bOk;
}
/*****************************************************************************
* VOID InitFNTCache()
*
* Initialize font cache, open the fntcache,dat file and create hash table
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID InitFNTCache()
{
LARGE_INTEGER FntRegLWT = { 0, 0};
LARGE_INTEGER T1RegLWT = { 0, 0};
LARGE_INTEGER Win32kLWT = { 0, 0};
LARGE_INTEGER AtmfdLWT = { 0, 0};
ULONG ulTTFonts = 0;
ULONG ulT1Fonts;
ULONG ulSize = 0;
BOOL bQryFntReg = FALSE;
USHORT AnsiCodePage, OemCodePage;
BOOL bLocked = TRUE;
// Initialize all the global variables
gflFntCacheState = 0;
ghsemFntCache = GreCreateSemaphore();
if (ghsemFntCache == NULL)
{
goto CleanUp;
}
// Only for performance evaluation.
if (bFntCacheDisabled())
{
goto CleanUp;
}
// Initialize and zero out all the memory
gFntCache = (FNTCACHE *) PALLOCMEM(sizeof(FNTCACHE), 'CFTT');
// There is something wrong we can not allocate buf
if (!gFntCache)
{
goto CleanUp;
}
if (G_fConsole)
bLocked = bSetFntCacheReg(DISABLE_REMOTE_FONT_BOOT_CACHE, 1);
// If we can not lock the font cache file,
// then we can not open it in console
if (!bLocked)
{
goto CleanUp;
}
gFntCache->pTable = NULL;
gFntCache->ulCurrentHlink = 0;
gFntCache->hDev[0] = 0;
gFntCache->hDev[1] = 0;
gFntCache->hDev[2] = 0;
gFntCache->hDev[3] = 0;
gFntCache->hDev[4] = 0;
gFntCache->bWrite = FALSE;
RtlGetDefaultCodePage(&AnsiCodePage,&OemCodePage);
// Get the last boot time status
vGetLastBootTimeStatus();
// Get LWT of font driver
vGetFontDriverLWT(&Win32kLWT, &AtmfdLWT);
// now open the TT Fonts key :
if (QueryFontReg(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts",
&FntRegLWT, &ulTTFonts))
{
ulTTFonts += FNTCACHE_EXTRA_LINKS;
// now open the Type 1 Fonts key :
if (QueryFontReg(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Type 1 Installer\\Type 1 Fonts",
&T1RegLWT, &ulT1Fonts))
{
bQryFntReg = TRUE;
}
}
{
FILEVIEW FileView;
RtlZeroMemory(&FileView, sizeof(FILEVIEW));
if (bMapFile(FNTCACHEPATH, &FileView, 0, NULL))
{
gFntCache->pTable = (FNTCACHEHEADER *) FileView.pvKView;
gFntCache->pSection = (FNTCACHEHEADER *) FileView.pSection;
}
if (gFntCache->pTable)
{
ULONG ulCheckSum;
BOOL bCacheRead;
BOOL bCheckSum;
bCacheRead = FALSE;
bCheckSum = FALSE;
// File did not change from last time boot.
if (gFntCache->pTable->CheckSum && FileView.cjView == gFntCache->pTable->ulFileSize &&
gFntCache->pTable->CheckSum == FNTCacheFileCheckSum(gFntCache->pTable, FileView.cjView) &&
gFntCache->pTable->AtmfdLWT.QuadPart == AtmfdLWT.QuadPart && // current signature of atmfd
gFntCache->pTable->ulCodePage == AnsiCodePage && // If locale changed, we need to re-create the cache
!(gFntCache->flPrevBoot & FNT_CACHE_STATE_ERROR) && // No error at last boot time
(!G_fConsole || // for remote session would not care about the registry update
(!(gFntCache->flPrevBoot & FNT_CACHE_STATE_FULL) &&
gFntCache->pTable->Win32kLWT.QuadPart == Win32kLWT.QuadPart && // check signature of win32k.sys
FntRegLWT.QuadPart == gFntCache->pTable->FntRegLWT.QuadPart && // Check date time of cache file
T1RegLWT.QuadPart == gFntCache->pTable->T1RegLWT.QuadPart
)
)
)
{
gflFntCacheState = FNT_CACHE_LOOKUP_MODE;
}
else
{
if(G_fConsole && bInitCacheTable(ulTTFonts, ulT1Fonts, FntRegLWT, T1RegLWT, Win32kLWT, AtmfdLWT, (ULONG) AnsiCodePage))
{
// If something will not match, then it means we need to create FNTCACHE again
gflFntCacheState = FNT_CACHE_CREATE_MODE;
}
else
{
if (G_fConsole)
{
WARNING(" Boot time Font Cache failed\n");
WARNING(" The pTable is corrupted and font registry is failed to open\n");
}
}
}
}
else
{
// If there is no FNTCACHE.DAT file
// Then we need to create it.
if(G_fConsole && bInitCacheTable(ulTTFonts, ulT1Fonts, FntRegLWT, T1RegLWT, Win32kLWT, AtmfdLWT, (ULONG) AnsiCodePage))
{
gflFntCacheState = FNT_CACHE_CREATE_MODE;
}
else
{
if (G_fConsole)
{
WARNING("Boot time Font Cache failed\n");
WARNING(" If you read this message, please contact YungT or NTFONTS\n");
WARNING(" You can continue without any harm, just hit g\n");
}
}
}
}
CleanUp:
// Semaphore initialized
if (gflFntCacheState & FNT_CACHE_MASK)
{
ASSERTGDI(gFntCache->pTable, "Fnt Cache pTable did not open \n");
// Initialize the start pointer of current Cache table
gFntCache->pCacheBufStart = (PBYTE) gFntCache->pTable +
SZ_FNTCACHE(gFntCache->pTable->ulMaxFonts);
gFntCache->pCacheBuf = gFntCache->pCacheBufStart + gFntCache->pTable->cjDataUsed;
gFntCache->pCacheBufEnd = gFntCache->pCacheBufStart + gFntCache->pTable->cjDataAll +
gFntCache->pTable->cjDataExtra;
gFntCache->ulCurrentHlink = gFntCache->pTable->ulTotalLinks;
if (gflFntCacheState & FNT_CACHE_LOOKUP_MODE)
{
// Unlock the the font cache file to make other session to use it
bSetFntCacheReg(DISABLE_REMOTE_FONT_BOOT_CACHE, 0);
}
else // FNT_CACHE_CREATE_MODE
{
gFntCache->flThisBoot = 0;
}
}
else
{
gflFntCacheState = 0;
// Clean up the memory
if (gFntCache)
{
if (gFntCache->pTable)
{
vUnmapFontCacheFile();
}
VFREEMEM((PVOID) gFntCache);
gFntCache = NULL;
}
if (ghsemFntCache)
{
GreDeleteSemaphore(ghsemFntCache);
ghsemFntCache = NULL;
}
}
FNT_KdBreakPoint(FNTCACHE_DBG_LEVEL_1, ("FNT Cache Create mode %d\n", gflFntCacheState));
}
/*****************************************************************************
* ULONG ComupteFNTCacheFastCheckSum(ULONG cwc, PWSZ pwsz)
*
* Helper function to compute fast checksum
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
ULONG ComupteFNTCacheFastCheckSum(ULONG cwc, PWSZ pwsz, PFONTFILEVIEW *ppfv,ULONG cFiles, DESIGNVECTOR *pdv, ULONG cjDV)
{
ULONG i;
ULONG checksum = 0;
USHORT *pusCode;
for ( i = 0; i < cFiles; i++)
{
checksum += (checksum * 256 + ppfv[i]->fv.cjView);
checksum += (checksum * 256 + (ULONG) ppfv[i]->fv.LastWriteTime.LowPart);
checksum += (checksum * 256 + (ULONG) ppfv[i]->fv.LastWriteTime.HighPart);
}
pusCode = (USHORT *) pwsz;
for ( i = 0; i < cwc; i++, pusCode++)
{
// SUM offset by 1 bit, it will make FastCheckSum unique
checksum += (checksum * 256 + (ULONG) *pusCode);
}
if (pdv && cjDV)
{
PULONG pulCur, pulEnd;
pulCur = (PULONG) pdv;
for (pulEnd = pulCur + cjDV / sizeof(ULONG); pulCur < pulEnd; pulCur ++)
{
checksum += 256 * checksum + *pulCur;
}
}
return checksum;
}
/*****************************************************************************
* ULONG LookUpFNTCacheTable(ULONG cwc, PWSZ pwszPathname, PULONG pulFastCheckSum)
*
* Lookup hash table if UFI exist then we return it, otherwise return 0
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
ULONG LookUpFNTCacheTable(ULONG cwc, PWSZ pwszPathname, PULONG pulFastCheckSum, PFONTFILEVIEW *ppfv, ULONG cFiles, PPDEV * pppDevCache,
DESIGNVECTOR *pdv, ULONG cjDV)
{
ULONG ulUFI = 0;
ULONG ulBucket = 0;
FNTHLINK *pFntHlink;
*pulFastCheckSum = 0;
*pppDevCache = NULL;
if (ghsemFntCache == NULL)
return ulUFI;
SEMOBJ so(ghsemFntCache);
if(cwc != 0)
{
*pulFastCheckSum = ComupteFNTCacheFastCheckSum ( cwc, pwszPathname, ppfv, cFiles, pdv, cjDV);
// If in CREATE mode, then nothing is in the cache
if (gflFntCacheState & FNT_CACHE_LOOKUP_MODE)
{
pFntHlink = NULL;
SearchFNTCacheHlink( *pulFastCheckSum, &pFntHlink, gFntCache->pTable);
if(pFntHlink && !(pFntHlink->flLink & FNT_CACHE_CHECKSUM_CONFLICT))
{
ASSERTGDI( pFntHlink->ulFastCheckSum == *pulFastCheckSum, "ulFastCheckSum != pFntHlink->ulFastCheckSum \n");
ulUFI = pFntHlink->ulUFI;
*pppDevCache = gFntCache->hDev[pFntHlink->ulDrvMode];
}
}
}
return ulUFI;
}
/*****************************************************************************
* VOID SearchFNTCacheHlink(ULONG ulFastCheckSum, FNTHLINK **ppFntHlink)
*
* Hash table search function
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID SearchFNTCacheHlink(ULONG ulFastCheckSum, FNTHLINK **ppFntHlink, FNTCACHEHEADER *pTable)
{
// If there is a Cache, then ppFntHlink will not be NULL
FNTHLINK *pFntHlink;
*ppFntHlink = NULL;
ULONG ulHashBucket;
DWORD iNext;
// Calculate the hash buckets
ulHashBucket = ulFastCheckSum % FNTCACHE_MAX_BUCKETS;
// Start from the first Offset.
iNext = pTable->aiBuckets[ulHashBucket];
while (iNext != FNTINDEX_INVALID)
{
pFntHlink = &pTable->ahlnk[iNext];
if (ulFastCheckSum == pFntHlink->ulFastCheckSum)
{
*ppFntHlink = pFntHlink;
break;
}
iNext = pFntHlink->iNext;
}
return;
}
FNTHLINK * SearchFntCacheNewLink(ULONG ulFastCheckSum)
{
FNTHLINK *pFntHlink = NULL;
// Search the new Link from pNewTable
SearchFNTCacheHlink(ulFastCheckSum, &pFntHlink, gFntCache->pTable);
// new Link dose not exist, we got to create it from pNewTable
if (!pFntHlink)
{
if (gFntCache->ulCurrentHlink < gFntCache->pTable->ulMaxFonts && bFntCacheCreateHLink(ulFastCheckSum))
{
pFntHlink = &gFntCache->pTable->ahlnk[gFntCache->ulCurrentHlink];
pFntHlink->ulFastCheckSum = ulFastCheckSum;
pFntHlink->ulUFI = 0;
pFntHlink->iNext = FNTINDEX_INVALID;
pFntHlink->cjData = 0;
pFntHlink->dpData = 0;
pFntHlink->flLink = 0;
pFntHlink->ulDrvMode = FNT_DUMMY_DRV;
gFntCache->ulCurrentHlink++;
}
else
{
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
}
}
return pFntHlink;
}
/*****************************************************************************
* VOID PutFNTCacheCheckSum(ULONG ulFastCheckSum,ULONG ulUFI)
*
* Hash table write mode, the UFI does not have in hash and then we put it in.
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
VOID PutFNTCacheCheckSum(ULONG ulFastCheckSum,ULONG ulUFI)
{
FNTHLINK *pFntHlink;
if (ghsemFntCache == NULL)
return;
SEMOBJ so(ghsemFntCache);
if (gflFntCacheState & FNT_CACHE_CREATE_MODE)
{
pFntHlink = NULL;
if (pFntHlink = SearchFntCacheNewLink(ulFastCheckSum))
{
// If fast check sum is conflict, we can not cache it.
if(pFntHlink->ulUFI == 0)
{
pFntHlink->ulUFI = ulUFI;
}
else
{
WARNING("Checksum conflict in PutFNTCacheCheckSum");
pFntHlink->flLink |= FNT_CACHE_CHECKSUM_CONFLICT;
}
gFntCache->bWrite = TRUE;
}
else
{
WARNING("FNTCACHE is not big enough \n");
FNT_KdBreakPoint(FNTCACHE_DBG_LEVEL_1, ("Put Trace: buckets %d, FastCheckSum %x,Check Sum %x \n",
gFntCache->ulCurrentHlink, ulFastCheckSum, ulUFI));
}
}
else
{
ASSERTGDI(gflFntCacheState & FNT_CACHE_LOOKUP_MODE, "PutFNTCacheCheckSum: gflFntCacheState\n");
// During read mode, it still wants to write into fntcache.dat
// Then we will rebuild it at next boot time.
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
}
}
/*****************************************************************************
* void FntCacheCreateHLink
*
* Build hash link in hash table
*
* History
* 4-3-98 Yung-Jen Tony Tsai [YungT]
* Wrote it.
*****************************************************************************/
BOOL bFntCacheCreateHLink(ULONG ulFastCheckSum)
{
ULONG ulHashBucket;
FNTHLINK *pCurHlink;
DWORD iNextLink;
ulHashBucket = ulFastCheckSum % FNTCACHE_MAX_BUCKETS;
iNextLink = gFntCache->pTable->aiBuckets[ulHashBucket];
if (iNextLink != FNTINDEX_INVALID)
{
if (iNextLink > gFntCache->pTable->ulMaxFonts)
return FALSE;
pCurHlink = &gFntCache->pTable->ahlnk[iNextLink];
// We put the new link at the end so that the search time is faster
while (pCurHlink->iNext != FNTINDEX_INVALID)
{
if (pCurHlink->iNext > gFntCache->pTable->ulMaxFonts)
{
gFntCache->flThisBoot |= FNT_CACHE_STATE_FULL;
return FALSE;
}
pCurHlink = &gFntCache->pTable->ahlnk[pCurHlink->iNext];
FNT_KdBreakPoint(FNTCACHE_DBG_LEVEL_0, ("Current iNextLink %x\n", pCurHlink->iNext));
}
pCurHlink->iNext = gFntCache->ulCurrentHlink;
}
else
{
// put it at the head of the linked list
gFntCache->pTable->aiBuckets[ulHashBucket] = gFntCache->ulCurrentHlink;
FNT_KdBreakPoint(FNTCACHE_DBG_LEVEL_0, ("Put on HashBuckets %x\n", ulHashBucket));
}
return TRUE;
}