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.
 
 
 
 
 
 

2731 lines
81 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
infcache.c
Abstract:
INF Cache management functions
Author:
Jamie Hunter (jamiehun) Jan-27-2000
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define BOUNDSCHECK(base,size,limit) ( \
((base)<=(limit)) && \
((size)<=(limit)) && \
((base+size)<=(limit)) && \
((base+size)>=(base)))
//
// Macros used to quadword-align Cache blocks.
//
#define CACHE_ALIGNMENT ((DWORD)8)
#define CACHE_ALIGN_MASK (~(DWORD)(CACHE_ALIGNMENT - 1))
#define CACHE_ALIGN_BLOCK(x) ((x & CACHE_ALIGN_MASK) + ((x & ~CACHE_ALIGN_MASK) ? CACHE_ALIGNMENT : 0))
BOOL
AlignForNextBlock(
IN HANDLE hFile,
IN DWORD ByteCount
);
#ifdef UNICODE
VOID InfCacheFreeCache(
IN PINFCACHE pInfCache
)
/*++
Routine Description:
Delete/Release in-memory image of INF CACHE
Arguments:
pInfCache - pointer to Run-Time data
Return Value:
none
--*/
{
if (pInfCache == NULL) {
//
// no cache
//
return;
}
if(pInfCache->bReadOnly && pInfCache->BaseAddress) {
//
// we're looking at memory mapped cache
//
pStringTableDestroy(pInfCache->pMatchTable);
pStringTableDestroy(pInfCache->pInfTable);
pSetupUnmapAndCloseFile(pInfCache->FileHandle, pInfCache->MappingHandle, pInfCache->BaseAddress);
} else {
//
// if cache is writable, this data is transient
//
if(pInfCache->pHeader) {
MyFree(pInfCache->pHeader);
}
if(pInfCache->pMatchTable) {
pStringTableDestroy(pInfCache->pMatchTable);
}
if(pInfCache->pInfTable) {
pStringTableDestroy(pInfCache->pInfTable);
}
if(pInfCache->pListTable) {
MyFree(pInfCache->pListTable);
}
}
//
// transient information
//
if(pInfCache->pSearchTable) {
pStringTableDestroy(pInfCache->pSearchTable);
}
MyFree(pInfCache);
}
#endif
#ifdef UNICODE
PINFCACHE InfCacheCreateNewCache(
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
Create new empty cache
Arguments:
LogContext - for logging
Return Value:
pointer to Run-Time header if succeeded, NULL if out of memory.
--*/
{
PINFCACHE pInfCache = (PINFCACHE)MyMalloc(sizeof(INFCACHE));
if(pInfCache == NULL) {
return NULL;
}
ZeroMemory(pInfCache,sizeof(INFCACHE));
//
// set initial state
//
pInfCache->BaseAddress = NULL;
pInfCache->bReadOnly = FALSE;
pInfCache->bDirty = TRUE;
pInfCache->bNoWriteBack = FALSE;
//
// create transient data
//
pInfCache->pHeader = (PCACHEHEADER)MyMalloc(sizeof(CACHEHEADER));
if(pInfCache->pHeader == NULL) {
goto cleanup;
}
pInfCache->pMatchTable = pStringTableInitialize(sizeof(CACHEMATCHENTRY));
if(pInfCache->pMatchTable == NULL) {
goto cleanup;
}
pInfCache->pInfTable = pStringTableInitialize(sizeof(CACHEINFENTRY));
if(pInfCache->pInfTable == NULL) {
goto cleanup;
}
pInfCache->pHeader->Version = INFCACHE_VERSION;
pInfCache->pHeader->Locale = GetThreadLocale();
pInfCache->pHeader->Flags = 0;
pInfCache->pHeader->FileSize = 0; // in memory image
pInfCache->pHeader->MatchTableOffset = 0; // in memory image
pInfCache->pHeader->MatchTableSize = 0; // in memory image
pInfCache->pHeader->InfTableOffset = 0; // in memory image
pInfCache->pHeader->InfTableSize = 0; // in memory image
pInfCache->pHeader->ListDataOffset = 0; // in memory image
pInfCache->pHeader->ListDataCount = 1; // initial size (count the free list node)
pInfCache->ListDataAlloc = 32768; // initial size of allocation
pInfCache->pListTable = (PCACHELISTENTRY)MyMalloc(sizeof(CACHELISTENTRY)*pInfCache->ListDataAlloc);
if(pInfCache->pListTable == NULL) {
goto cleanup;
}
//
// initialize free-list to empty (even though we allocated enough space, we don't commit it until needed)
//
pInfCache->pListTable[0].Value = 0; // how many free entries
pInfCache->pListTable[0].Next = 0;
//
// search table
//
pInfCache->pSearchTable = pStringTableInitialize(sizeof(CACHEHITENTRY));
if(pInfCache->pSearchTable == NULL) {
goto cleanup;
}
WriteLogEntry(LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_USING_NEW_INF_CACHE,
NULL
);
return pInfCache;
cleanup:
InfCacheFreeCache(pInfCache);
return NULL;
}
#endif
#ifdef UNICODE
DWORD MarkForDelete(
IN LPCTSTR FilePath
)
/*++
Routine Description:
Special delete operation that will open the file with required access
mark it as needs deleting
and then close it again
Arguments:
FilePath - name of file to delete
Return Value:
Success status
--*/
{
TCHAR TmpFilePath[MAX_PATH*2];
PTSTR FileName;
HANDLE hFile;
int c;
hFile = CreateFile(FilePath,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL
);
if(hFile == INVALID_HANDLE_VALUE) {
//
// this can fail for various reasons
//
if(GetLastError() == ERROR_FILE_NOT_FOUND) {
return NO_ERROR;
}
return GetLastError();
}
//
// rename file to a temporary one for period that file remains alive
//
lstrcpyn(TmpFilePath,FilePath,MAX_PATH);
FileName = (PTSTR)pSetupGetFileTitle(TmpFilePath);
for(c=0;c<1000;c++) {
_stprintf(FileName,OLDCACHE_NAME_TEMPLATE,c);
if (MoveFile(FilePath,TmpFilePath)) {
break;
}
if (GetLastError() != ERROR_FILE_EXISTS) {
MYASSERT(GetLastError() == ERROR_FILE_EXISTS);
break;
}
}
MYASSERT(c<1000);
//
// ok, done (file may go away as soon as we close handle)
//
CloseHandle(hFile);
return NO_ERROR;
}
#endif
#ifdef UNICODE
DWORD InfCacheGetFileNames(
IN LPCTSTR InfDirectory,
OUT TCHAR InfPath[3][MAX_PATH]
)
{
TCHAR InfName[MAX_PATH];
int c;
for(c=0;c<3;c++) {
lstrcpyn(InfPath[c],InfDirectory,MAX_PATH);
_stprintf(InfName,INFCACHE_NAME_TEMPLATE,c);
if(!pSetupConcatenatePaths(InfPath[c],InfName,MAX_PATH,NULL)) {
//
// filename too big, fall into default search mode (we'll never be able to save the cache)
//
return ERROR_BAD_PATHNAME;
}
}
return NO_ERROR;
}
#endif
#ifdef UNICODE
PINFCACHE InfCacheLoadCache(
IN LPCTSTR InfDirectory,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
Retrieves cache for INF directory, if any.
We'll try
1) INFCACHE.1 (if it's a bad file, we'll rename to OLDCACHE.xxx)
2) INFCACHE.2 (ditto)
if we can't open a either of them, we'll skip over. We'll only have
both if something happened during write (such as reboot)
the OLDCACHE.xxx will be deleted when last handle to it is closed
we attempt to return (1) existing cache, (2) empty cache, (3) NULL
Arguments:
InfDirectory - directory to find cache in
LogContext - for logging
Return Value:
pointer to Run-Time header if succeeded, NULL if fatal error (go into default search mode)
--*/
{
//
// currently only support for unicode setupapi
//
TCHAR InfPath[3][MAX_PATH];
int c;
DWORD FileSize;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
HANDLE MappingHandle = NULL;
PVOID BaseAddress = NULL;
PCACHEHEADER pHeader = NULL;
PINFCACHE pInfCache = NULL;
DWORD Err;
MYASSERT(InfDirectory);
if ((Err = InfCacheGetFileNames(InfDirectory,InfPath))!=NO_ERROR) {
return NULL;
}
//
// look at INFCACHE.1 (primary) INFCACHE.2 (backup)
//
for(c=1;c<3;c++) {
//
// try and map this file into memory
//
//
FileHandle = CreateFile(
InfPath[c],
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_DELETE, // need delete permission
NULL,
OPEN_EXISTING,
0,
NULL
);
if(FileHandle == INVALID_HANDLE_VALUE) {
continue; // this file is locked, or doesn't exist
}
if((Err = pSetupMapFileForRead(FileHandle,&FileSize,&MappingHandle,&BaseAddress)) != NO_ERROR) {
//
// shouldn't happen on a good file, so clean it up
//
MarkForDelete(InfPath[c]);
continue;
}
if(FileSize >= sizeof(CACHEHEADER)) {
pHeader = (PCACHEHEADER)BaseAddress;
if(pHeader->Version == INFCACHE_VERSION &&
pHeader->FileSize <= FileSize &&
BOUNDSCHECK(pHeader->MatchTableOffset,pHeader->MatchTableSize,pHeader->FileSize) &&
BOUNDSCHECK(pHeader->InfTableOffset,pHeader->InfTableSize,pHeader->FileSize) &&
BOUNDSCHECK(pHeader->ListDataOffset,(pHeader->ListDataCount*sizeof(CACHELISTENTRY)),pHeader->FileSize) &&
(pHeader->MatchTableOffset >= sizeof(CACHEHEADER)) &&
(pHeader->InfTableOffset >= pHeader->MatchTableOffset+pHeader->MatchTableSize) &&
(pHeader->ListDataOffset >= pHeader->InfTableOffset+pHeader->InfTableSize)) {
//
// we're reasonably happy this file is valid
//
break;
}
}
//
// bad file (we was able to open file for reading, so it wasn't locked for writing)
//
MarkForDelete(InfPath[c]);
pSetupUnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
}
switch(c) {
case 0: // obtained primary file
case 1: // obtained secondary file
case 2: // obtained backup file, don't move as secondary may exist as locked
WriteLogEntry(LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_USING_INF_CACHE,
NULL,
InfPath[c]);
break;
case 3: // obtained no file
return InfCacheCreateNewCache(LogContext);
default: // whoops?
MYASSERT(FALSE);
return InfCacheCreateNewCache(LogContext);
}
//
// if we get here, we have a mapped file
//
MYASSERT(BaseAddress);
pInfCache = (PINFCACHE)MyMalloc(sizeof(INFCACHE));
if(pInfCache == NULL) {
pSetupUnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
return NULL;
}
ZeroMemory(pInfCache,sizeof(INFCACHE));
//
// set initial state
//
pInfCache->FileHandle = FileHandle;
pInfCache->MappingHandle = MappingHandle;
pInfCache->BaseAddress = BaseAddress;
pInfCache->bReadOnly = TRUE;
pInfCache->bDirty = FALSE;
pInfCache->bNoWriteBack = FALSE;
//
// make Runtime-Data point to relevent data structures
//
pInfCache->pHeader = pHeader;
//
// note that InitializeStringTableFromMemoryMappedFile creates a header that must be
// released by MyFree instead of pSetupStringTableDestroy
//
pInfCache->pMatchTable = InitializeStringTableFromMemoryMappedFile(
(PBYTE)BaseAddress+pHeader->MatchTableOffset,
pInfCache->pHeader->MatchTableSize,
pInfCache->pHeader->Locale,
sizeof(CACHEMATCHENTRY)
);
pInfCache->pInfTable = InitializeStringTableFromMemoryMappedFile(
(PBYTE)BaseAddress+pHeader->InfTableOffset,
pInfCache->pHeader->InfTableSize,
pInfCache->pHeader->Locale,
sizeof(CACHEINFENTRY)
);
pInfCache->pListTable = (PCACHELISTENTRY)((PBYTE)BaseAddress+pHeader->ListDataOffset);
pInfCache->ListDataAlloc = 0; // initial size of allocation (0 since this is static)
//
// search table - transient and empty
//
pInfCache->pSearchTable = pStringTableInitialize(sizeof(CACHEHITENTRY));
//
// ok, now did all memory allocations succeed?
//
if(pInfCache->pMatchTable==NULL || pInfCache->pInfTable==NULL || pInfCache->pSearchTable==NULL) {
InfCacheFreeCache(pInfCache);
return NULL;
}
return pInfCache;
//
// for ANSI version, just use default search mode (for now)
//
return NULL;
}
#endif
#ifdef UNICODE
DWORD InfCacheMakeWritable(
IN OUT PINFCACHE pInfCache
)
/*++
Routine Description:
Modifies InfCache in such a way that it can be written to
This is expensive as all previously memory-mapped data must be copied into memory
Arguments:
pInfCache - the cache we want to make writable
Return Value:
status, typically NO_ERROR
--*/
{
PCACHEHEADER pNewCacheHeader = NULL;
PVOID pNewMatchTable = NULL;
PVOID pNewInfTable = NULL;
PCACHELISTENTRY pNewListTable = NULL;
ULONG ListAllocSize = 0;
if(pInfCache == NULL || !pInfCache->bReadOnly) {
//
// not a cache we can/need to modify
//
return NO_ERROR;
}
if (pInfCache->bNoWriteBack) {
//
// we've already attempted this once, cache now invalid
//
return ERROR_INVALID_DATA;
}
MYASSERT(pInfCache->BaseAddress);
MYASSERT(!pInfCache->bDirty);
//
// allocatable data we need to duplicate is
// CACHEHEADER
// MatchStringTable
// InfStringTable
// DataList
//
pNewCacheHeader = (PCACHEHEADER)MyMalloc(sizeof(CACHEHEADER));
if(pNewCacheHeader == NULL) {
goto cleanup;
}
ZeroMemory(pNewCacheHeader,sizeof(CACHEHEADER));
pNewCacheHeader->FileSize = 0;
pNewCacheHeader->Flags = 0;
pNewCacheHeader->InfTableOffset = 0;
pNewCacheHeader->InfTableSize = 0;
pNewCacheHeader->ListDataCount = pInfCache->pHeader->ListDataCount;
pNewCacheHeader->Locale = pInfCache->pHeader->Locale;
pNewCacheHeader->Version = INFCACHE_VERSION;
pNewMatchTable = pStringTableDuplicate(pInfCache->pMatchTable);
if(pNewMatchTable == NULL) {
goto cleanup;
}
pNewInfTable = pStringTableDuplicate(pInfCache->pInfTable);
if(pNewInfTable == NULL) {
goto cleanup;
}
ListAllocSize = pNewCacheHeader->ListDataCount + 32768;
pNewListTable = (PCACHELISTENTRY)MyMalloc(sizeof(CACHELISTENTRY)*ListAllocSize);
if(pNewListTable == NULL) {
goto cleanup;
}
//
// copy the table
//
CopyMemory(pNewListTable,pInfCache->pListTable,pNewCacheHeader->ListDataCount*sizeof(CACHELISTENTRY));
//
// ok, now commit - delete & replace old data
//
pStringTableDestroy(pInfCache->pMatchTable);
pStringTableDestroy(pInfCache->pInfTable);
pSetupUnmapAndCloseFile(pInfCache->FileHandle, pInfCache->MappingHandle, pInfCache->BaseAddress);
pInfCache->FileHandle = INVALID_HANDLE_VALUE;
pInfCache->MappingHandle = NULL;
pInfCache->BaseAddress = NULL;
pInfCache->bReadOnly = FALSE;
pInfCache->pHeader = pNewCacheHeader;
pInfCache->pMatchTable = pNewMatchTable;
pInfCache->pInfTable = pNewInfTable;
pInfCache->pListTable = pNewListTable;
pInfCache->ListDataAlloc = ListAllocSize;
return NO_ERROR;
cleanup:
//
// we don't have enough memory to duplicate
//
if(pNewCacheHeader) {
MyFree(pNewCacheHeader);
}
if(pNewMatchTable) {
pStringTableDestroy(pNewMatchTable);
}
if(pNewInfTable) {
pStringTableDestroy(pNewInfTable);
}
if(pNewListTable) {
MyFree(pNewListTable);
}
return ERROR_NOT_ENOUGH_MEMORY;
}
#endif
#ifdef UNICODE
DWORD InfCacheWriteCache(
IN LPCTSTR InfDirectory,
IN OUT PINFCACHE pInfCache,
IN PSETUP_LOG_CONTEXT LogContext
)
/*++
Routine Description:
Writes a cache file to the INF directory being searched
Worst case scenario is we abandon the write, possibly leaving
a file turd around (until next inf search)
Typically,
1) we'll write to INFCACHE.0
2) we'll rename INFCACHE.2 to OLDCACHE.xxx (only if INFCACHE.1&2 exists)
3) we'll rename INFCACHE.1 to INFCACHE.2
4) we'll rename INFCACHE.0 to INFCACHE.1
5) we'll rename INFCACHE.1 to OLDCACHE.xxx
at stage 4, new callers may fail to open INFCACHE.1 and attempt INFCACHE.2
OLDCACHE.xxx will be deleted when last handle to it is closed
Arguments:
pInfCache - the cache we want to make writable
Return Value:
status, typically NO_ERROR
--*/
{
HANDLE hFile;
HANDLE hFile2;
TCHAR InfPath[3][MAX_PATH];
DWORD Offset;
DWORD BytesWritten;
PVOID MatchTableBlock;
PVOID InfTableBlock;
DWORD Err;
DWORD CacheIndex = 0;
//
// don't bother writing it if we don't have to
//
if(pInfCache->bNoWriteBack) {
return ERROR_INVALID_DATA;
}
if(!pInfCache->bDirty || pInfCache->bReadOnly) {
return NO_ERROR;
}
MYASSERT(InfDirectory);
if ((Err = InfCacheGetFileNames(InfDirectory,InfPath))!=NO_ERROR) {
return Err;
}
//
// attempt to open the temporary file for writing
//
hFile = CreateFile(InfPath[0],
GENERIC_WRITE,
FILE_SHARE_DELETE, // exclusive, but can be deleted/renamed
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile == INVALID_HANDLE_VALUE) {
//
// this will fail if we're non-admin, or we're already writing the cache
//
return GetLastError();
}
//
// align past header
//
Offset = CACHE_ALIGN_BLOCK(sizeof(CACHEHEADER));
MYASSERT(Offset>=sizeof(CACHEHEADER));
//
// get information about MatchTable
//
pInfCache->pHeader->MatchTableOffset = Offset;
pInfCache->pHeader->MatchTableSize = pStringTableGetDataBlock(pInfCache->pMatchTable, &MatchTableBlock);
Offset += CACHE_ALIGN_BLOCK(pInfCache->pHeader->MatchTableSize);
MYASSERT(Offset>=pInfCache->pHeader->MatchTableOffset+pInfCache->pHeader->MatchTableSize);
//
// get information about InfTable
//
pInfCache->pHeader->InfTableOffset = Offset;
pInfCache->pHeader->InfTableSize = pStringTableGetDataBlock(pInfCache->pInfTable, &InfTableBlock);
Offset += CACHE_ALIGN_BLOCK(pInfCache->pHeader->InfTableSize);
MYASSERT(Offset>=pInfCache->pHeader->InfTableOffset+pInfCache->pHeader->InfTableSize);
//
// get information about ListData
//
pInfCache->pHeader->ListDataOffset = Offset;
Offset += CACHE_ALIGN_BLOCK((pInfCache->pHeader->ListDataCount*sizeof(CACHELISTENTRY)));
MYASSERT(Offset>=pInfCache->pHeader->ListDataOffset+pInfCache->pHeader->ListDataCount*sizeof(CACHELISTENTRY));
//
// size of file now computed
//
pInfCache->pHeader->FileSize = Offset;
//
// write the file out
//
Offset = 0;
//
// cache header
//
if(!WriteFile(hFile, pInfCache->pHeader, sizeof(CACHEHEADER), &BytesWritten, NULL)) {
Err = GetLastError();
goto clean;
}
MYASSERT(BytesWritten == sizeof(CACHEHEADER));
Offset += BytesWritten;
//
// MatchTable
//
if(AlignForNextBlock(hFile, pInfCache->pHeader->MatchTableOffset - Offset)) {
Offset = pInfCache->pHeader->MatchTableOffset;
} else {
Err = GetLastError();
goto clean;
}
if(!WriteFile(hFile, MatchTableBlock, pInfCache->pHeader->MatchTableSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean;
}
MYASSERT(BytesWritten == pInfCache->pHeader->MatchTableSize);
Offset += BytesWritten;
//
// InfTable
//
if(AlignForNextBlock(hFile, pInfCache->pHeader->InfTableOffset - Offset)) {
Offset = pInfCache->pHeader->InfTableOffset;
} else {
Err = GetLastError();
goto clean;
}
if(!WriteFile(hFile, InfTableBlock, pInfCache->pHeader->InfTableSize, &BytesWritten, NULL)) {
Err = GetLastError();
goto clean;
}
MYASSERT(BytesWritten == pInfCache->pHeader->InfTableSize);
Offset += BytesWritten;
//
// ListData
//
if(AlignForNextBlock(hFile, pInfCache->pHeader->ListDataOffset - Offset)) {
Offset = pInfCache->pHeader->ListDataOffset;
} else {
Err = GetLastError();
goto clean;
}
if(!WriteFile(hFile, pInfCache->pListTable, pInfCache->pHeader->ListDataCount*sizeof(CACHELISTENTRY), &BytesWritten, NULL)) {
Err = GetLastError();
goto clean;
}
MYASSERT(BytesWritten == pInfCache->pHeader->ListDataCount*sizeof(CACHELISTENTRY));
Offset += BytesWritten;
//
// final padding
//
if(AlignForNextBlock(hFile, pInfCache->pHeader->FileSize - Offset)) {
Offset = pInfCache->pHeader->FileSize;
} else {
Err = GetLastError();
goto clean;
}
FlushFileBuffers(hFile);
//
// new cache written, do we need to shuffle primary to backup?
//
hFile2 = CreateFile(InfPath[1],
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_DELETE, // lock this file in place
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile2 != INVALID_HANDLE_VALUE) {
//
// ok, we have a primary, so back it up
// delete the old backup first
// once in place, any new opens will find it in backup position
// until we move and release new cache
//
MarkForDelete(InfPath[2]);
MoveFile(InfPath[1],InfPath[2]);
CloseHandle(hFile2);
}
//
// now attempt to move our cache
//
if(MoveFile(InfPath[0],InfPath[1])) {
CacheIndex = 1;
}
CloseHandle(hFile);
//
// new cache committed & ready for reading
// try not to leave turds around
//
MarkForDelete(InfPath[2]);
MarkForDelete(InfPath[0]);
pInfCache->bDirty = FALSE;
WriteLogEntry(LogContext,
CacheIndex ? DRIVER_LOG_INFO : DRIVER_LOG_ERROR,
CacheIndex ? MSG_LOG_MODIFIED_INF_CACHE : MSG_LOG_FAILED_MODIFY_INF_CACHE,
NULL,
InfPath[CacheIndex]);
return NO_ERROR;
clean:
//
// abandon the file
// delete first, we can do this since we opened the file shared-delete
// don't close before delete, otherwise we might delete a file someone else is writing
//
DeleteFile(InfPath[0]);
CloseHandle(hFile);
return Err;
}
#endif
#ifdef UNICODE
ULONG InfCacheAllocListEntry(
IN OUT PINFCACHE pInfCache,
IN LONG init
)
/*++
Routine Description:
Allocates a single list entry, initializing the datum to init
Side effect: if pInfCache not writable, it will be made writable
Side effect: if pInfCache not dirty, it will be marked dirty
Arguments:
pInfCache - the cache we're going to modify
init - initial value of datum
Return Value:
index of datum, or 0 on failure. (GetLastError indicates error)
--*/
{
DWORD status;
ULONG entry;
if(pInfCache->bReadOnly) {
status = InfCacheMakeWritable(pInfCache);
if(status != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
SetLastError(status);
return 0;
}
}
//
// query free-list
//
entry = pInfCache->pListTable[0].Next;
if(entry) {
//
// allocate from free list - reuse space
//
pInfCache->pListTable[0].Value--;
pInfCache->pListTable[0].Next = pInfCache->pListTable[entry].Next;
pInfCache->pListTable[entry].Value = init;
pInfCache->pListTable[entry].Next = 0;
pInfCache->bDirty = TRUE;
return entry;
}
if(pInfCache->pHeader->ListDataCount >= pInfCache->ListDataAlloc) {
//
// allocate some extra space
//
ULONG CountNewSpace;
PCACHELISTENTRY pNewSpace;
MYASSERT(pInfCache->ListDataAlloc);
CountNewSpace = pInfCache->ListDataAlloc*2;
pNewSpace = (PCACHELISTENTRY)MyRealloc(pInfCache->pListTable,sizeof(CACHELISTENTRY)*CountNewSpace);
if(pNewSpace == NULL) {
//
// ack!
//
pInfCache->bNoWriteBack = TRUE; // junk this cache at end
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
pInfCache->ListDataAlloc = CountNewSpace;
pInfCache->pListTable = pNewSpace;
}
//
// allocate from extra space
//
entry = pInfCache->pHeader->ListDataCount;
pInfCache->pHeader->ListDataCount++;
pInfCache->pListTable[entry].Value = init;
pInfCache->pListTable[entry].Next = 0;
pInfCache->bDirty = TRUE;
return entry;
}
#endif
#ifdef UNICODE
DWORD InfCacheFreeListEntry(
IN OUT PINFCACHE pInfCache,
IN ULONG entry
)
/*++
Routine Description:
Releases a single list entry (no modifications to prev/next links made)
Side effect: if pInfCache not writable, it will be made writable
Side effect: if pInfCache not dirty, it will be marked dirty
Arguments:
pInfCache - the cache we're going to modify
entry - list entry to add to free list
Return Value:
status, typically NO_ERROR
--*/
{
DWORD status;
if(entry == 0 || pInfCache == NULL || entry >= pInfCache->pHeader->ListDataCount) {
return ERROR_INVALID_DATA;
}
if(pInfCache->bReadOnly) {
status = InfCacheMakeWritable(pInfCache);
if(status != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
return status;
}
}
pInfCache->pListTable[entry].Value = -1;
pInfCache->pListTable[entry].Next = pInfCache->pListTable[0].Next;
pInfCache->pListTable[0].Next = entry;
pInfCache->pListTable[0].Value++;
pInfCache->bDirty = TRUE;
return NO_ERROR;
}
#endif
#ifdef UNICODE
DWORD InfCacheRemoveMatchRefToInf(
IN OUT PINFCACHE pInfCache,
IN LONG MatchEntry,
IN LONG InfEntry
)
/*++
Routine Description:
Removes details about a specific INF from specific HWID entry
assumptions: pInfCache already writable
Arguments:
pInfCache - the cache we're going to modify
MatchEntry - the Match list we need to remove INF from
InfEntry - the INF we need to remove from Match list
Return Value:
status, typically NO_ERROR
--*/
{
DWORD status;
CACHEMATCHENTRY matchdata;
ULONG parent_index;
ULONG index;
ULONG newindex;
BOOL head;
MYASSERT(pInfCache);
MYASSERT(!pInfCache->bReadOnly);
MYASSERT(MatchEntry);
MYASSERT(InfEntry);
if(!pStringTableGetExtraData(pInfCache->pMatchTable,MatchEntry,&matchdata,sizeof(matchdata))) {
MYASSERT(FALSE); // should not fail
}
parent_index = 0;
index = matchdata.InfList;
head = FALSE;
while (index) {
newindex = pInfCache->pListTable[index].Next;
if (pInfCache->pListTable[index].Value == InfEntry) {
//
// remove
//
pInfCache->bDirty = TRUE;
if(parent_index) {
pInfCache->pListTable[parent_index].Next = newindex;
} else {
matchdata.InfList = newindex;
head = TRUE;
}
status = InfCacheFreeListEntry(pInfCache,index);
if(status != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
return status;
}
} else {
parent_index = index;
}
index = newindex;
}
if (head) {
//
// we modified the head item
//
if(!pStringTableSetExtraData(pInfCache->pMatchTable,MatchEntry,&matchdata,sizeof(matchdata))) {
MYASSERT(FALSE); // should not fail
}
}
return NO_ERROR;
}
#endif
#ifdef UNICODE
DWORD InfCacheRemoveInf(
IN OUT PINFCACHE pInfCache,
IN LONG nHitEntry,
IN PCACHEINFENTRY inf_entry
)
/*++
Routine Description:
Removes details about a specific INF from cache
Side effect: if pInfCache not writable, it will be made writable
Side effect: if pInfCache not dirty, it will be marked dirty
Arguments:
pInfCache - the cache we're going to modify
nHitEntry - string id in Inf StringTable
inf_entry - structure obtained from Inf StringTable
Return Value:
status, typically NO_ERROR
--*/
{
DWORD status;
DWORD hwstatus;
ULONG parent_index;
CACHEINFENTRY dummy_entry;
MYASSERT(inf_entry); // later we may make this optional
MYASSERT(nHitEntry);
if(inf_entry->MatchList == CIE_INF_INVALID) {
//
// already showing as deleted
//
return NO_ERROR;
}
if(pInfCache == NULL || pInfCache->bNoWriteBack) {
return ERROR_INVALID_DATA;
}
if(pInfCache->bReadOnly) {
status = InfCacheMakeWritable(pInfCache);
if(status != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
return status;
}
}
pInfCache->bDirty = TRUE;
parent_index = inf_entry->MatchList;
//
// invalidate inf_entry
//
dummy_entry.MatchList = CIE_INF_INVALID;
dummy_entry.FileTime.dwLowDateTime = 0;
dummy_entry.FileTime.dwHighDateTime = 0;
dummy_entry.MatchFlags = CIEF_INF_NOTINF;
if(!pStringTableSetExtraData(pInfCache->pInfTable,nHitEntry,&dummy_entry,sizeof(dummy_entry))) {
MYASSERT(FALSE); // should not fail
}
//
// parse through and delete list of match ID's
//
hwstatus = NO_ERROR;
while(parent_index>0 && parent_index<pInfCache->pHeader->ListDataCount) {
LONG value = pInfCache->pListTable[parent_index].Value;
ULONG next = pInfCache->pListTable[parent_index].Next;
//
// free the list entry for re-use
//
status = InfCacheFreeListEntry(pInfCache,parent_index);
if(status != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
hwstatus = status;
}
parent_index = next;
//
// for each Match ID, delete all references to the INF
//
status = InfCacheRemoveMatchRefToInf(pInfCache,value,nHitEntry);
if(hwstatus == NO_ERROR) {
hwstatus = status;
}
}
return hwstatus;
}
#endif
#ifdef UNICODE
LONG InfCacheLookupInf(
IN OUT PINFCACHE pInfCache,
IN LPWIN32_FIND_DATA FindFileData,
OUT PCACHEINFENTRY inf_entry
)
/*++
Routine Description:
looks up the file as given by FindFileData to see if it's in the cache
If it's in the cache marked valid, and the date-stamp in cache is same as date-stamp of INF
then it's a 'HIT'
If it's in the cache marked valid, but date-stamp is wrong, then it's deleted and considered
a "MISS'
All other cases, consider it a 'MISS'
Arguments:
pInfCache - the cache we're using
FindFileData - information about a specific file we're looking at
inf_entry - structure obtained from Inf StringTable (if NULL, delete)
Return Value:
-1 for a 'MISS'. StringID of INF for a 'HIT'
inf_entry filled out with MatchList == CIE_INF_INVALID for a miss.
--*/
{
LONG i;
DWORD StringLength;
MYASSERT(pInfCache);
MYASSERT(FindFileData);
MYASSERT(inf_entry);
//
// determine if the cache entry of pInfCache is considered valid
//
i = pStringTableLookUpString(pInfCache->pInfTable,
FindFileData->cFileName,
&StringLength,
NULL, // hash value
NULL, // find context
STRTAB_CASE_INSENSITIVE,
inf_entry,
sizeof(CACHEINFENTRY));
if(i>=0 && inf_entry->MatchList != CIE_INF_INVALID) {
//
// cache hit (and matchlist is valid)
//
if(CompareFileTime(&inf_entry->FileTime,&FindFileData->ftLastWriteTime)==0) {
//
// valid cache hit
//
return i;
}
//
// cache out of date miss
// although we'll rebuild it later, let's use this opportunity to delete the entry
//
InfCacheRemoveInf(pInfCache,i,inf_entry);
}
//
// we're here because we have a miss, however fill in a new (empty) inf_entry
// MatchList set to CIE_INF_INVALID to indicate list is invalid and inf must be searched
//
inf_entry->FileTime = FindFileData->ftLastWriteTime;
inf_entry->MatchList = CIE_INF_INVALID;
inf_entry->MatchFlags = CIEF_INF_NOTINF;
return -1;
}
#endif
#ifdef UNICODE
ULONG InfCacheAddListTail(
IN OUT PINFCACHE pInfCache,
IN OUT PULONG head,
IN OUT PULONG tail,
IN LONG value
)
/*++
Routine Description:
Adds value to tail of a list where *tail is an entry in the list
Arguments:
pInfCache - cache to modify
head - head of list
tail - DataList entry
value - data to add
Return Value:
new entry position, 0 on error (GetLastError() returns error)
--*/
{
ULONG next;
ULONG first;
MYASSERT(pInfCache);
MYASSERT(head == NULL || head != tail);
if (tail) {
first = *tail;
} else if (head) {
first = *head;
} else {
MYASSERT(head || tail);
}
if (!first) {
next = InfCacheAllocListEntry(pInfCache,value);
if (!next) {
return 0;
}
if (head) {
*head = next;
}
} else {
//
// move head to last item in list
//
while(pInfCache->pListTable[first].Next) {
first = pInfCache->pListTable[first].Next;
}
next = InfCacheAllocListEntry(pInfCache,value);
if(!next) {
return 0;
}
pInfCache->pListTable[first].Next = next;
}
if(tail) {
*tail = next;
}
return next;
}
#endif
#ifdef UNICODE
LONG InfCacheAddMatchItem(
IN OUT PINFCACHE pInfCache,
IN LPCTSTR key,
IN LONG InfEntry
)
/*++
Routine Description:
Given an INF StringID (InfEntry) and match key,
obtain (and return) Match StringID
while also adding InfEntry to head of Match's INF list
(if not already at head)
Order is not particularly important, however adding to head is
quicker to do, and easier to reduce number of times we add inf
if match id is referenced multiple times
Arguments:
pInfCache - cache to modify
key - match string (buffer must be writable)
InfEntry - StringID
Return Value:
new entry in match table, -1 on error (GetLastError() returns error)
--*/
{
LONG MatchIndex;
CACHEMATCHENTRY matchentry;
DWORD StringLength;
MYASSERT(pInfCache);
MYASSERT(key);
MYASSERT(InfEntry>=0);
//
// if cache is invalid, we'll skip this as optimization
//
if(pInfCache->bNoWriteBack) {
SetLastError(ERROR_INVALID_DATA);
return -1;
}
MatchIndex = pStringTableLookUpString(pInfCache->pMatchTable,
(LPTSTR)key, // will not be modified
&StringLength,
NULL, // hash value
NULL, // find context
STRTAB_CASE_INSENSITIVE,
&matchentry,
sizeof(matchentry));
if(MatchIndex < 0) {
//
// entirely new entry
//
matchentry.InfList = InfCacheAllocListEntry(pInfCache,InfEntry);
if(matchentry.InfList == 0) {
return -1;
}
MatchIndex = pStringTableAddString(pInfCache->pMatchTable,
(LPTSTR)key, // will not be modified
STRTAB_CASE_INSENSITIVE|STRTAB_NEW_EXTRADATA,
&matchentry,
sizeof(matchentry));
if(MatchIndex<0) {
pInfCache->bNoWriteBack = TRUE;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return -1;
}
} else {
MYASSERT(matchentry.InfList<pInfCache->pHeader->ListDataCount);
//
// if we came across this same match earlier for this inf,
// the inf should still be at head of list
// the world doesn't come to an end though if we end up adding
// the inf twice - we check for this when deleting the inf
//
if (pInfCache->pListTable[matchentry.InfList].Value != InfEntry) {
ULONG newentry = InfCacheAllocListEntry(pInfCache,InfEntry);
if(newentry == 0) {
return -1;
}
pInfCache->pListTable[newentry].Next = matchentry.InfList;
matchentry.InfList = newentry;
if(!pStringTableSetExtraData(pInfCache->pMatchTable,MatchIndex,&matchentry,sizeof(matchentry))) {
MYASSERT(FALSE); // should not fail
}
}
}
return MatchIndex;
}
#endif
#ifdef UNICODE
LONG InfCacheAddInf(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN OUT PINFCACHE pInfCache,
IN LPWIN32_FIND_DATA FindFileData,
OUT PCACHEINFENTRY inf_entry,
IN PLOADED_INF pInf
)
/*++
Routine Description:
Called to add a newly discovered INF to the cache
If hINF is "bad", then we'll mark the INF as an exclusion
so we don't waste time with it in future
If we return with an error, caller knows that the INF
must be searched if it can be searched
Arguments:
LogContext - logging context for errors
pInfCache - cache to modify
FindFileData - contains name of INF and date-stamp
inf_entry - returns list of match ID's associated with INF
pINF - opened INF to add information about. NOTE: Either this INF must
be locked by the caller, or the INF cannot be accessed by any other
thread. THIS ROUTINE DOES NOT DO LOCKING ON THE INF.
Return Value:
InfEntry - -1 if error (GetLastError returns status)
--*/
{
//
// FindFileData contains name & date-stamp of INF
// we need to process all search information out of the INF
//
LONG nInfIndex = -1;
LONG nMatchId = -1;
ULONG last_list_entry = 0;
ULONG head_list_entry = 0;
DWORD Err = NO_ERROR;
PCTSTR MatchString;
GUID guid;
PINF_SECTION MfgListSection;
PINF_LINE MfgListLine;
UINT MfgListLineIndex;
PTSTR CurMfgSecName;
TCHAR CurMfgSecWithExt[MAX_SECT_NAME_LEN];
MYASSERT(pInfCache);
MYASSERT(FindFileData);
MYASSERT(inf_entry);
if (pInfCache->bNoWriteBack) {
//
// cache is already bad, so don't waste time updating it
// note though that the INF should be searched
//
return ERROR_INVALID_DATA;
}
if(pInfCache->bReadOnly) {
Err = InfCacheMakeWritable(pInfCache);
if(Err != NO_ERROR) {
pInfCache->bNoWriteBack = TRUE; // cache now invalid
return Err;
}
pInfCache->bDirty = TRUE;
}
//
// this stuff should be set up earlier
//
MYASSERT(inf_entry->MatchList == CIE_INF_INVALID);
MYASSERT(inf_entry->MatchFlags == CIEF_INF_NOTINF);
MYASSERT(inf_entry->FileTime.dwHighDateTime = FindFileData->ftLastWriteTime.dwHighDateTime);
MYASSERT(inf_entry->FileTime.dwLowDateTime = FindFileData->ftLastWriteTime.dwLowDateTime);
MYASSERT(!pInfCache->bReadOnly);
//
// we need the InfIndex before we start (also mark this as file physically exist)
//
inf_entry->MatchList = 0;
nInfIndex = pStringTableAddString(pInfCache->pInfTable,
FindFileData->cFileName,
STRTAB_CASE_INSENSITIVE|STRTAB_NEW_EXTRADATA,
inf_entry,
sizeof(CACHEINFENTRY));
if (nInfIndex<0) {
//
// ack, out of memory
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
if(pInf) {
if(pInf->Style == INF_STYLE_WIN4) {
inf_entry->MatchFlags |= CIEF_INF_WIN4;
if(pInf->InfSourceMediaType == SPOST_URL) {
inf_entry->MatchFlags |= CIEF_INF_URL;
}
//
// for a Win4 style INF, we're going to add Class GUID & Class Name to the
// pool of ID's we can match on
// note that if one or the other is missing, we don't lookup here as registry
// information could change
// we do the cross-lookups at the time of the INF search
//
if((MatchString = pSetupGetVersionDatum(&pInf->VersionBlock, pszClassGuid))!=NULL) {
//
// we found a class GUID
//
inf_entry->MatchFlags |= CIEF_INF_CLASSGUID;
nMatchId = InfCacheAddMatchItem(pInfCache,MatchString,nInfIndex);
if (nMatchId<0) {
Err = GetLastError();
goto cleanup;
}
if (!InfCacheAddListTail(pInfCache,&head_list_entry,&last_list_entry,nMatchId)) {
Err = GetLastError();
goto cleanup;
}
//
// check out a special case {0}
//
if(pSetupGuidFromString(MatchString, &guid) == NO_ERROR && pSetupIsGuidNull(&guid)) {
inf_entry->MatchFlags |= CIEF_INF_NULLGUID;
}
}
if((MatchString = pSetupGetVersionDatum(&pInf->VersionBlock, pszClass))!=NULL) {
//
// we found a class name
//
inf_entry->MatchFlags |= CIEF_INF_CLASSNAME;
nMatchId = InfCacheAddMatchItem(pInfCache,MatchString,nInfIndex);
if (nMatchId<0) {
Err = GetLastError();
goto cleanup;
}
if (!InfCacheAddListTail(pInfCache,&head_list_entry,&last_list_entry,nMatchId)) {
Err = GetLastError();
goto cleanup;
}
}
//
// enumerate all manufacturers
//
if((MfgListSection = InfLocateSection(pInf, pszManufacturer, NULL)) &&
MfgListSection->LineCount) {
//
// We have a [Manufacturer] section and there is at least one
// line within it.
//
inf_entry->MatchFlags |= CIEF_INF_MANUFACTURER;
for(MfgListLineIndex = 0;
InfLocateLine(pInf, MfgListSection, NULL, &MfgListLineIndex, &MfgListLine);
MfgListLineIndex++) {
//
// Make sure the current line is one of these valid forms:
//
// MfgDisplayNameAndModelsSection
// MfgDisplayName = MfgModelsSection [,TargetDecoration...]
//
if(!ISSEARCHABLE(MfgListLine)) {
//
// We have a line with multiple fields but no key--skip
// it.
//
continue;
}
if(CurMfgSecName = InfGetField(pInf, MfgListLine, 1, NULL)) {
INFCONTEXT device;
//
// Check to see if there is an applicable
// TargetDecoration entry for this manufacturer's
// models section (if so, the models section name will
// be appended with that decoration).
//
if(GetDecoratedModelsSection(LogContext,
pInf,
MfgListLine,
NULL,
CurMfgSecWithExt)) {
//
// From here on, use the decorated models section...
//
CurMfgSecName = CurMfgSecWithExt;
}
if(SetupFindFirstLine(pInf, CurMfgSecName, NULL, &device)) {
do {
TCHAR devname[LINE_LEN];
DWORD devindex;
DWORD fields = SetupGetFieldCount(&device);
//
// for a device line, field 1 = section, field 2+ = match keys
//
for(devindex=2;devindex<=fields;devindex++) {
if(SetupGetStringField(&device,devindex,devname,LINE_LEN,NULL)) {
//
// finally, a hit key to add
//
nMatchId = InfCacheAddMatchItem(pInfCache,devname,nInfIndex);
if(nMatchId<0) {
Err = GetLastError();
goto cleanup;
}
if (!InfCacheAddListTail(pInfCache,&head_list_entry,&last_list_entry,nMatchId)) {
Err = GetLastError();
goto cleanup;
}
}
}
} while(SetupFindNextLine(&device,&device));
}
}
}
}
} else if (pInf->Style == INF_STYLE_OLDNT) {
//
// for an OLDNT style INF, we'll add Legacy class name to the pool of ID's
// we can match on
//
inf_entry->MatchFlags |= CIEF_INF_OLDNT;
if((MatchString = pSetupGetVersionDatum(&pInf->VersionBlock, pszClass))!=NULL) {
//
// we found a (legacy) class name
//
inf_entry->MatchFlags |= CIEF_INF_CLASSNAME;
nMatchId = InfCacheAddMatchItem(pInfCache,MatchString,nInfIndex);
if (nMatchId<0) {
Err = GetLastError();
goto cleanup;
}
if (!InfCacheAddListTail(pInfCache,&head_list_entry,&last_list_entry,nMatchId)) {
Err = GetLastError();
goto cleanup;
}
}
} else {
MYASSERT(FALSE);
}
}
//
// now re-write the inf data with new flags & match patterns
//
inf_entry->MatchList = head_list_entry;
if(!pStringTableSetExtraData(pInfCache->pInfTable,nInfIndex,inf_entry,sizeof(CACHEINFENTRY))) {
MYASSERT(FALSE); // should not fail
}
return nInfIndex;
cleanup:
pInfCache->bNoWriteBack = TRUE;
MYASSERT(Err);
SetLastError(Err);
return -1;
}
#endif
#ifdef UNICODE
LONG InfCacheSearchTableLookup(
IN OUT PINFCACHE pInfCache,
IN PCTSTR filename,
IN OUT PCACHEHITENTRY hitstats)
/*++
Routine Description:
Looks up filename in the search table, returning search information
if filename was not already in search table, it's added
Arguments:
pInfCache - cache to modify
filename - file to obtain hit-entry information for
hitstats - information obtained (such as if this files been processed etc)
Return Value:
index in search table (-1 if error)
--*/
{
LONG nHitIndex;
DWORD StringLength;
MYASSERT(pInfCache);
MYASSERT(filename);
MYASSERT(hitstats);
nHitIndex = pStringTableLookUpString(pInfCache->pSearchTable,
(PTSTR)filename, // filename wont be changed
&StringLength,
NULL, // hash value
NULL, // find context
STRTAB_CASE_INSENSITIVE,
hitstats,
sizeof(CACHEHITENTRY));
if(nHitIndex < 0) {
//
// entirely new entry (hitstats expected to have a value)
//
nHitIndex = pStringTableAddString(pInfCache->pSearchTable,
(PTSTR)filename, // filename wont be changed
STRTAB_CASE_INSENSITIVE|STRTAB_NEW_EXTRADATA,
hitstats,
sizeof(CACHEHITENTRY));
if (nHitIndex<0) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return -1;
}
}
return nHitIndex;
}
#endif
#ifdef UNICODE
ULONG InfCacheSearchTableSetFlags(
IN OUT PINFCACHE pInfCache,
IN PCTSTR filename,
IN ULONG setflags,
IN ULONG clrflags
)
/*++
Routine Description:
Modifies flags associated with a filename
Arguments:
pInfCache - cache to modify
filename - file to obtain hit-entry information for
setflags - flags to set
clrflags - flags to clear
Return Value:
combined flags, or (ULONG)(-1) on error.
--*/
{
CACHEHITENTRY searchentry;
LONG nHitIndex;
ULONG flags;
MYASSERT(pInfCache);
MYASSERT(filename);
searchentry.Flags = setflags; // prime in case of new entry
nHitIndex = InfCacheSearchTableLookup(pInfCache,filename,&searchentry);
if(nHitIndex<0) {
return (ULONG)(-1);
}
flags = (searchentry.Flags&~clrflags) | setflags;
if (flags != searchentry.Flags) {
searchentry.Flags = flags;
if(!pStringTableSetExtraData(pInfCache->pSearchTable,nHitIndex,&searchentry,sizeof(searchentry))) {
MYASSERT(FALSE); // should not fail
}
}
return searchentry.Flags;
}
#endif
#ifdef UNICODE
DWORD InfCacheMarkMatchInfs(
IN OUT PINFCACHE pInfCache,
IN PCTSTR MatchString,
IN ULONG MatchFlag
)
/*++
Routine Description:
Called to iterate through the INF's associated with MatchString
and flag them using MatchFlag
Arguments:
pInfCache - cache to check (may modify search data)
MatchString - match string to include
MatchFlag - flag to set in all INF's associated with match string
Return Value:
status - typically NO_ERROR
--*/
{
LONG MatchIndex;
DWORD StringLength;
CACHEMATCHENTRY matchentry;
ULONG entry;
PTSTR InfName;
ULONG SearchFlags;
MYASSERT(pInfCache);
MYASSERT(MatchString);
MYASSERT(MatchFlag);
//
// find list of Inf's associated with match string
//
MatchIndex = pStringTableLookUpString(pInfCache->pMatchTable,
(PTSTR)MatchString, // it will not be modified
&StringLength,
NULL, // hash value
NULL, // find context
STRTAB_CASE_INSENSITIVE,
&matchentry,
sizeof(matchentry));
if(MatchIndex < 0) {
//
// no match
//
return NO_ERROR;
}
for(entry = matchentry.InfList ; entry > 0 && entry < pInfCache->pHeader->ListDataCount ; entry = pInfCache->pListTable[entry].Next) {
LONG InfEntry = pInfCache->pListTable[entry].Value;
//
// obtain name of Inf
//
InfName = pStringTableStringFromId(pInfCache->pInfTable,InfEntry);
SearchFlags = InfCacheSearchTableSetFlags(pInfCache,InfName,MatchFlag,0);
if(SearchFlags == (ULONG)(-1)) {
//
// failed - huh?
// abort into fail-safe pass
//
MYASSERT(SearchFlags != (ULONG)(-1));
return GetLastError();
}
}
return NO_ERROR;
}
#endif
#ifdef UNICODE
BOOL
InfCacheSearchEnum(
IN PVOID StringTable,
IN LONG StringId,
IN PCTSTR String,
IN PVOID ExtraData,
IN UINT ExtraDataSize,
IN LPARAM lParam
)
/*++
Routine Description:
Callback for Phase-3 of InfCacheSearchDirectory
Arguments:
StringTable - unused
StringId - unused
String - used to form INF name
ExtraData - points to flags
ExtraDataSize - unused
lParam - points to InfCacheEnumData
Return Value:
always TRUE unless out of memory condition
--*/
{
PINFCACHE_ENUMDATA enum_data = (PINFCACHE_ENUMDATA)lParam;
CACHEHITENTRY *hit_stats = (CACHEHITENTRY *)ExtraData;
PTSTR InfFullPath = NULL;
DWORD InfFullPathSize;
BOOL b;
WIN32_FIND_DATA FindData;
PLOADED_INF pInf = NULL;
UINT ErrorLineNumber;
BOOL PnfWasUsed;
BOOL cont = TRUE;
MYASSERT(ExtraDataSize == sizeof(CACHEHITENTRY));
MYASSERT(String);
MYASSERT(enum_data);
MYASSERT(hit_stats);
MYASSERT(enum_data->Requirement);
MYASSERT(enum_data->Callback);
//
// see if this is an INF of interest
//
if((hit_stats->Flags & enum_data->Requirement) == enum_data->Requirement) {
//
// this is a HIT
// we need to open HINF
//
InfFullPathSize = lstrlen(enum_data->InfDir)+MAX_PATH+2;
InfFullPath = MyMalloc(InfFullPathSize*sizeof(TCHAR));
if (!InfFullPath) {
return TRUE; // out of memory (does not abort search)
}
lstrcpy(InfFullPath,enum_data->InfDir);
pSetupConcatenatePaths(InfFullPath,String,InfFullPathSize,NULL);
if(b = FileExists(InfFullPath, &FindData)) {
if(LoadInfFile(InfFullPath,
&FindData,
INF_STYLE_WIN4 | INF_STYLE_OLDNT, // we've filtered this ourselves
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS | LDINF_FLAG_ALWAYS_TRY_PNF,
NULL,
NULL,
NULL,
NULL,
enum_data->LogContext,
&pInf,
&ErrorLineNumber,
&PnfWasUsed) != NO_ERROR) {
pInf = NULL;
WriteLogEntry(
enum_data->LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_COULD_NOT_LOAD_HIT_INF,
NULL,
InfFullPath);
}
} else {
pInf = NULL;
}
if (pInf) {
cont = enum_data->Callback(enum_data->LogContext,InfFullPath,pInf,PnfWasUsed,enum_data->Context);
if(!cont) {
enum_data->ExitStatus = GetLastError();
MYASSERT(enum_data->ExitStatus);
}
FreeInfFile(pInf);
}
}
if (InfFullPath) {
MyFree(InfFullPath);
}
return cont;
}
#endif
DWORD InfCacheSearchDirectory(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN DWORD Action,
IN PCTSTR InfDir,
IN InfCacheCallback Callback, OPTIONAL
IN PVOID Context, OPTIONAL
IN PCTSTR ClassIdList, OPTIONAL
IN PCTSTR HwIdList OPTIONAL
)
/*++
Routine Description:
Main workhorse of the InfCache
Search a single specified directory
calling Callback(hInf,Context) for each inf that has a likelyhood of matching
note that Action flags, ClassId and HwIdList are hints, and Callback may get
called for any/all INF's Callback must re-check for all search criteria.
Searching is done in 3 phases
Phase 1: Parse all INF's in directory. If an INF is not in cache, *almost always* call
callback. If special inclusions (OLD INF's INF's with no Class GUID's) specified, then
INF's that match the special criteria are processed here (since they can't be processed
in Phase 2). All INF's that exist, haven't been processed and haven't been excluded get
marked with CHE_FLAGS_PENDING ready for phase-2
Phase 2: Process ClassId and HwIdList matching, setting CHE_FLAGS_GUIDMATCH and
CHE_FLAGS_IDMATCH apropriately in matching INF's and search criteria flags. Search
criteria will either be:
CHE_FLAGS_PENDING - callback on all Win4 style INF's
CHE_FLAGS_PENDING | CHE_FLAGS_GUIDMATCH - callback on all Win4 INF's that have matching class
CHE_FLAGS_PENDING | CHE_FLAGS_IDMATCH - wildcard class, matching hardware ID's
CHE_FLAGS_PENDING | CHE_FLAGS_GUIDMATCH | CHE_FLAGS_IDMATCH - most specific match
Phase 3: enumerate through all INF's that we've marked with exact same flags as
search criteria, and call callback on those INF's
Using this search method, Win4 INF's that are in the cache are always processed last.
Arguments:
LogContext - for logging
InfDir - single directory to search
Callback - function to call on a likely match
Context - parameter to pass to function
ClassIdList (optional) - multi-sz list of class id's (typically guid, name and legacy name)
HwIdList (optional)- multi-sz list of hardware id's
Return Value:
status, typically NO_ERROR
--*/
{
PINFCACHE pInfCache = NULL;
PTSTR InfPath = NULL;
UINT PathSize;
DWORD Err = NO_ERROR;
WIN32_FIND_DATA FindFileData;
HANDLE FindHandle = INVALID_HANDLE_VALUE;
BOOL bNoWriteBack = FALSE;
LONG InfId;
LONG SearchId;
ULONG SearchFlags;
CACHEINFENTRY inf_entry;
CACHEHITENTRY hit_stats;
ULONG ReqFlags;
INFCACHE_ENUMDATA enum_data;
PSETUP_LOG_CONTEXT LocalLogContext = NULL;
BOOL TryPnf = FALSE;
BOOL TryCache = FALSE;
FILETIME FileTimeNow;
FILETIME FileTimeBefore;
FILETIME FileTimeAfter;
MYASSERT(InfDir);
//
// obtain cache for directory of interest
// we should be able to handle a NULL return
// note that caller can either treat these two bits
// as an operation (0-3) or as bitmaps
// the result would be the same
//
if(!LogContext) {
if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
LogContext = LocalLogContext;
} else {
LocalLogContext = NULL;
Err = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
}
WriteLogEntry(
LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_ENUMERATING_FILES,
NULL,
InfDir);
PathSize = lstrlen(InfDir)+10;
InfPath = MyMalloc(PathSize*sizeof(TCHAR));
if(!InfPath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
lstrcpy(InfPath,InfDir);
pSetupConcatenatePaths(InfPath,INFCACHE_INF_WILDCARD,PathSize,NULL);
#ifdef UNICODE
if (pSetupInfIsFromOemLocation(InfPath,FALSE)) {
if (Action & INFCACHE_FORCE_CACHE) {
TryCache = TRUE;
}
if (Action & INFCACHE_FORCE_PNF) {
TryPnf = TRUE;
}
} else {
TryPnf = TRUE;
//
// Try using INF cache unless we're doing an INFCACHE_ENUMALL
//
if((Action & INFCACHE_ACTIONBITS) != INFCACHE_ENUMALL) {
TryCache = TRUE;
}
}
if (!TryCache) {
//
// directory is not in our default search path
// treat as INFCACHE_ENUMALL
//
pInfCache = NULL;
} else {
switch(Action & INFCACHE_ACTIONBITS) {
case INFCACHE_NOWRITE:
pInfCache = InfCacheLoadCache(InfDir,LogContext);
bNoWriteBack = TRUE;
break;
case INFCACHE_DEFAULT:
pInfCache = InfCacheLoadCache(InfDir,LogContext);
break;
case INFCACHE_REBUILD:
pInfCache = InfCacheCreateNewCache(LogContext);
break;
case INFCACHE_ENUMALL:
pInfCache = NULL;
break;
default:
MYASSERT(FALSE);
}
}
#else
pInfCache = NULL;
#endif
//
// if an INF is changed, we will withhold writing back within 5 seconds
// this ensures that a file stamped by InfCacheAwareCopyFile can get
// modified again within the 2 second or so window where it may get
// the same timestamp, but without us "freezing" any changes
//
GetSystemTimeAsFileTime(&FileTimeNow);
AddFileTimeSeconds(&FileTimeNow,&FileTimeBefore,-5);
AddFileTimeSeconds(&FileTimeNow,&FileTimeAfter,5);
//
// first phase - enumerate the INF directory
//
FindHandle = FindFirstFile(InfPath,&FindFileData);
if(FindHandle != INVALID_HANDLE_VALUE) {
do {
BOOL NewInf = FALSE;
BOOL bCallCallback = FALSE;
PLOADED_INF pInf = NULL;
UINT ErrorLineNumber;
BOOL PnfWasUsed;
#ifdef UNICODE
if(!pInfCache || pInfCache->bNoWriteBack) {
//
// fallen into failsafe mode
//
bCallCallback = TRUE;
} else if((CompareFileTime(&FindFileData.ftLastWriteTime,&FileTimeBefore)>0)
&& (CompareFileTime(&FindFileData.ftLastWriteTime,&FileTimeAfter)<0)) {
//
// if actual file time falls close to now, then
// we don't trust it
// force it to be deleted out of the cache
// and make sure we do a callback
//
// if there's an entry in the cache, we want to invalidate it
// - this is particularly important if we're being called to
// recreate the cache
//
InfId = InfCacheLookupInf(pInfCache,&FindFileData,&inf_entry);
if(InfId>=0) {
//
// there was a match
//
InfCacheRemoveInf(pInfCache,InfId,&inf_entry);
InfId = -1;
}
bCallCallback = TRUE;
NewInf = FALSE;
WriteLogEntry(
LogContext,
DRIVER_LOG_VERBOSE,
MSG_LOG_CACHE_TIMEFRAME,
NULL,
FindFileData.cFileName);
} else {
//
// mark this INF as existing
//
SearchFlags = InfCacheSearchTableSetFlags(pInfCache,
FindFileData.cFileName,
CHE_FLAGS_PENDING, // start off expecting this INF to be processed later
0);
if(SearchFlags == (ULONG)(-1)) {
//
// failed - handle here
//
bCallCallback = TRUE;
}
InfId = InfCacheLookupInf(pInfCache,&FindFileData,&inf_entry);
if (InfId<0) {
NewInf = TRUE;
} else {
#if 0
//
// handle special inclusions (we can't handle these in Phase 2)
//
if (((Action&INFCACHE_INC_OLDINFS) && (inf_entry.MatchFlags&CIEF_INF_OLDINF)) ||
((Action&INFCACHE_INC_NOCLASS) && (inf_entry.MatchFlags&CIEF_INF_NOCLASS))) {
bCallCallback = TRUE;
}
#endif
//
// handle exclusions (so that they will get excluded in Phase 2)
// exclusions are different for OLDNT and WIN4
//
if (inf_entry.MatchFlags & CIEF_INF_WIN4) {
//
// WIN4 INF
//
if(((Action & INFCACHE_EXC_URL) && (inf_entry.MatchFlags & CIEF_INF_URL)) ||
((Action & INFCACHE_EXC_NULLCLASS) && (inf_entry.MatchFlags & CIEF_INF_NULLGUID)) ||
((Action & INFCACHE_EXC_NOMANU) && !(inf_entry.MatchFlags & CIEF_INF_MANUFACTURER)) ||
((Action & INFCACHE_EXC_NOCLASS) && !(inf_entry.MatchFlags & CIEF_INF_CLASSINFO))) {
//
// exclude this INF
//
InfCacheSearchTableSetFlags(pInfCache,
FindFileData.cFileName,
0,
CHE_FLAGS_PENDING);
WriteLogEntry(
LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_EXCLUDE_WIN4_INF,
NULL,
FindFileData.cFileName);
}
} else if (inf_entry.MatchList & CIEF_INF_OLDNT) {
if((Action & INFCACHE_EXC_OLDINFS) ||
((Action & INFCACHE_EXC_NOCLASS) && !(inf_entry.MatchList & CIEF_INF_CLASSINFO))) {
//
// exclude this INF
//
InfCacheSearchTableSetFlags(pInfCache,
FindFileData.cFileName,
0,
CHE_FLAGS_PENDING);
WriteLogEntry(
LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_EXCLUDE_OLDNT_INF,
NULL,
FindFileData.cFileName);
} else {
//
// allow old INF's to match with any HwId's
// by considering it already matched
//
InfCacheSearchTableSetFlags(pInfCache,
FindFileData.cFileName,
CHE_FLAGS_IDMATCH,
0);
}
} else {
//
// always exclude non-inf's
//
InfCacheSearchTableSetFlags(pInfCache,
FindFileData.cFileName,
0,
CHE_FLAGS_PENDING);
}
}
}
#else
bCallCallback = TRUE; // Win9x
#endif
if (!Callback) {
//
// we were only called to re-build the cache
//
bCallCallback = FALSE;
}
if (NewInf || bCallCallback) {
PTSTR InfFullPath = NULL;
DWORD InfFullPathSize = lstrlen(InfDir)+MAX_PATH+2;
//
// we need to open HINF in either case
//
InfFullPath = MyMalloc(InfFullPathSize*sizeof(TCHAR));
if(InfFullPath == NULL) {
//
// carry on with other files, even if out of memory
// not the best thing to do, but consistant with
// what we did before.
//
continue;
}
lstrcpy(InfFullPath,InfDir);
pSetupConcatenatePaths(InfFullPath,FindFileData.cFileName,InfFullPathSize,NULL);
if((Err=LoadInfFile(InfFullPath,
&FindFileData,
INF_STYLE_WIN4 | INF_STYLE_OLDNT, // we'll filter this ourselves
LDINF_FLAG_IGNORE_VOLATILE_DIRIDS | (TryPnf?LDINF_FLAG_ALWAYS_TRY_PNF:0),
NULL,
NULL,
NULL,
NULL,
LogContext,
&pInf,
&ErrorLineNumber,
&PnfWasUsed)) != NO_ERROR) {
pInf = NULL;
WriteLogEntry(
LogContext,
DRIVER_LOG_VVERBOSE,
MSG_LOG_COULD_NOT_LOAD_NEW_INF,
NULL,
InfFullPath);
}
#ifdef UNICODE
if(NewInf) {
//
// if opening the INF failed, we still want to record this fact
// in the cache so we don't try to re-open next time round
//
InfId = InfCacheAddInf(LogContext,pInfCache,&FindFileData,&inf_entry,pInf);
if(Callback) {
bCallCallback = TRUE;
}
}
#endif
if (pInf) {
if (bCallCallback && Callback) {
//
// we're processing the INF now
// clear pending flag (if we can) so we don't try and process INF a 2nd time
// the only time this can fail is if we haven't already added the INF
// so either way we wont callback twice
//
#ifdef UNICODE
if (pInfCache) {
//
// only set flags in the cache
// if we have a cache :-)
//
InfCacheSearchTableSetFlags(pInfCache,FindFileData.cFileName,0,CHE_FLAGS_PENDING);
}
#endif
if(!Callback(LogContext,InfFullPath,pInf,PnfWasUsed,Context)) {
Err = GetLastError();
MYASSERT(Err);
FreeInfFile(pInf);
MyFree(InfFullPath);
goto cleanup;
}
}
FreeInfFile(pInf);
}
MyFree(InfFullPath);
}
} while (FindNextFile(FindHandle,&FindFileData));
FindClose(FindHandle);
}
if (!pInfCache) {
//
// we have processed all files already
// skip cache search code, since we don't
// have a cache to search
//
Err = NO_ERROR;
goto cleanup;
}
#ifdef UNICODE
//
// at this point we can commit cache
//
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_CACHE_1,
NULL);
if(pInfCache && !bNoWriteBack) {
InfCacheWriteCache(InfDir,pInfCache,LogContext);
}
if (!Callback) {
//
// optimization: no callback
// (we were only called, eg, to update cache)
// leave early
//
Err = NO_ERROR;
goto cleanup;
}
//
// Phase 2 - determine all other INF's to process via Cache
//
// will want INFs that exist, haven't yet been processed
// and haven't been excluded
//
ReqFlags = CHE_FLAGS_PENDING;
if (ClassIdList && ClassIdList[0]) {
PCTSTR ClassId;
//
// Primary list (typically Class GUID, Class Name and Legacy Class Name)
//
Err = NO_ERROR;
for(ClassId = ClassIdList;*ClassId;ClassId += lstrlen(ClassId)+1) {
Err = InfCacheMarkMatchInfs(pInfCache,ClassId,CHE_FLAGS_GUIDMATCH);
if (Err != NO_ERROR) {
break;
}
}
if (Err == NO_ERROR) {
//
// succeeded, restrict requirement
//
ReqFlags |= CHE_FLAGS_GUIDMATCH;
}
}
if (HwIdList && HwIdList[0]) {
PCTSTR HwId;
//
// Secondary list
// if a list of hardware Id's specified, we only want hits that include
// any of the hardware Id's
//
Err = NO_ERROR;
for(HwId = HwIdList;*HwId;HwId += lstrlen(HwId)+1) {
Err = InfCacheMarkMatchInfs(pInfCache,HwId,CHE_FLAGS_IDMATCH);
if(Err != NO_ERROR) {
break;
}
}
if (Err == NO_ERROR) {
//
// succeeded, restrict requirement
//
ReqFlags |= CHE_FLAGS_IDMATCH;
}
}
//
// Phase 3 - process all INF's that meet requirements
// do this by simply enumerating the search string table
//
enum_data.LogContext = LogContext;
enum_data.Callback = Callback;
enum_data.Context = Context;
enum_data.InfDir = InfDir;
enum_data.Requirement = ReqFlags;
enum_data.ExitStatus = NO_ERROR;
Err = NO_ERROR;
if(!pStringTableEnum(pInfCache->pSearchTable,
&hit_stats,
sizeof(hit_stats),
InfCacheSearchEnum,
(LPARAM)&enum_data)) {
//
// we'll only fail for error condition
//
Err = enum_data.ExitStatus;
}
WriteLogEntry(
LogContext,
DRIVER_LOG_TIME,
MSG_LOG_END_CACHE_2,
NULL);
#else
Err = NO_ERROR;
#endif
cleanup:
#ifdef UNICODE
if (pInfCache) {
InfCacheFreeCache(pInfCache);
}
#endif
if (InfPath) {
MyFree(InfPath);
}
if (LogContext && LocalLogContext) {
DeleteLogContext(LocalLogContext);
}
return Err;
}
DWORD InfCacheSearchPath(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN DWORD Action,
IN PCTSTR InfDirPath, OPTIONAL
IN InfCacheCallback Callback, OPTIONAL
IN PVOID Context, OPTIONAL
IN PCTSTR ClassIdList, OPTIONAL
IN PCTSTR HwIdList OPTIONAL
)
/*++
Routine Description:
Iterates InfDirPath calling InfCacheSearchDirectory for each entry
Arguments:
LogContext - for logging
InfDir - single directory to search - if not specified, uses driver path
Callback - function to call on a likely match
Context - parameter to pass to function
ClassIdList (optional) - multi-sz list of class id's (typically guid, name and legacy name)
HwIdList (optional)- multi-sz list of hardware id's
Return Value:
status, typically NO_ERROR
--*/
{
PSETUP_LOG_CONTEXT LocalLogContext = NULL;
DWORD Err = NO_ERROR;
PCTSTR InfDir;
if (!InfDirPath) {
InfDirPath = InfSearchPaths;
}
if(!LogContext) {
if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
LogContext = LocalLogContext;
} else {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
for (InfDir = InfDirPath; *InfDir; InfDir+=lstrlen(InfDir)+1) {
Err = InfCacheSearchDirectory(LogContext,Action,InfDir,Callback,Context,ClassIdList,HwIdList);
if(Err != NO_ERROR) {
break;
}
}
if (LogContext && LocalLogContext) {
DeleteLogContext(LocalLogContext);
}
return Err;
}
#ifdef UNICODE
BOOL WINAPI pSetupInfCacheBuild(
IN DWORD Action
)
/*++
Routine Description:
Privately exported, called from (eg) syssetup to reset cache(s)
Arguments:
Action - one of:
INFCACHEBUILD_UPDATE
INFCACHEBUILD_REBUILD
Return Value:
TRUE if success, FALSE on Error (GetLastError indicates error)
--*/
{
DWORD RealAction;
DWORD Err;
switch(Action) {
case INFCACHEBUILD_UPDATE:
RealAction = INFCACHE_DEFAULT;
break;
case INFCACHEBUILD_REBUILD:
RealAction = INFCACHE_REBUILD;
break;
default:
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
RealAction |= INFCACHE_FORCE_CACHE|INFCACHE_FORCE_PNF;
try {
Err = InfCacheSearchPath(NULL,
RealAction,
NULL,
NULL,
NULL,
NULL,
NULL
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_DATA;
}
SetLastError(Err);
return Err==NO_ERROR;
}
#endif
BOOL
InfCacheAwareCopyFile(
IN LPCTSTR Source,
IN LPCTSTR Target
)
{
#ifdef UNICODE
FILETIME FileTimeNow;
//
// we use the uniqueness of the timestamp on the INF to
// know if an INF has been modified
// it appears that some vendors decide to "touch" the timestamp
// on related INF's that throws INFCACHE
//
// it's possible that InfCacheAwareCopyFile might get called for the same
// filename (different file) multiple times over a 2 second interval
// the InfCache code above will not trust a file to be stable until
// 5 seconds has elapsed, in which case modifications will have new
// timestamps
//
//
// what's our current time?
//
if(!CopyFile(Source, Target, FALSE)) {
return FALSE;
}
GetSystemTimeAsFileTime(&FileTimeNow);
//
// update time-stamp effectively invalidates the entry in INFCACHE
// this file will not be 'comitted' to cache for at least another 5
// seconds
//
GetSetFileTimestamp(Target,NULL,NULL,&FileTimeNow,TRUE);
return TRUE;
#else
return CopyFile(Source,Target,FALSE);
#endif
}