Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1556 lines
43 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
fileutil.c
Abstract:
File-related functions for Windows NT Setup API dll.
Author:
Ted Miller (tedm) 11-Jan-1995
Revision History:
--*/
#include "setupntp.h"
#pragma hdrstop
DWORD
OpenAndMapFileForRead(
IN PCTSTR FileName,
OUT PDWORD FileSize,
OUT PHANDLE FileHandle,
OUT PHANDLE MappingHandle,
OUT PVOID *BaseAddress
)
/*++
Routine Description:
Open and map an existing file for read access.
Arguments:
FileName - supplies pathname to file to be mapped.
FileSize - receives the size in bytes of the file.
FileHandle - receives the win32 file handle for the open file.
The file will be opened for generic read access.
MappingHandle - receives the win32 handle for the file mapping
object. This object will be for read access.
BaseAddress - receives the address where the file is mapped.
Return Value:
NO_ERROR if the file was opened and mapped successfully.
The caller must unmap the file with UnmapAndCloseFile when
access to the file is no longer desired.
Win32 error code if the file was not successfully mapped.
--*/
{
DWORD rc;
//
// Open the file -- fail if it does not exist.
//
*FileHandle = CreateFile(
FileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if(*FileHandle == INVALID_HANDLE_VALUE) {
rc = GetLastError();
} else if((rc = MapFileForRead(*FileHandle,
FileSize,
MappingHandle,
BaseAddress)) != NO_ERROR) {
CloseHandle(*FileHandle);
}
return(rc);
}
DWORD
MapFileForRead(
IN HANDLE FileHandle,
OUT PDWORD FileSize,
OUT PHANDLE MappingHandle,
OUT PVOID *BaseAddress
)
/*++
Routine Description:
Map an opened file for read access.
Arguments:
FileHandle - supplies the handle of the opened file to be mapped.
This handle must have been opened with at least read access.
FileSize - receives the size in bytes of the file.
MappingHandle - receives the win32 handle for the file mapping
object. This object will be for read access.
BaseAddress - receives the address where the file is mapped.
Return Value:
NO_ERROR if the file was mapped successfully. The caller must
unmap the file with UnmapAndCloseFile when access to the file
is no longer desired.
Win32 error code if the file was not successfully mapped.
--*/
{
DWORD rc;
//
// Get the size of the file.
//
*FileSize = GetFileSize(FileHandle, NULL);
if(*FileSize != (DWORD)(-1)) {
//
// Create file mapping for the whole file.
//
*MappingHandle = CreateFileMapping(
FileHandle,
NULL,
PAGE_READONLY,
0,
*FileSize,
NULL
);
if(*MappingHandle) {
//
// Map the whole file.
//
*BaseAddress = MapViewOfFile(
*MappingHandle,
FILE_MAP_READ,
0,
0,
*FileSize
);
if(*BaseAddress) {
return(NO_ERROR);
}
rc = GetLastError();
CloseHandle(*MappingHandle);
} else {
rc = GetLastError();
}
} else {
rc = GetLastError();
}
return(rc);
}
BOOL
UnmapAndCloseFile(
IN HANDLE FileHandle,
IN HANDLE MappingHandle,
IN PVOID BaseAddress
)
/*++
Routine Description:
Unmap and close a file.
Arguments:
FileHandle - supplies win32 handle to open file.
MappingHandle - supplies the win32 handle for the open file mapping
object.
BaseAddress - supplies the address where the file is mapped.
Return Value:
BOOLean value indicating success or failure.
--*/
{
BOOL b;
b = UnmapViewOfFile(BaseAddress);
b = b && CloseHandle(MappingHandle);
b = b && CloseHandle(FileHandle);
return(b);
}
DWORD
ReadAsciiOrUnicodeTextFile(
IN HANDLE FileHandle,
OUT PTEXTFILE_READ_BUFFER Result
)
/*++
Routine Description:
#ifdef UNICODE
Read in a text file that may be in either ascii or unicode format.
If the file is ascii it is assumed to be OEM format and is converted
to Unicode.
#else
This routine is rather unfortunately named because Unicode is not
supported at all. Read in a text file. The file is assumed to be in
ANSI format. The result is a mapped image of the file.
The usage of the fields of the TEXTFILE_READ_BUFFER structure (see below)
are changed accordingly for the lack of unicode support.
#endif
Arguments:
FileHandle - Supplies the handle of the text file to be read.
Result - supplies the address of a TEXTFILE_READ_BUFFER structure that
receives information about the text file buffer read. The structure
is defined as follows:
typedef struct _TEXTFILE_READ_BUFFER {
PCTSTR TextBuffer;
DWORD TextBufferSize;
HANDLE FileHandle;
HANDLE MappingHandle;
PVOID ViewAddress;
} TEXTFILE_READ_BUFFER, *PTEXTFILE_READ_BUFFER;
TextBuffer - pointer to the read-only unicode string containing
the entire text of the file.
(NOTE: If the file is a Unicode file with a Byte Order Mark
prefix, this Unicode character is not included in the returned
buffer.)
TextBufferSize - size of the TextBuffer (in characters).
FileHandle - If this is a valid handle (i.e., it's not equal to
INVALID_HANDLE_VALUE), then the file was already Unicode, so
the TextBuffer is simply the mapped-in image of the file.
This field is reserved for use by the DestroyTextFileReadBuffer
routine, and should not be accessed.
MappingHandle - If FileHandle is valid, then this contains the
mapping handle for the file image mapping.
This field is reserved for use by the DestroyTextFileReadBuffer
routine, and should not be accessed.
ViewAddress - If FileHandle is valid, then this contains the
starting memory address where the file image was mapped in.
This field is reserved for use by the DestroyTextFileReadBuffer
routine, and should not be accessed.
Return Value:
Win32 error value indicating the outcome.
Remarks:
Upon return from this routine, the caller MUST NOT attempt to close FileHandle.
This routine with either close the handle itself (after it's finished with it, or
upon error), or it will store the handle away in the TEXTFILE_READ_BUFFER struct,
to be later closed via DestroyTextFileReadBuffer().
--*/
{
DWORD rc;
DWORD FileSize;
HANDLE MappingHandle;
PVOID ViewAddress, TextStartAddress;
#ifdef UNICODE
BOOL IsUnicode;
#endif
//
// Map the file for read access.
//
rc = MapFileForRead(
FileHandle,
&FileSize,
&MappingHandle,
&ViewAddress
);
if(rc != NO_ERROR) {
//
// We couldn't map the file--close the file handle now.
//
CloseHandle(FileHandle);
} else {
//
// Determine whether the file is unicode. Guard with try/except in
// case we get an inpage error.
//
try {
//
// Check to see if the file starts with a Unicode Byte Order Mark
// (BOM) character (0xFEFF). If so, then we know that the file
// is Unicode, and don't have to go through the slow process of
// trying to figure it out.
//
TextStartAddress = ViewAddress;
#ifdef UNICODE
if((FileSize >= sizeof(WCHAR)) && (*(PWCHAR)TextStartAddress == 0xFEFF)) {
//
// The file has the BOM prefix. Adjust the pointer to the
// start of the text, so that we don't include the marker
// in the text buffer we return.
//
IsUnicode = TRUE;
((PWCHAR)TextStartAddress)++;
FileSize -= sizeof(WCHAR);
} else {
IsUnicode = IsTextUnicode(TextStartAddress,FileSize,NULL);
}
#endif
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_READ_FAULT;
}
if(rc == NO_ERROR) {
#ifdef UNICODE
if(IsUnicode) {
#endif
//
// No conversion is required--we'll just use the mapped-in
// image in memory.
//
Result->TextBuffer = TextStartAddress;
Result->TextBufferSize = FileSize/sizeof(TCHAR);
Result->FileHandle = FileHandle;
Result->MappingHandle = MappingHandle;
Result->ViewAddress = ViewAddress;
#ifdef UNICODE
} else {
DWORD WcharCount;
PWCHAR Buffer;
//
// Need to convert the file to unicode.
// Allocate a buffer that is maximally sized.
// The maximum size of the unicode text is
// double the size of the oem text, and would occur
// when each oem character is single-byte.
//
if(Buffer = MyMalloc(FileSize * sizeof(WCHAR))) {
try {
WcharCount = MultiByteToWideChar(CP_ACP,
MB_PRECOMPOSED,
TextStartAddress,
FileSize,
Buffer,
FileSize
);
if(!WcharCount) {
rc = GetLastError();
}
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_READ_FAULT;
}
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
if(rc == NO_ERROR) {
//
// If the converted buffer doesn't require the entire block
// we allocated, attempt to reallocate the buffer to its
// correct size. We don't care if this fails, since the
// buffer we have is perfectly fine (just bigger than we
// need).
//
if((WcharCount == FileSize) ||
!(Result->TextBuffer =
MyRealloc(Buffer, WcharCount * sizeof(WCHAR)))) {
Result->TextBuffer = Buffer;
}
Result->TextBufferSize = WcharCount;
Result->FileHandle = INVALID_HANDLE_VALUE;
} else {
//
// Free the buffer, if it was previously allocated.
//
if(Buffer) {
MyFree(Buffer);
}
}
}
#endif
}
//
// If the file was already Unicode, and we didn't enounter any errors,
// then we don't want to close it, because we use the mapped-in view
// directly.
//
#ifdef UNICODE
if((rc != NO_ERROR) || !IsUnicode) {
#else
if(rc != NO_ERROR) {
#endif
UnmapAndCloseFile(FileHandle, MappingHandle, ViewAddress);
}
}
return rc;
}
BOOL
DestroyTextFileReadBuffer(
IN PTEXTFILE_READ_BUFFER ReadBuffer
)
/*++
Routine Description:
Destroy a textfile read buffer created by ReadAsciiOrUnicodeTextFile.
Arguments:
ReadBuffer - supplies the address of a TEXTFILE_READ_BUFFER structure
for the buffer to be destroyed.
Return Value:
BOOLean value indicating success or failure.
--*/
{
//
// If our ReadBuffer structure has a valid FileHandle, then we must
// unmap and close the file, otherwise, we simply need to free the
// allocated buffer.
//
if(ReadBuffer->FileHandle != INVALID_HANDLE_VALUE) {
return UnmapAndCloseFile(ReadBuffer->FileHandle,
ReadBuffer->MappingHandle,
ReadBuffer->ViewAddress
);
} else {
MyFree(ReadBuffer->TextBuffer);
return TRUE;
}
}
BOOL
GetVersionInfoFromImage(
IN PCTSTR FileName,
OUT PDWORDLONG Version,
OUT LANGID *Language
)
/*++
Routine Description:
Retrieve file version and language info from a file.
The version is specified in the dwFileVersionMS and dwFileVersionLS fields
of a VS_FIXEDFILEINFO, as filled in by the win32 version APIs. For the
language we look at the translation table in the version resources and assume
that the first langid/codepage pair specifies the language.
If the file is not a coff image or does not have version resources,
the function fails. The function does not fail if we are able to retrieve
the version but not the language.
Arguments:
FileName - supplies the full path of the file whose version data is desired.
Version - receives the version stamp of the file. If the file is not a coff image
or does not contain the appropriate version resource data, the function fails.
Language - receives the language id of the file. If the file is not a coff image
or does not contain the appropriate version resource data, this will be 0
and the function succeeds.
Return Value:
TRUE if we were able to retreive at least the version stamp.
FALSE otherwise.
--*/
{
DWORD d;
PVOID VersionBlock;
VS_FIXEDFILEINFO *FixedVersionInfo;
UINT DataLength;
BOOL b;
PWORD Translation;
DWORD Ignored;
//
// Assume failure
//
b = FALSE;
//
// Get the size of version info block.
//
if(d = GetFileVersionInfoSize((PTSTR)FileName,&Ignored)) {
//
// Allocate memory block of sufficient size to hold version info block
//
VersionBlock = MyMalloc(d*sizeof(TCHAR));
if(VersionBlock) {
//
// Get the version block from the file.
//
if(GetFileVersionInfo((PTSTR)FileName,0,d*sizeof(TCHAR),VersionBlock)) {
//
// Get fixed version info.
//
if(VerQueryValue(VersionBlock,TEXT("\\"),&FixedVersionInfo,&DataLength)) {
//
// If we get here, we declare success, even if there is
// no language.
//
b = TRUE;
//
// Return version to caller.
//
*Version = (((DWORDLONG)FixedVersionInfo->dwFileVersionMS) << 32)
+ FixedVersionInfo->dwFileVersionLS;
//
// Attempt to get language of file. We'll simply ask for the
// translation table and use the first language id we find in there
// as *the* language of the file.
//
// The translation table consists of LANGID/Codepage pairs.
//
if(VerQueryValue(VersionBlock,TEXT("\\VarFileInfo\\Translation"),&Translation,&DataLength)
&& (DataLength >= (2*sizeof(WORD)))) {
*Language = Translation[0];
} else {
//
// No language
//
*Language = 0;
}
}
}
MyFree(VersionBlock);
}
}
return(b);
}
BOOL
FileExists(
IN PCTSTR FileName,
OUT PWIN32_FIND_DATA FindData OPTIONAL
)
/*++
Routine Description:
Determine if a file exists and is accessible.
Errormode is set (and then restored) so the user will not see
any pop-ups.
Arguments:
FileName - supplies full path of file to check for existance.
FindData - if specified, receives find data for the file.
Return Value:
TRUE if the file exists and is accessible.
FALSE if not. GetLastError() returns extended error info.
--*/
{
WIN32_FIND_DATA findData;
HANDLE FindHandle;
UINT OldMode;
DWORD Error;
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
FindHandle = FindFirstFile(FileName,&findData);
if(FindHandle == INVALID_HANDLE_VALUE) {
Error = GetLastError();
} else {
FindClose(FindHandle);
if(FindData) {
*FindData = findData;
}
Error = NO_ERROR;
}
SetErrorMode(OldMode);
SetLastError(Error);
return (Error == NO_ERROR);
}
DWORD
GetSetFileTimestamp(
IN PCTSTR FileName,
OUT FILETIME *CreateTime, OPTIONAL
OUT FILETIME *AccessTime, OPTIONAL
OUT FILETIME *WriteTime, OPTIONAL
IN BOOL Set
)
/*++
Routine Description:
Get or set a file's timestamp values.
Arguments:
FileName - supplies full path of file to get or set timestamps
CreateTime - if specified and the underlying filesystem supports it,
receives the creation time of the file.
AccessTime - if specified and the underlying filesystem supports it,
receives the last access time of the file.
WriteTime - if specified, receives the last write time of the file.
Return Value:
TRUE if the file exists and is accessible.
FALSE if not. GetLastError() returns extended error info.
--*/
{
HANDLE h;
DWORD d;
BOOL b;
h = CreateFile(
FileName,
Set ? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if(h == INVALID_HANDLE_VALUE) {
return(GetLastError());
}
b = Set
? SetFileTime(h,CreateTime,AccessTime,WriteTime)
: GetFileTime(h,CreateTime,AccessTime,WriteTime);
d = b ? NO_ERROR : GetLastError();
CloseHandle(h);
return(d);
}
DWORD
RetreiveFileSecurity(
IN PCTSTR FileName,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
)
/*++
Routine Description:
Retreive security information from a file and place it into a buffer.
Arguments:
FileName - supplies name of file whose security information is desired.
SecurityDescriptor - If the function is successful, receives pointer
to buffer containing security information for the file. The pointer
may be NULL, indicating that there is no security information
associated with the file or that the underlying filesystem does not
support file security.
Return Value:
Win32 error code indicating outcome. If NO_ERROR check the value returned
in SecurityDescriptor.
The caller can free the buffer with MyFree() when done with it.
--*/
{
BOOL b;
DWORD d;
DWORD BytesRequired;
PSECURITY_DESCRIPTOR p;
BytesRequired = 1024;
while (TRUE) {
//
// Allocate a buffer of the required size.
//
p = MyMalloc(BytesRequired);
if(!p) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Get the security.
//
b = GetFileSecurity(
FileName,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
p,
BytesRequired,
&BytesRequired
);
//
// Return with sucess
//
if(b) {
*SecurityDescriptor = p;
return(NO_ERROR);
}
//
// Return an error code, unless we just need a bigger buffer
//
MyFree(p);
d = GetLastError();
if(d != ERROR_INSUFFICIENT_BUFFER) {
return (d);
}
//
// There's a bug in GetFileSecurity that can cause it to ask for a
// REALLY big buffer. In that case, we return an error.
//
if (BytesRequired > 0xF0000000) {
return (ERROR_INVALID_DATA);
}
//
// Otherwise, we'll try again with a bigger buffer
//
}
}
DWORD
StampFileSecurity(
IN PCTSTR FileName,
IN PSECURITY_DESCRIPTOR SecurityInfo
)
/*++
Routine Description:
Set security information on a file.
Arguments:
FileName - supplies name of file whose security information is desired.
SecurityDescriptor - supplies pointer to buffer containing security information
for the file. This buffer should have been returned by a call to
RetreiveFileSecurity. If the underlying filesystem does not support
file security, the function fails.
Return Value:
Win32 error code indicating outcome.
--*/
{
BOOL b;
b = SetFileSecurity(
FileName,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
SecurityInfo
);
return(b ? NO_ERROR : GetLastError());
}
DWORD
TakeOwnershipOfFile(
IN PCTSTR Filename
)
/*++
Routine Description:
Sets the owner of a given file to the default owner specified in
the current process token.
Arguments:
FileName - supplies name of the file of which to take ownership.
Return Value:
Win32 error code indicating outcome.
--*/
{
BOOL b;
SECURITY_DESCRIPTOR SecurityDescriptor;
DWORD Err;
HANDLE Token;
DWORD BytesRequired;
PTOKEN_OWNER OwnerInfo;
//
// Open the process token.
//
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&Token)) {
Err = GetLastError();
goto clean0;
}
//
// Get the current process's default owner sid.
//
GetTokenInformation(Token,TokenOwner,NULL,0,&BytesRequired);
Err = GetLastError();
if(Err != ERROR_INSUFFICIENT_BUFFER) {
goto clean1;
}
OwnerInfo = MyMalloc(BytesRequired);
if(!OwnerInfo) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
b = GetTokenInformation(Token,TokenOwner,OwnerInfo,BytesRequired,&BytesRequired);
if(!b) {
Err = GetLastError();
goto clean2;
}
//
// Initialize the security descriptor.
//
if(!InitializeSecurityDescriptor(&SecurityDescriptor,SECURITY_DESCRIPTOR_REVISION)
|| !SetSecurityDescriptorOwner(&SecurityDescriptor,OwnerInfo->Owner,FALSE)) {
Err = GetLastError();
goto clean2;
}
//
// Set file security.
//
Err = SetFileSecurity(Filename,OWNER_SECURITY_INFORMATION,&SecurityDescriptor)
? NO_ERROR
: GetLastError();
//
// Not all filesystems support this operation.
//
if(Err == ERROR_NOT_SUPPORTED) {
Err = NO_ERROR;
}
clean2:
MyFree(OwnerInfo);
clean1:
CloseHandle(Token);
clean0:
return(Err);
}
DWORD
SearchForInfFile(
IN PCTSTR InfName,
OUT LPWIN32_FIND_DATA FindData,
IN DWORD SearchControl,
OUT PTSTR FullInfPath,
IN UINT FullInfPathSize,
OUT PUINT RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine searches for an INF file in the manner specified
by the SearchControl parameter. If the file is found, its
full path is returned.
Arguments:
InfName - Supplies name of INF to search for. This name is simply
appended to the two search directory paths, so if the name
contains directories, the file will searched for in the
subdirectory under the search directory. I.e.:
\foo\bar.inf
will be searched for as %windir%\inf\foo\bar.inf and
%windir%\system32\foo\bar.inf.
FindData - Supplies the address of a Win32 Find Data structure that
receives information about the file specified (if it is found).
SearchControl - Specifies the order in which directories should
be searched:
INFINFO_DEFAULT_SEARCH : search %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : search for the INF in each of the
directories listed in the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
FullInfPath - If the file is found, receives the full path of the INF.
FullInfPathSize - Supplies the size of the FullInfPath buffer (in
characters).
RequiredSize - Optionally, receives the number of characters (including
terminating NULL) required to store the FullInfPath.
Return Value:
Win32 error code indicating whether the function was successful. Common
return values are:
NO_ERROR if the file was found, and the INF file path returned
successfully.
ERROR_INSUFFICIENT_BUFFER if the supplied buffer was not large enough
to hold the full INF path (RequiredSize will indicated how large
the buffer needs to be)
ERROR_FILE_NOT_FOUND if the file was not found.
ERROR_INVALID_PARAMETER if the SearchControl parameter is invalid.
--*/
{
PCTSTR PathList;
TCHAR CurInfPath[MAX_PATH];
PCTSTR PathPtr, InfPathLocation;
DWORD PathLength;
BOOL b, FreePathList;
DWORD d;
//
// Retrieve the path list.
//
if(SearchControl == INFINFO_INF_PATH_LIST_SEARCH) {
//
// Just use our global list of INF search paths.
//
PathList = InfSearchPaths;
FreePathList = FALSE;
} else {
if(!(PathList = AllocAndReturnDriverSearchList(SearchControl))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
FreePathList = TRUE;
}
//
// Now look for the INF in each path in our MultiSz list.
//
InfPathLocation = NULL;
d = NO_ERROR;
for(PathPtr = PathList; *PathPtr; PathPtr += (lstrlen(PathPtr) + 1)) {
//
// Concatenate the INF file name with the current search path.
//
lstrcpy(CurInfPath, PathPtr);
ConcatenatePaths(CurInfPath,
InfName,
SIZECHARS(CurInfPath),
&PathLength
);
if(b = FileExists(CurInfPath, FindData)) {
if(!(FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
InfPathLocation = CurInfPath;
break;
}
} else {
//
// See if we got a 'real' error
//
d = GetLastError();
if((d == ERROR_NO_MORE_FILES) || (d == ERROR_FILE_NOT_FOUND) || (d == ERROR_PATH_NOT_FOUND)) {
//
// Not really an error--continue looking.
//
d = NO_ERROR;
} else {
//
// This is a 'real' error, abort the search.
//
break;
}
}
}
//
// Whatever the outcome, we're through with the PathList buffer.
//
if(FreePathList) {
MyFree(PathList);
}
if(d != NO_ERROR) {
return d;
} else if(!InfPathLocation) {
return ERROR_FILE_NOT_FOUND;
}
if(RequiredSize) {
*RequiredSize = PathLength;
}
if(PathLength > FullInfPathSize) {
return ERROR_INSUFFICIENT_BUFFER;
}
CopyMemory(FullInfPath,
InfPathLocation,
PathLength * sizeof(TCHAR)
);
return NO_ERROR;
}
DWORD
MultiSzFromSearchControl(
IN DWORD SearchControl,
OUT PTCHAR PathList,
IN DWORD PathListSize,
OUT PDWORD RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine takes a search control ordinal and builds a MultiSz list
based on the search list it specifies.
Arguments:
SearchControl - Specifies the directory list to be built. May be one
of the following values:
INFINFO_DEFAULT_SEARCH : %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : Each of the directories listed in
the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
PathList - Supplies the address of a character buffer that will receive
the MultiSz list.
PathListSize - Supplies the size, in characters, of the PathList buffer.
RequiredSize - Optionally, receives the number of characters required
to store the MultiSz PathList.
(NOTE: The user-supplied buffer is used to retrieve the value entry
from the registry. Therefore, if the value is a REG_EXPAND_SZ entry,
the RequiredSize parameter may be set too small on an
ERROR_INSUFFICIENT_BUFFER error. This will happen if the buffer was
too small to retrieve the value entry, before expansion. In this case,
calling the API again with a buffer sized according to the RequiredSize
output may fail with an ERROR_INSUFFICIENT_BUFFER yet again, since
expansion may require an even larger buffer.)
Return Value:
If successful, returns NO_ERROR.
If failure, returns an ERROR_* status code.
--*/
{
HKEY hk;
PCTSTR Path1, Path2;
PTSTR PathBuffer;
DWORD RegDataType, PathLength, PathLength1, PathLength2;
DWORD NumPaths, Err;
BOOL UseDefaultDevicePath;
if(PathList) {
Err = NO_ERROR; // assume success.
} else {
return ERROR_INVALID_PARAMETER;
}
UseDefaultDevicePath = FALSE;
if(SearchControl == INFINFO_INF_PATH_LIST_SEARCH) {
//
// Retrieve the INF search path list from the registry.
//
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
pszPathSetup,
0,
KEY_READ,
&hk) != ERROR_SUCCESS) {
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
} else {
PathBuffer = NULL;
try {
//
// Get the DevicePath value entry. Support REG_SZ or REG_EXPAND_SZ data.
//
PathLength = PathListSize * sizeof(TCHAR);
Err = RegQueryValueEx(hk,
pszDevicePath,
NULL,
&RegDataType,
(LPBYTE)PathList,
&PathLength
);
//
// Need path length in characters from now on.
//
PathLength /= sizeof(TCHAR);
if(Err == ERROR_SUCCESS) {
if((RegDataType == REG_SZ) || (RegDataType == REG_EXPAND_SZ)) {
//
// Convert this semicolon-delimited list to a REG_MULTI_SZ.
//
NumPaths = DelimStringToMultiSz(PathList,
PathLength,
TEXT(';')
);
#if 0
if(RegDataType == REG_EXPAND_SZ) {
#endif
//
// Allocate a temporary buffer large enough to hold the number
// of paths in the MULTI_SZ list, each having maximum length
// (plus an extra terminating NULL at the end).
//
if(!(PathBuffer = MyMalloc((NumPaths * MAX_PATH * sizeof(TCHAR))
+ sizeof(TCHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
PathLength = 0;
for(Path1 = PathList;
*Path1;
Path1 += lstrlen(Path1) + 1) {
if(RegDataType == REG_EXPAND_SZ) {
PathLength += ExpandEnvironmentStrings(Path1,
PathBuffer + PathLength,
MAX_PATH
);
} else {
lstrcpy(PathBuffer + PathLength, Path1);
PathLength += lstrlen(Path1) + 1;
}
//
// If the last character in this path is a backslash, then strip
// it off.
//
if(*(PathBuffer + PathLength - 2) == TEXT('\\')) {
PathLength--;
*(PathBuffer + PathLength - 1) = TEXT('\0');
}
}
//
// Add additional terminating NULL at the end.
//
*(PathBuffer + PathLength) = TEXT('\0');
if(++PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory(PathList,
PathBuffer,
PathLength * sizeof(TCHAR)
);
}
MyFree(PathBuffer);
PathBuffer = NULL;
#if 0
}
#endif
} else {
//
// Bad data type--just use the Inf directory.
//
UseDefaultDevicePath = TRUE;
}
} else if(Err == ERROR_MORE_DATA){
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
if(PathBuffer) {
MyFree(PathBuffer);
}
}
RegCloseKey(hk);
}
}
if(UseDefaultDevicePath) {
PathLength = lstrlen(InfDirectory) + 2;
if(PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
Err = NO_ERROR;
CopyMemory(PathList, InfDirectory, (PathLength - 1) * sizeof(TCHAR));
//
// Add extra NULL to terminate the list.
//
PathList[PathLength - 1] = TEXT('\0');
}
} else if((Err == NO_ERROR) && (SearchControl != INFINFO_INF_PATH_LIST_SEARCH)) {
switch(SearchControl) {
case INFINFO_DEFAULT_SEARCH :
Path1 = InfDirectory;
Path2 = SystemDirectory;
break;
case INFINFO_REVERSE_DEFAULT_SEARCH :
Path1 = SystemDirectory;
Path2 = InfDirectory;
break;
default :
return ERROR_INVALID_PARAMETER;
}
PathLength1 = lstrlen(Path1) + 1;
PathLength2 = lstrlen(Path2) + 1;
PathLength = PathLength1 + PathLength2 + 1;
if(PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory(PathList, Path1, PathLength1 * sizeof(TCHAR));
CopyMemory(&(PathList[PathLength1]), Path2, PathLength2 * sizeof(TCHAR));
//
// Add additional terminating NULL at the end.
//
PathList[PathLength - 1] = 0;
}
}
if(((Err == NO_ERROR) || (Err == ERROR_INSUFFICIENT_BUFFER)) && RequiredSize) {
*RequiredSize = PathLength;
}
return Err;
}
PTSTR
AllocAndReturnDriverSearchList(
IN DWORD SearchControl
)
/*++
Routine Description:
This routine returns a buffer contains a multi-sz list of all directory paths in our
driver search path list.
The buffer returned must be freed with MyFree().
Arguments:
SearchControl - Specifies the directory list to be retrieved. May be one
of the following values:
INFINFO_DEFAULT_SEARCH : %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : Each of the directories listed in
the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
Returns:
Pointer to the allocated buffer containing the list, or NULL if out-of-memory.
--*/
{
PTSTR PathListBuffer, TrimBuffer;
DWORD BufferSize;
DWORD Err;
//
// Start out with a buffer of MAX_PATH length, which should cover most cases.
//
BufferSize = MAX_PATH;
if(PathListBuffer = MyMalloc(BufferSize * sizeof(TCHAR))) {
//
// Loop on a call to MultiSzFromSearchControl until we succeed or hit some
// error other than buffer-too-small. There are two reasons for this. 1st, it
// is possible that someone could have added a new path to the registry list
// between calls, and 2nd, since that routine uses our buffer to retrieve the
// original (non-expanded) list, it can only report the size it needs to retrieve
// the unexpanded list. After it is given enough space to retrieve it, _then_ it
// can tell us how much space we really need.
//
// With all that said, we'll almost never see this call made more than once.
//
while(TRUE) {
if((Err = MultiSzFromSearchControl(SearchControl,
PathListBuffer,
BufferSize,
&BufferSize)) == NO_ERROR) {
//
// We've successfully retrieved the path list. If the list is larger
// than necessary (the normal case), then trim it down before returning.
// (If this fails it's no big deal--we'll just keep on using the original
// buffer.)
//
if(TrimBuffer = MyRealloc(PathListBuffer, BufferSize * sizeof(TCHAR))) {
return TrimBuffer;
} else {
return PathListBuffer;
}
} else {
//
// Free our current buffer before we find out what went wrong.
//
MyFree(PathListBuffer);
if((Err != ERROR_INSUFFICIENT_BUFFER) ||
!(PathListBuffer = MyMalloc(BufferSize * sizeof(TCHAR)))) {
//
// We failed.
//
return NULL;
}
}
}
}
return NULL;
}
BOOL
DelayedMove(
IN PCTSTR CurrentName,
IN PCTSTR NewName OPTIONAL
)
/*++
Routine Description:
Queue a file for copy or delete on next reboot.
On Windows NT this means using MoveFileEx(). On Win95 this means
using the wininit.ini mechanism.
It is assumed that the target file already exists. On Win95, we need
to know the short filename since the wininit.ini mechanism only understands
SFNs and we have to do a bunch of special crud to make this all work.
Note: we do NOT attempt to deal with the long filename of the target
when renaming on Win95. In other words, if the target name is not 8.3,
it will wind up as 8.3 after processing of wininit.ini is done!
Arguments:
CurrentName - supplies the name of the file as it exists currently.
NewName - if specified supplies the new name. If not specified
then the file named by CurrentName is deleted on next reboot.
Returns:
Boolean value indicating outcome. If failure, last error is set.
--*/
{
BOOL b;
if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
b = MoveFileEx(
CurrentName,
NewName,
MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT
);
} else {
TCHAR WininitFile[MAX_PATH];
TCHAR NewSFN[MAX_PATH];
TCHAR CurrentSFN[MAX_PATH];
//
// Calculate full path of wininit.ini and get short filenames.
//
GetWindowsDirectory(WininitFile,MAX_PATH);
ConcatenatePaths(WininitFile,TEXT("WININIT.INI"),MAX_PATH,NULL);
if(GetShortPathName(CurrentName,CurrentSFN,MAX_PATH)) {
if(NewName) {
if(GetShortPathName(NewName,NewSFN,MAX_PATH)) {
//
// No idea whether the existance of the target file will hose up
// whatever processes wininit.ini when it goes to do the rename,
// so delete the target file -- sure hope the list is processed
// in order.
//
if(b = WritePrivateProfileString(TEXT("Rename"),TEXT("NUL"),NewSFN,WininitFile)) {
b = WritePrivateProfileString(TEXT("Rename"),NewSFN,CurrentSFN,WininitFile);
//
// If we were totally cool we would attempt to use that rename stuff
// in the registry to rename the sfn in NewSFN to NewName.
//
}
} else {
b = FALSE;
}
} else {
b = WritePrivateProfileString(TEXT("Rename"),TEXT("NUL"),CurrentSFN,WininitFile);
}
}
}
return(b);
}