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.
 
 
 
 
 
 

7970 lines
282 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
fileq4.c
Abstract:
Setup file queue routines for commit (ie, performing enqueued actions).
Author:
Ted Miller (tedm) 15-Feb-1995
Revision History:
Jamie Hunter (jamiehun) 28-Jan-1997
Added backup queue capabilities
backup on demand capabilities
and unwind capabilities
--*/
#include "precomp.h"
#pragma hdrstop
#define LINK_START (TEXT("<A>"))
#define LINK_END (TEXT("</A>"))
typedef struct _Q_CAB_CB_DATA {
PSP_FILE_QUEUE Queue;
PSOURCE_MEDIA_INFO SourceMedia;
PSP_FILE_QUEUE_NODE CurrentFirstNode;
PVOID MsgHandler;
PVOID Context;
BOOL IsMsgHandlerNativeCharWidth;
PSETUP_LOG_CONTEXT LogContext;
} Q_CAB_CB_DATA, *PQ_CAB_CB_DATA;
typedef struct _CERT_PROMPT {
LPCTSTR lpszDescription;
LPCTSTR lpszFile;
SetupapiVerifyProblem ProblemType;
ULONG DriverSigningPolicy;
} CERT_PROMPT, *PCERT_PROMPT;
typedef struct _AUTHENTICODE_CERT_PROMPT {
LPCTSTR lpszDescription;
HANDLE hWVTStateData;
DWORD Error;
} AUTHENTICODE_CERT_PROMPT, *PAUTHENTICODE_CERT_PROMPT;
typedef struct _DRIVERBLOCK_PROMPT {
LPCTSTR lpszFile;
SDBENTRYINFO entryinfo;
} DRIVERBLOCK_PROMPT, *PDRIVERBLOCK_PROMPT;
DWORD
pSetupCommitSingleBackup(
IN PSP_FILE_QUEUE Queue,
IN PCTSTR FullTargetPath,
IN LONG TargetRootPath,
IN LONG TargetSubDir,
IN LONG TargetFilename,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth,
IN BOOL RenameExisting,
OUT PBOOL InUse
);
DWORD
pCommitCopyQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
DWORD
pCommitBackupQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
DWORD
pCommitDeleteQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
DWORD
pCommitRenameQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
UINT
pSetupCabinetQueueCallback(
IN PVOID Context,
IN UINT Notification,
IN UINT_PTR Param1,
IN UINT_PTR Param2
);
DWORD
pSetupCopySingleQueuedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
OUT PTSTR NewSourcePath,
IN BOOL IsMsgHandlerNativeCharWidth,
IN DWORD CopyStyleFlags
);
DWORD
pSetupCopySingleQueuedFileCabCase(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR CabinetName,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
VOID
pSetupSetPathOverrides(
IN PVOID StringTable,
IN OUT PTSTR RootPath,
IN OUT PTSTR SubPath,
IN LONG RootPathId,
IN LONG SubPathId,
IN PTSTR NewPath
);
VOID
pSetupBuildSourceForCopy(
IN PCTSTR UserRoot,
IN PCTSTR UserPath,
IN LONG MediaRoot,
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
OUT PTSTR FullPath
);
INT_PTR
CALLBACK
CertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
);
INT_PTR
CALLBACK
AuthenticodeCertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
);
INT_PTR
CALLBACK
NoAuthenticodeCertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
);
INT_PTR
CALLBACK
DriverBlockDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
);
VOID
RestoreBootReplacedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode
);
VOID
pSetupExemptFileFromProtection(
IN PCTSTR FileName,
IN DWORD FileChangeFlags,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
OUT PDWORD QueueNodeFlags OPTIONAL
);
VOID
pSetupUninstallNewCatalogNodes(
IN PSP_FILE_QUEUE Queue,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
);
BOOL
_SetupCommitFileQueue(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Implementation for SetupCommitFileQueue; handles ANSI and Unicode
callback routines.
Arguments:
Same as for SetupCommitFileQueue().
IsMsgHandlerNativeCharWidth - indicates whether the MsgHandler callback
expects native char width args (or ansi ones, in the unicode build
of this dll).
Return Value:
Boolean value indicating outcome. If FALSE, the GetLastError() indicates
cause of failure.
--*/
{
PSP_FILE_QUEUE Queue;
DWORD rc;
BOOL Success = TRUE;
BOOL ChangedThreadLogContext = FALSE;
PSETUP_LOG_CONTEXT SavedLogContext = NULL;
PSETUP_LOG_CONTEXT LogContext = NULL;
//
// Queue handle is actually a pointer to the queue structure.
//
Queue = (PSP_FILE_QUEUE)QueueHandle;
//
// do a quick handle validation before anything else
//
try {
Success = ((Queue != NULL) && (Queue != INVALID_HANDLE_VALUE) && (Queue->Signature == SP_FILE_QUEUE_SIG));
if (Success) {
LogContext = Queue->LogContext;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
if (!Success) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
//
// If there's nothing to do, bail now. This prevents an empty
// progress dialog from flashing on the screen. Don't return out
// of the body of the try -- that is bad news performance-wise.
//
try {
Success = (!Queue->DeleteNodeCount && !Queue->RenameNodeCount && !Queue->CopyNodeCount && !Queue->BackupNodeCount);
} except (EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
if(Success) {
//
// We are successful in that we had no file operations to do. However,
// we still need to validate the queued catalogs at this time, because
// we always do validation in the context of file copying. If we don't
// do this, we have a hole where a device INF that doesn't copy files
// (e.g., a modem INF) can circumvent driver signing checking.
//
WriteLogEntry(
LogContext,
SETUP_LOG_TIME,
MSG_LOG_BEGIN_VERIFY3_CAT_TIME,
NULL); // text message
rc = _SetupVerifyQueuedCatalogs(Owner,
Queue,
VERCAT_INSTALL_INF_AND_CAT,
NULL,
NULL
);
WriteLogEntry(
LogContext,
SETUP_LOG_TIME,
MSG_LOG_END_VERIFY3_CAT_TIME,
NULL); // text message
if (rc == NO_ERROR) {
//
// If we performed a backup and this is a device install then call
// the pSetupCompleteBackup API to create the Reinstall instance
// subkey and do other device rollback cleanup.
//
if (Queue->Flags & FQF_DEVICE_BACKUP) {
pSetupCompleteBackup(Queue);
}
Queue->Flags |= FQF_QUEUE_ALREADY_COMMITTED;
} else {
//
// Go uninstall any newly-copied INFs/PNFs/CATs.
//
pSetupUninstallNewCatalogNodes(Queue, LogContext);
}
SetLastError(rc);
return(rc == NO_ERROR);
}
ASSERT_HEAP_IS_VALID();
//
// make a note of default logging context for duration of queue processing
// this will catch, eg, INF being opened as part of a callback
//
MYASSERT(!ChangedThreadLogContext);
ChangedThreadLogContext = SetThreadLogContext(LogContext,&SavedLogContext);
if (ChangedThreadLogContext) {
//
// add one more ref to protext log context
//
RefLogContext(LogContext);
}
Success = pSetupCallMsgHandler(
LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTQUEUE,
(UINT_PTR)Owner,
0
);
if(!Success) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto final;
}
try {
//
// Verify catalogs/infs.
//
WriteLogEntry(
LogContext,
SETUP_LOG_TIME,
MSG_LOG_BEGIN_VERIFY2_CAT_TIME,
NULL); // text message
rc = _SetupVerifyQueuedCatalogs(Owner,
Queue,
VERCAT_INSTALL_INF_AND_CAT,
NULL,
NULL
);
WriteLogEntry(
LogContext,
SETUP_LOG_TIME,
MSG_LOG_END_VERIFY2_CAT_TIME,
NULL); // text message
Success = (rc == NO_ERROR);
if(rc != NO_ERROR) {
goto Bail;
}
ASSERT_HEAP_IS_VALID();
//
// Handle backup first
// don't commit if there's nothing to do
//
rc = Queue->BackupNodeCount
? pCommitBackupQueue(Queue,MsgHandler,Context,IsMsgHandlerNativeCharWidth)
: NO_ERROR;
Success = (rc == NO_ERROR);
ASSERT_HEAP_IS_VALID();
if (!Success) {
goto Bail;
}
//
// Handle deletes
// now done after backups, but may incorporate a per-delete backup
// don't commit if there's nothing to do
//
rc = Queue->DeleteNodeCount
? pCommitDeleteQueue(Queue,MsgHandler,Context,IsMsgHandlerNativeCharWidth)
: NO_ERROR;
Success = (rc == NO_ERROR);
ASSERT_HEAP_IS_VALID();
if (!Success) {
goto Bail;
}
//
// Handle renames next.
// don't commit if there's nothing to do
//
rc = Queue->RenameNodeCount
? pCommitRenameQueue(Queue,MsgHandler,Context,IsMsgHandlerNativeCharWidth)
: NO_ERROR;
Success = (rc == NO_ERROR);
ASSERT_HEAP_IS_VALID();
if (!Success) {
goto Bail;
}
//
// Handle copies last. Don't bother calling the copy commit routine
// if there are no files to copy.
//
rc = Queue->CopyNodeCount
? pCommitCopyQueue(Queue,MsgHandler,Context,IsMsgHandlerNativeCharWidth)
: NO_ERROR;
Success = (rc == NO_ERROR);
ASSERT_HEAP_IS_VALID();
if (!Success) {
goto Bail;
}
rc = DoAllDelayedMoves(Queue);
Success = (rc == NO_ERROR);
if(Success) {
//
// Set a flag indicating we've committed the file queue (used to keep
// us from attempting to prune the queue after having committed it).
//
Queue->Flags |= FQF_QUEUE_ALREADY_COMMITTED;
}
//
// If we performed a backup and this is a device install then call
// the pSetupCompleteBackup API to create the Reinstall instance
// subkey and do other device rollback cleanup.
//
if (Queue->Flags & FQF_DEVICE_BACKUP) {
pSetupCompleteBackup(Queue);
}
Bail:
;
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
rc = ERROR_INVALID_DATA;
}
pSetupCallMsgHandler(
LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDQUEUE,
Success,
0
);
pSetupUnwindAll(Queue, Success);
final:
//
// If we didn't succeed, then uninstall any new INFs/PNFs/CATs we may have
// installed.
//
if(!Success) {
pSetupUninstallNewCatalogNodes(Queue, LogContext);
}
if (ChangedThreadLogContext) {
//
// restore thread log context
//
SetThreadLogContext(SavedLogContext,NULL);
DeleteLogContext(LogContext); // counter RefLogContext
}
SetLastError(rc);
return(Success);
}
//
// ANSI version. Also need undecorated (Unicode) version for compatibility
// with apps that were linked before we had A and W versions.
//
BOOL
SetupCommitFileQueueA(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_A MsgHandler,
IN PVOID Context
)
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,FALSE));
}
#undef SetupCommitFileQueue
SetupCommitFileQueue(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_W MsgHandler,
IN PVOID Context
)
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,TRUE));
}
BOOL
SetupCommitFileQueueW(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK MsgHandler,
IN PVOID Context
)
/*++
Routine Description:
Perform file operations enqueued on a setup file queue.
Arguments:
OwnerWindow - if specified, supplies the window handle of a window
that is to be used as the parent of any progress dialogs.
QueueHandle - supplies a handle to a setup file queue, as returned
by SetupOpenFileQueue.
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
Return Value:
Boolean value indicating outcome.
--*/
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,TRUE));
}
DWORD
pCommitBackupQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Process the backup Queue
Backup each file specified in the queue if it exists
File is marked as backup
Location of backup is recorded
Files are not added to unwind queue here
They get added to unwind queue the first time they are potentially modified
See also pCommitDeleteQueue, pCommitRenameQueue and pCommitCopyQueue
Arguments:
Queue - queue that contains the backup sub-queue
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
IsMsgHandlerNativeCharWidth - For Unicode/Ansi support
Return Value:
DWORD indicating status or success
--*/
{
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
UINT u;
BOOL b;
DWORD rc;
PCTSTR FullTargetPath,FullBackupPath;
FILEPATHS FilePaths;
BOOL Skipped = FALSE;
DWORD BackupFlags = SP_BACKUP_BACKUPPASS;
MYASSERT(Queue->BackupNodeCount);
b = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_BACKUP,
Queue->BackupNodeCount
);
if(!b) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
for(QueueNode=Queue->BackupQueue; QueueNode; QueueNode=QueueNode->Next) {
//
// Form the full path of the file to be backed up
//
FullBackupPath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->SourceRootPath,
QueueNode->SourcePath,
QueueNode->SourceFilename
);
if(!FullBackupPath) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
FullTargetPath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetPath) {
MyFree(FullBackupPath);
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
FilePaths.Source = FullTargetPath; // copying from
FilePaths.Target = FullBackupPath; // copying to (backup)
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = BackupFlags;
Skipped = FALSE;
//
// Inform the callback that we are about to start a backup operation.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTBACKUP,
(UINT_PTR)&FilePaths,
FILEOP_BACKUP
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullTargetPath);
MyFree(FullBackupPath);
goto clean0;
}
if(u == FILEOP_DOIT) {
//
// Attempt the backup. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
//SetFileAttributes(FullTargetPath,FILE_ATTRIBUTE_NORMAL);
do {
rc = pSetupBackupFile((HSPFILEQ)Queue,
FullTargetPath,
FullBackupPath,
-1, // TargetID not known
QueueNode->TargetDirectory, // what to backup
-1, // Queue Node's don't maintain this intermediate path
QueueNode->TargetFilename,
QueueNode->SourceRootPath, // backup as...
QueueNode->SourcePath,
QueueNode->SourceFilename,
&b
);
if (rc == NO_ERROR) {
if (b) {
// delayed (in use)
QueueNode->InternalFlags |= INUSE_IN_USE;
//
// Tell the callback.
//
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = FILEOP_BACKUP;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_FILEOPDELAYED,
(UINT_PTR)&FilePaths,
0
);
}
} else {
FilePaths.Win32Error = rc;
FilePaths.Flags = BackupFlags;
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_BACKUPERROR,
(UINT_PTR)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullTargetPath);
MyFree(FullBackupPath);
goto clean0;
}
if(u == FILEOP_SKIP) {
// we skipped the backup
Skipped = TRUE;
break;
}
}
} while(rc != NO_ERROR);
} else {
// we skipped the backup
Skipped = TRUE;
rc = NO_ERROR;
}
FilePaths.Win32Error = rc;
FilePaths.Flags = BackupFlags;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDBACKUP,
(UINT_PTR)&FilePaths,
0
);
MyFree(FullTargetPath);
MyFree(FullBackupPath);
}
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_BACKUP,
0
);
rc = NO_ERROR;
clean0:
SetLastError(rc);
return rc;
}
DWORD
pSetupCommitSingleBackup(
IN PSP_FILE_QUEUE Queue,
IN PCTSTR FullTargetPath,
IN LONG TargetRootPath,
IN LONG TargetSubDir,
IN LONG TargetFilename,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth,
IN BOOL RenameExisting,
OUT PBOOL InUse
)
/*++
Routine Description:
Check a single file that is potentially about to be modified
If the target file doesn't exist, then this routine does nothing
If the target file hasn't been backed up, back it up
If the target file has been backed up, but is not on unwind queue,
add to unwind queue
The default target location of the backup is used, which is either
into a backup directory tree, or a temporary backup location
Location of backup is recorded
Arguments:
Queue - queue that contains the backup sub-queue
FullTargetPath - String giving target path, or NULL if not formed
TargetRootPath - String ID giving RootPath, or -1 if not specified
TargetSubDir - String ID giving SubDir (relative to RootPath),
or -1 if not specified
TargetFilename - String ID giving Filename, or -1 if not specified
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
IsMsgHandlerNativeCharWidth - For Unicode/Ansi support
RenameExisting - Should existing file be renamed?
InUse - if specified, set to indicate if file is in use or not
This should never be the case
Return Value:
DWORD indicating status or success
--*/
{
UINT u;
BOOL b;
DWORD rc;
DWORD rc2;
FILEPATHS FilePaths;
LONG TargetID;
PTSTR TargetPathLocal = NULL;
PSP_UNWIND_NODE UnwindNode = NULL;
SP_TARGET_ENT TargetInfo;
BOOL FileOfSameNameExists;
BOOL DoBackup = TRUE;
BOOL NeedUnwind = FALSE;
BOOL Skipped = FALSE;
WIN32_FILE_ATTRIBUTE_DATA FileAttribData;
UINT OldMode;
BOOL DoRename;
DWORD BackupFlags = SP_BACKUP_DEMANDPASS;
//
// used in this function to init time field
//
static const FILETIME zeroTime = {
0,0
};
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); // inhibit unexpected dialog boxes
MYASSERT(Queue);
if (FullTargetPath == NULL) {
TargetPathLocal = pSetupFormFullPath(
Queue->StringTable,
TargetRootPath,
TargetSubDir,
TargetFilename);
if(!TargetPathLocal) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
FullTargetPath = TargetPathLocal;
}
FileOfSameNameExists = GetFileAttributesEx(FullTargetPath, GetFileExInfoStandard, &FileAttribData);
if (!FileOfSameNameExists) {
// file doesn't exist, so no need to backup
rc = NO_ERROR;
goto clean0;
}
rc = pSetupBackupGetTargetByPath((HSPFILEQ)Queue,
NULL, // use Queue's string table
FullTargetPath,
TargetRootPath,
TargetSubDir,
TargetFilename,
&TargetID,
&TargetInfo
);
if (rc != NO_ERROR) {
// failed for some strange reason
goto clean0;
}
if (TargetInfo.InternalFlags & SP_TEFLG_INUSE) {
//
// was "inuse'd" before
// we mark as still INUSE
if (InUse != NULL) {
*InUse = TRUE;
}
//
// Don't consider this an error, unless we were supposed to rename the
// existing file.
//
rc = RenameExisting ? ERROR_SHARING_VIOLATION : NO_ERROR;
goto clean0;
}
if (TargetInfo.InternalFlags & SP_TEFLG_SKIPPED) {
//
// was skipped before
// we can't rely on it now
//
rc = NO_ERROR;
goto clean0;
}
//
// If we've been asked to backup the existing file, then make sure the
// SP_TEFLG_RENAMEEXISTING flag is set in the TargetInfo. Also, figure out
// if we've already done the rename.
//
if(RenameExisting &&
!(TargetInfo.InternalFlags & SP_TEFLG_RENAMEEXISTING)) {
//
// We'd better not think we already renamed this file!
//
MYASSERT(!(TargetInfo.InternalFlags & SP_TEFLG_MOVED));
TargetInfo.InternalFlags |= SP_TEFLG_RENAMEEXISTING;
//
// update internal info (this call should never fail)
//
pSetupBackupSetTargetByID((HSPFILEQ)Queue,
TargetID,
&TargetInfo
);
}
//
// Figure out whether we've been asked to rename the existing file to a
// temp name in the same directory, but haven't yet done so.
//
DoRename = ((TargetInfo.InternalFlags & (SP_TEFLG_RENAMEEXISTING | SP_TEFLG_MOVED)) == SP_TEFLG_RENAMEEXISTING);
if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
//
// already backed up
//
DoBackup = FALSE;
if((TargetInfo.InternalFlags & SP_TEFLG_UNWIND) && !DoRename) {
//
// already added to unwind queue, and we don't need to do a rename--
// don't need to do anything at all
//
rc = NO_ERROR;
goto clean0;
}
//
// we don't need to backup
// but we still need to add to unwind queue, rename the existing file,
// or both.
//
}
if(DoBackup) {
BackupFlags |= SP_BACKUP_DEMANDPASS;
}
if(DoRename) {
BackupFlags |= SP_BACKUP_BOOTFILE | SP_BACKUP_SPECIAL;
}
FilePaths.Source = FullTargetPath; // what we are backing up
FilePaths.Target = NULL; // indicates an automatic backup
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = BackupFlags;
if (DoRename) {
pSetupExemptFileFromProtection(
FullTargetPath,
SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
| SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
Queue->LogContext,
NULL
);
}
if (DoBackup && (Queue->Flags & FQF_BACKUP_AWARE)) {
//
// Inform the callback that we are about to start a backup operation.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTBACKUP,
(UINT_PTR)&FilePaths,
FILEOP_BACKUP
);
} else {
//
// no backup, or not backup aware, assume a default
//
u = FILEOP_DOIT;
}
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
if((u == FILEOP_DOIT) || (BackupFlags & SP_BACKUP_SPECIAL)) {
//
// Attempt the backup. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
//SetFileAttributes(FullTargetPath,FILE_ATTRIBUTE_NORMAL);
//
// Setup an unwind node, unless we already have one.
//
if(!(TargetInfo.InternalFlags & SP_TEFLG_UNWIND)) {
UnwindNode = MyMalloc(sizeof(SP_UNWIND_NODE));
if (UnwindNode == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
UnwindNode->NextNode = Queue->UnwindQueue;
UnwindNode->TargetID = TargetID;
if (RetreiveFileSecurity( FullTargetPath, &(UnwindNode->SecurityDesc)) != NO_ERROR) {
// failed, but not fatal
UnwindNode->SecurityDesc = NULL;
}
if (GetSetFileTimestamp( FullTargetPath, &(UnwindNode->CreateTime),
&(UnwindNode->AccessTime),
&(UnwindNode->WriteTime),
FALSE) != NO_ERROR) {
// failed, but not fatal
UnwindNode->CreateTime = zeroTime;
UnwindNode->AccessTime = zeroTime;
UnwindNode->WriteTime = zeroTime;
}
}
if (DoBackup || DoRename) {
do {
rc = pSetupBackupFile((HSPFILEQ)Queue,
FullTargetPath, // since we know this, pass it
NULL, // automatic destination
TargetID, // we got this earlier
TargetRootPath, // since we know this, pass it
TargetSubDir,
TargetFilename,
-1, // use the details from TargetID (or temp)
-1,
-1,
&b // in use (should always return FALSE)
);
if (rc == NO_ERROR) {
if (InUse != NULL) {
*InUse = b;
}
if (b) {
//
// if file is in use, callback can decide what to do
//
if (Queue->Flags & FQF_BACKUP_AWARE) {
//
// Tell the callback.
//
FilePaths.Win32Error = ERROR_SHARING_VIOLATION;
FilePaths.Flags = BackupFlags;
if (Queue->Flags & FQF_BACKUP_AWARE) {
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_BACKUPERROR,
(UINT_PTR)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
} else {
rc = ERROR_OPERATION_ABORTED;
goto clean0;
}
}
} else {
//
// success!!!!!
// we would have to unwind this if setup fails
//
NeedUnwind = TRUE;
}
} else {
FilePaths.Win32Error = rc;
FilePaths.Flags = BackupFlags;
if (Queue->Flags & FQF_BACKUP_AWARE) {
//
// inform about error
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_BACKUPERROR,
(UINT_PTR)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
} else {
//
// if caller is not backup aware, abort
//
rc = ERROR_OPERATION_ABORTED;
goto clean0;
}
if(u == FILEOP_SKIP) {
//
// we skipped the backup
//
Skipped = TRUE;
break;
}
}
} while(rc != NO_ERROR);
} else {
//
// didn't need to backup, only need to add to unwind queue
//
NeedUnwind = TRUE;
}
} else {
//
// we skipped the backup
//
Skipped = TRUE;
rc = NO_ERROR;
}
if (DoBackup) {
FilePaths.Win32Error = rc;
if (Queue->Flags & FQF_BACKUP_AWARE) {
//
// report result only if backup aware
//
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDBACKUP,
(UINT_PTR)&FilePaths,
0
);
}
}
if (Skipped) {
//
// once we return, file may get overwritten or deleted
// we have to save the fact it has been skipped once
// so we always skip this file
//
if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, TargetID, &TargetInfo) == NO_ERROR) {
//
// flag the file should always be skipped
//
TargetInfo.InternalFlags|=SP_TEFLG_SKIPPED;
pSetupBackupSetTargetByID((HSPFILEQ)Queue, TargetID, &TargetInfo);
}
}
else if (NeedUnwind) {
//
// We only want to add this to unwind queue
//
if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, TargetID, &TargetInfo) == NO_ERROR) {
if ((TargetInfo.InternalFlags&SP_TEFLG_UNWIND)==FALSE) {
//
// node needs to be added to unwind queue
// we only ever do this once
//
Queue->UnwindQueue = UnwindNode;
//
// set to NULL so we don't clean it up later
//
UnwindNode = NULL;
//
// flag that we've added it to unwind queue
// so we don't try and do it again later
//
TargetInfo.InternalFlags|=SP_TEFLG_UNWIND;
pSetupBackupSetTargetByID((HSPFILEQ)Queue, TargetID, &TargetInfo);
}
}
}
rc = NO_ERROR;
clean0:
if (UnwindNode != NULL) {
//
// we allocated, but didn't use this structure
//
if (UnwindNode->SecurityDesc != NULL) {
MyFree(UnwindNode->SecurityDesc);
}
MyFree(UnwindNode);
}
if (TargetPathLocal != NULL) {
MyFree(TargetPathLocal);
}
SetErrorMode(OldMode);
SetLastError(rc);
return rc;
}
DWORD
pCommitDeleteQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Process the delete Queue
Delete each file specified in the queue
Files are backed up before they are deleted (if not already backed up)
See also pCommitBackupQueue, pCommitRenameQueue and pCommitCopyQueue
Arguments:
Queue - queue that contains the delete sub-queue
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
IsMsgHandlerNativeCharWidth - For Unicode/Ansi support
Return Value:
DWORD indicating status or success
--*/
{
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
UINT u;
BOOL b;
DWORD rc;
PCTSTR FullTargetPath;
FILEPATHS FilePaths;
BOOL BackupInUse = FALSE;
BOOL TargetIsProtected;
MYASSERT(Queue->DeleteNodeCount);
b = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_DELETE,
Queue->DeleteNodeCount
);
if(!b) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
for(QueueNode=Queue->DeleteQueue; QueueNode; QueueNode=QueueNode->Next) {
//
// Form the full path of the file to be deleted.
//
FullTargetPath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetPath) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Backup the file we're about to delete
//
if((rc=pSetupDoLastKnownGoodBackup(Queue,
FullTargetPath,
LASTGOOD_OPERATION_DELETE,
NULL)) != NO_ERROR) {
MyFree(FullTargetPath);
goto clean0;
}
rc = pSetupCommitSingleBackup(Queue,
FullTargetPath,
QueueNode->TargetDirectory,
-1,
QueueNode->TargetFilename,
MsgHandler,
Context,
IsMsgHandlerNativeCharWidth,
FALSE,
&BackupInUse
);
if (rc != NO_ERROR) {
MyFree(FullTargetPath);
goto clean0;
}
FilePaths.Source = NULL;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = 0;
//
// Inform the callback that we are about to start a delete operation.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTDELETE,
(UINT_PTR)&FilePaths,
FILEOP_DELETE
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullTargetPath);
goto clean0;
}
if(u == FILEOP_DOIT) {
//
// Attempt the delete. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
SetFileAttributes(FullTargetPath,FILE_ATTRIBUTE_NORMAL);
do {
if (BackupInUse) {
rc = ERROR_SHARING_VIOLATION;
} else {
rc = DeleteFile(FullTargetPath) ? NO_ERROR : GetLastError();
}
if((rc == ERROR_ACCESS_DENIED)
|| (rc == ERROR_SHARING_VIOLATION)
|| (rc == ERROR_USER_MAPPED_FILE)) {
//
// The file is probably in use.
//
if(QueueNode->InternalFlags & IQF_DELAYED_DELETE_OK) {
//
// Inf wanted delete on next reboot. Check to see if
// we're being asked to delete a protected system file.
// If so (and all the catalog nodes associated with the
// queue were OK), then we'll allow this to happen.
// Otherwise, we'll silently skip the deletion (and log
// it).
//
MYASSERT((Queue->Flags & FQF_DID_CATALOGS_OK) ||
(Queue->Flags & FQF_DID_CATALOGS_FAILED));
if(Queue->Flags & FQF_DID_CATALOGS_OK) {
QueueNode->InternalFlags |= INUSE_IN_USE;
TargetIsProtected = IsFileProtected(FullTargetPath,
Queue->LogContext,
NULL
);
if(b = PostDelayedMove(Queue,
FullTargetPath,
NULL,
-1,
TargetIsProtected)) {
//
// Tell the callback.
//
FilePaths.Source = NULL;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = FILEOP_DELETE;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_FILEOPDELAYED,
(UINT_PTR)&FilePaths,
0
);
}
} else {
//
// We're installing an unsigned package. Skip the
// delayed delete operation, and generate a log
// entry about this.
//
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_DELAYED_DELETE_SKIPPED_FOR_SFC,
NULL,
FullTargetPath
);
}
} else {
//
// Just skip this file.
//
b = TRUE;
}
rc = b ? NO_ERROR : GetLastError();
if(rc) {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELAYDELETE_FILE_ERROR,
NULL,
FullTargetPath);
WriteLogError(Queue->LogContext,SETUP_LOG_ERROR,rc);
} else {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_DELAYDELETED_FILE,
NULL,
FullTargetPath);
}
} else if(rc) {
WriteLogEntry(
Queue->LogContext,
DEL_ERR_LOG_LEVEL(rc) | SETUP_LOG_BUFFER,
MSG_LOG_DELETE_FILE_ERROR,
NULL,
FullTargetPath);
WriteLogError(Queue->LogContext,DEL_ERR_LOG_LEVEL(rc),rc);
} else {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_DELETED_FILE,
NULL,
FullTargetPath);
}
if( rc == NO_ERROR )
{
rc = pSetupCallSCE(
ST_SCE_DELETE,
FullTargetPath,
NULL,
NULL,
-1,
NULL
);
SetLastError( rc );
}
if(rc != NO_ERROR) {
FilePaths.Win32Error = rc;
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_DELETEERROR,
(UINT_PTR)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullTargetPath);
goto clean0;
}
if(u == FILEOP_SKIP) {
break;
}
}
} while(rc != NO_ERROR);
} else {
rc = NO_ERROR;
}
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDDELETE,
(UINT_PTR)&FilePaths,
0
);
MyFree(FullTargetPath);
}
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_DELETE,
0
);
rc = NO_ERROR;
clean0:
SetLastError(rc);
return rc;
}
DWORD
pCommitRenameQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Process the rename Queue
Rename each file specified in the queue
Files are backed up before they are renamed (if not already backed up)
If the target exists, it is also backed up (if not already backed up)
Performance: this can get optimized by treating the newly named files
as a backup
See also pCommitBackupQueue, pCommitDeleteQueue and pCommitCopyQueue
Arguments:
Queue - queue that contains the rename sub-queue
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
IsMsgHandlerNativeCharWidth - For Unicode/Ansi support
Return Value:
DWORD indicating status or success
--*/
{
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
UINT u;
BOOL b;
DWORD rc;
PCTSTR FullTargetPath;
PCTSTR FullSourcePath;
FILEPATHS FilePaths;
BOOL BackupInUse = FALSE;
BOOL TargetIsProtected;
MYASSERT(Queue->RenameNodeCount);
b = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_RENAME,
Queue->RenameNodeCount
);
if(!b) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
goto clean0;
}
for(QueueNode=Queue->RenameQueue; QueueNode; QueueNode=QueueNode->Next) {
//
// Form the full source path of the file to be renamed.
//
FullSourcePath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->SourcePath,
QueueNode->SourceFilename,
-1
);
if(!FullSourcePath) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Form the full target path of the file to be renamed.
//
FullTargetPath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory == -1 ? QueueNode->SourcePath : QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetPath) {
MyFree(FullSourcePath);
rc = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Backup the file we may be overwriting
//
if((rc=pSetupDoLastKnownGoodBackup(Queue,
FullTargetPath,
0,
NULL)) != NO_ERROR) {
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
rc = pSetupCommitSingleBackup(Queue,
FullTargetPath,
QueueNode->TargetDirectory == -1 ? QueueNode->SourcePath : QueueNode->TargetDirectory,
-1, // we don't use this
QueueNode->TargetFilename,
MsgHandler,
Context,
IsMsgHandlerNativeCharWidth,
FALSE,
&BackupInUse
);
if (rc != NO_ERROR) {
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
//
// Backup the file we're about to rename
//
if((rc=pSetupDoLastKnownGoodBackup(Queue,
FullSourcePath,
LASTGOOD_OPERATION_DELETE,
NULL)) != NO_ERROR) {
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
rc = pSetupCommitSingleBackup(Queue,
FullSourcePath,
QueueNode->SourcePath,
-1, // we don't use this????
QueueNode->SourceFilename,
MsgHandler,
Context,
IsMsgHandlerNativeCharWidth,
FALSE,
&b
);
if (rc != NO_ERROR) {
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
if (b) {
//
// BackupInUse is the "OR" of the two backup In-Use flags
//
BackupInUse = TRUE;
}
FilePaths.Source = FullSourcePath;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
//
// Inform the callback that we are about to start a rename operation.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTRENAME,
(UINT_PTR)&FilePaths,
FILEOP_RENAME
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
if(u == FILEOP_DOIT) {
//
// Attempt the rename. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
do {
if (BackupInUse) {
//
// backup is in use, must delay op. Check to see if either
// the source or target files are protected system files.
// If so (and all the catalog nodes associated with the
// queue were OK), then we'll allos this to happen.
// Otherwise, we'll silently fail the rename (and log it).
//
MYASSERT((Queue->Flags & FQF_DID_CATALOGS_OK) ||
(Queue->Flags & FQF_DID_CATALOGS_FAILED));
if(Queue->Flags & FQF_DID_CATALOGS_OK) {
TargetIsProtected = IsFileProtected(FullSourcePath,
Queue->LogContext,
NULL
);
if(!TargetIsProtected) {
TargetIsProtected = IsFileProtected(FullTargetPath,
Queue->LogContext,
NULL
);
}
if(b = PostDelayedMove(Queue,
FullSourcePath,
FullTargetPath,
-1,
TargetIsProtected)) {
rc = NO_ERROR;
}
else
{
rc = GetLastError();
}
if(rc) {
WriteLogEntry(
Queue->LogContext,
DEL_ERR_LOG_LEVEL(rc) | SETUP_LOG_BUFFER,
MSG_LOG_DELAYRENAME_FILE_ERROR,
NULL,
FullSourcePath,
FullTargetPath);
WriteLogError(Queue->LogContext,DEL_ERR_LOG_LEVEL(rc),rc);
} else {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_DELAYRENAMED_FILE,
NULL,
FullSourcePath,
FullTargetPath);
}
} else {
//
// We're installing an unsigned package. Skip the
// delayed rename operation, and generate a log
// entry about this.
//
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_DELAYED_MOVE_SKIPPED_FOR_SFC,
NULL,
FullTargetPath
);
//
// act as if no error occurred.
//
rc = NO_ERROR;
}
} else {
rc = MoveFile(FullSourcePath,FullTargetPath) ? NO_ERROR : GetLastError();
if(rc) {
WriteLogEntry(
Queue->LogContext,
DEL_ERR_LOG_LEVEL(rc) | SETUP_LOG_BUFFER,
MSG_LOG_RENAME_FILE_ERROR,
NULL,
FullSourcePath,
FullTargetPath);
WriteLogError(Queue->LogContext,DEL_ERR_LOG_LEVEL(rc),rc);
} else {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_RENAMED_FILE,
NULL,
FullSourcePath,
FullTargetPath);
}
}
if( rc == NO_ERROR )
{
rc = pSetupCallSCE(
ST_SCE_RENAME,
FullSourcePath,
NULL,
FullTargetPath,
-1,
NULL
);
SetLastError( rc );
}
if((rc == ERROR_FILE_NOT_FOUND) || (rc == ERROR_PATH_NOT_FOUND)) {
rc = NO_ERROR;
}
if(rc != NO_ERROR) {
FilePaths.Win32Error = rc;
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_RENAMEERROR,
(UINT_PTR)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
MyFree(FullSourcePath);
MyFree(FullTargetPath);
goto clean0;
}
if(u == FILEOP_SKIP) {
break;
}
}
} while(rc != NO_ERROR);
} else {
rc = NO_ERROR;
}
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDRENAME,
(UINT_PTR)&FilePaths,
0
);
MyFree(FullSourcePath);
MyFree(FullTargetPath);
}
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_RENAME,
0
);
rc = NO_ERROR;
clean0:
SetLastError(rc);
return rc;
}
DWORD
pCommitCopyQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Process the copy sub-Queues
Copy each file specified in the sub-queues
Files are backed up before they are overwritten (if not already backed up)
See also pCommitBackupQueue, pCommitDeleteQueue and pCommitRenameQueue
Arguments:
Queue - queue that contains the copy sub-queues
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
IsMsgHandlerNativeCharWidth - For Unicode/Ansi support
Return Value:
DWORD indicating status or success
--*/
{
PSOURCE_MEDIA_INFO SourceMediaInfo;
SOURCE_MEDIA SourceMedia;
PTCHAR p, temp;
UINT SourcePathLen;
UINT u;
DWORD rc;
Q_CAB_CB_DATA QData;
BOOL b;
BOOL FirstIteration;
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
TCHAR UserSourceRoot[MAX_PATH];
TCHAR UserSourcePath[MAX_PATH];
TCHAR FullSourcePath[MAX_PATH];
TCHAR UserOverride[MAX_PATH];
LPCTSTR RestorePath = NULL;
UINT DriveType;
BOOL IsRemovable, AnyProcessed, AnyNotProcessed, SkipMedia;
BOOL SpecialMedia = FALSE;
BOOL LocateCab;
PCTSTR MediaRoot;
DWORD MediaLogTag;
LONG Cabfile;
LONG Tagfile;
//
// The caller is supposed to skip calling us if there are no files
// to be copied.
//
MYASSERT(Queue->CopyNodeCount);
//
// Inform the callback that we are starting.
//
b = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_COPY,
Queue->CopyNodeCount
);
if(!b) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
return(rc);
}
if(Queue->RestorePathID != -1) {
RestorePath = pSetupStringTableStringFromId(Queue->StringTable, Queue->RestorePathID);
DiskPromptGetDriveType(RestorePath, &DriveType, &IsRemovable);
if(IsRemovable) {
//
// do not allow restore from removable media
//
RestorePath = NULL;
}
}
//
// Initially, no user-specified override path exists.
//
UserSourceRoot[0] = TEXT('\0');
UserSourcePath[0] = TEXT('\0');
//
// The outermost loop iterates through all the source media descriptors.
//
for(SourceMediaInfo=Queue->SourceMediaList; SourceMediaInfo; SourceMediaInfo=SourceMediaInfo->Next) {
//
// If there are no files on this particular media, skip it.
// Otherwise get pointer to queue node for first file on this media.
//
if(!SourceMediaInfo->CopyNodeCount) {
continue;
}
MYASSERT(SourceMediaInfo->CopyQueue);
//
// if last media was special media (see long discussion above),
// then forget about any user override
//
if (SpecialMedia) {
UserSourceRoot[0] = TEXT('\0');
UserSourcePath[0] = TEXT('\0');
SpecialMedia = FALSE;
}
//
// see if this media is special media
//
if (SourceMediaInfo->Flags & ( SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH |
SMI_FLAG_USE_LOCAL_SPCACHE |
SMI_FLAG_USE_LOCAL_SOURCE_CAB ) ) {
SpecialMedia = TRUE;
}
//
// If we're in restore-mode
// we've been given a directory to restore from
// ignore the media root, and use restore-point root
// restore as many files as we can
//
// note, we check for file presence via FileExists
// rather than trying to determine file name
// since we'll always backup in uncompressed form
// with same name as listed in [SourceDisksNames]
//
if(RestorePath) {
//
// Restore Symantics - prior to prompting for media, see
// if we can restore backup
//
QueueNode = NULL;
for(queueNode = SourceMediaInfo->CopyQueue;
queueNode;
queueNode=queueNode->Next) {
pSetupBuildSourceForCopy(
RestorePath,
NULL,
SourceMediaInfo->SourceRootPath,
Queue,
queueNode,
FullSourcePath
);
//
// don't allow alternate sourcenames in this case
//
if(FileExists(FullSourcePath,NULL)) {
//
// backup exists, copy it
//
rc = pSetupCopySingleQueuedFile(
Queue,
queueNode,
FullSourcePath,
MsgHandler,
Context,
UserOverride,
IsMsgHandlerNativeCharWidth,
SP_COPY_ALREADYDECOMP // backup already decomp'd.
);
if(rc == NO_ERROR) {
//
// we restored this file through backup
// carry on to next file
//
queueNode->InternalFlags |= IQF_PROCESSED;
continue;
}
//
// we know backup existed so if this failed
// consider it major enough to abort restore
// (eg, file unsigned, user specified abort)
//
SetLastError(rc);
return(rc);
}
if(!QueueNode) {
//
// first problematic file
//
QueueNode = queueNode;
}
}
if(!QueueNode) {
//
// we copied all files of this media from backup
// carry on to next media
//
continue;
}
} else {
//
// not restoring, start at first file
//
QueueNode = SourceMediaInfo->CopyQueue;
}
//
// We will need to prompt for media, which requires some preparation.
// We need to get the first file in the queue for this media, because
// its path is where we will expect to find it or its cabinet or tag
// file. If there is no tag file, then we will look for the file
// itself.
//
FirstIteration = TRUE;
SkipMedia = FALSE;
LocateCab = FALSE;
Tagfile = SourceMediaInfo->Tagfile;
Cabfile = SourceMediaInfo->Cabfile;
RepromptMedia:
//
// The case where we have non-removeable media and the path was
// previously overridden must be handled specially. For example, we
// could have files queued on the same source root but different
// subdirs. If the user changes the network location, for example,
// we have to be careful or we'll ignore the change in subdirectories
// as we move among the media.
//
// To work around this, we check on non-removable media to see if the
// queue node we're presently working with is in a subdirectory. If it
// is, then we reset our UserSourcePath string.
//
// (andrewr)...I don't get this comment above. The current code
// iterates through each source media info structure, which doesn't include
// subdirectory information, only source root path information. If it
// does, then the caller is doing something really wierd, since they
// should be using the SourcePath to define subdirectories from one master
// root.
//
// It appears that the reasoning behind the code below is as follows:
//
// The assumption is that if we have removable media and multiple source
// paths, then we will have to swap media out of the drive. We don't
// override source root paths if we are dealing with removable media.
// If the source root path is non removable, then all of the source media
// is "tied together." If the user overrides the source root path, then
// we override subsequent fixed media source root paths.
//
// In the case of dealing with service pack source media or a local cab-file
// drivers cache, the source media info for a queue will not be tied together,
// even though we're dealing with fixed media.
//
// To reconcile the comments above and the reasoning it uses with the
// contradiction that svc pack media imposes, we have 2 options:
//
// 1. If we encounter flags that indicate one of our special cases, then don't
// use any user override for the new source media. (or, put another way,
// if we know that the last media was actually one of these special media,
// then don't allow an override of the normal media.
//
// 2. Introduce some sort of hueristic that determines if the prior source media
// and the current source media are similar. If they are, then go ahead and
// use any user specified override, otherwise use the proper path.
//
//
// For simplicities sake, I use approach 1 above. This is made a little simpler
// by following the following rule. When adding source media to the media list,
// insert special media (ie, has flags identifying the media as svc pack media)
// at the head of the list, insert normal media after that. By following this
// approach we know that we can just "zero out" the user overrides for the special
// media and we'll just do the right thing for the regular media.
//
// In the case where there is an explicit cab-file to use
// then we ask the user to point to cab-file instead of source file (first iteration)
//
MediaRoot = *UserSourceRoot
? UserSourceRoot
: pSetupStringTableStringFromId(Queue->StringTable, SourceMediaInfo->SourceRootPath);
DiskPromptGetDriveType(MediaRoot, &DriveType, &IsRemovable);
if(!IsRemovable && (QueueNode->SourcePath != -1)) {
*UserSourcePath = TEXT('\0');
}
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
QueueNode,
FullSourcePath
);
if (FirstIteration
&& (Tagfile != Cabfile)
&& (Cabfile != -1)) {
MYASSERT(!SkipMedia);
MYASSERT(!(SourceMediaInfo->Flags & SMI_FLAG_USE_LOCAL_SOURCE_CAB));
//
// build location of cab file
//
temp = _tcsrchr(FullSourcePath,TEXT('\\'));
MYASSERT( temp );
if(temp) {
*(temp+1) = 0;
} else {
FullSourcePath[0] = 0;
}
//
// obtain path of (potential) cab file
//
pSetupConcatenatePaths( FullSourcePath, pSetupStringTableStringFromId(Queue->StringTable,Cabfile), MAX_PATH, NULL );
LocateCab = TRUE;
} else {
LocateCab = FALSE;
}
if((p = _tcsrchr(FullSourcePath,TEXT('\\')))!=NULL) {
*p++ = TEXT('\0');
} else {
//
// I'm being pedantic here, this should never happen
//
MYASSERT(p);
p = FullSourcePath;
}
//
// Now FullSourcePath has the path part and p has the file part
// for the first file in the queue for this media (or explicit cab file)
// Get the media in the drive by calling the callback function.
//
// Although it would be nice to not have to
// call this callback if we know that we don't have to (there is media
// where the caller said there should be (local media, media already in, etc.)
// we do need to call this so that we afford the caller the luxury of
// changing their mind one last time.
//
// the only exception to this rule is if we are using the local driver
// cache cab-file. In this case, we don't want the user to ever get
// prompted for this file, so we skip any media prompting. We know that
// if we have media added that has this flag set, then the cab already exists
// and we can just use it (otherwise we wouldn't have initialized it in the
// first place, we'd just use the os source path!)
//
SourceMedia.Tagfile = (Tagfile != -1 && FirstIteration)
? pSetupStringTableStringFromId(
Queue->StringTable,
Tagfile
)
: NULL;
SourceMedia.Description = (SourceMediaInfo->Description != -1)
? pSetupStringTableStringFromId(
Queue->StringTable,
SourceMediaInfo->DescriptionDisplayName
)
: NULL;
SourceMedia.SourcePath = FullSourcePath;
SourceMedia.SourceFile = p;
SourceMedia.Flags = (QueueNode->StyleFlags & (SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP | SP_COPY_NOBROWSE));
MediaLogTag = AllocLogInfoSlotOrLevel(Queue->LogContext,SETUP_LOG_INFO,FALSE);
WriteLogEntry(
Queue->LogContext,
MediaLogTag,
MSG_LOG_NEEDMEDIA,
NULL,
SourceMedia.Tagfile ? SourceMedia.Tagfile : TEXT(""),
SourceMedia.Description ? SourceMedia.Description : TEXT(""),
SourceMedia.SourcePath ? SourceMedia.SourcePath : TEXT(""),
SourceMedia.SourceFile ? SourceMedia.SourceFile : TEXT(""),
SourceMedia.Flags
);
if( SkipMedia || (FirstIteration && (SourceMediaInfo->Flags & SMI_FLAG_USE_LOCAL_SOURCE_CAB)) ) {
u = FILEOP_DOIT;
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_NEEDMEDIA_AUTOSKIP,
NULL
);
} else {
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_NEEDMEDIA,
(UINT_PTR)&SourceMedia,
(UINT_PTR)UserOverride
);
}
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
MSG_LOG_NEEDMEDIA_ABORT,
NULL);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
rc
);
ReleaseLogInfoSlot(Queue->LogContext,MediaLogTag);
MediaLogTag = 0;
SetLastError(rc);
return(rc);
}
if(u == FILEOP_SKIP) {
//
// If this file was a bootfile replacement, then we need to restore
// the original file that was renamed to a temporary filename.
//
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_WARNING,
MSG_LOG_NEEDMEDIA_SKIP,
NULL
);
ReleaseLogInfoSlot(Queue->LogContext,MediaLogTag);
MediaLogTag = 0;
if(QueueNode->StyleFlags & SP_COPY_REPLACE_BOOT_FILE) {
RestoreBootReplacedFile(Queue, QueueNode);
}
//
// If there are more files on this media, then try another one.
// Otherwise we're done with this media.
//
QueueNode->InternalFlags |= IQF_PROCESSED;
for(QueueNode=QueueNode->Next; QueueNode; QueueNode=QueueNode->Next) {
if(!(QueueNode->InternalFlags & IQF_PROCESSED)) {
FirstIteration = FALSE;
goto RepromptMedia;
}
}
continue;
}
if(u == FILEOP_NEWPATH) {
//
// User gave us a new source path. See which parts of the new path
// match the existing path/overrides we are using.
//
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_NEEDMEDIA_NEWPATH,
NULL,
UserOverride
);
ReleaseLogInfoSlot(Queue->LogContext,MediaLogTag);
MediaLogTag = 0;
pSetupSetPathOverrides(
Queue->StringTable,
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
QueueNode->SourcePath,
UserOverride
);
}
//
// logging specific stuff
//
if(MediaLogTag!=0) {
//
// we explicitly cleared MediaLogTag for each case we handled
//
if (u != FILEOP_DOIT) {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_WARNING,
MSG_LOG_NEEDMEDIA_BADRESULT,
NULL,
u);
}
ReleaseLogInfoSlot(Queue->LogContext,MediaLogTag);
MediaLogTag = 0;
}
//
// If we get here, the media is now accessible.
// Some or all of the files might be in a cabinet whose name is the tagfile.
//
// NOTE: Win95 used the tagfile field to be the cabinet name instead.
// If present it is used as a tagfile of sorts. The absence of a tagfile
// means the files are not in cabinets. For NT, we don't bother
// with all of this but instead try to be a little smarter.
//
// Scan the media for all source files we expect to find on it.
// If we find a file, process it. Later we hit the cabinet and only
// process the files we didn't already find outside the cabinet.
//
// exception to this is "explicit cabinet"
//
if(LocateCab) {
//
// an explicit cabinet was specified
// this is first iteration
// we've gone through NEED_MEDIA to obtain disk for this cabinet
// don't try to process files outside cabinet
// we know there is at least one file not processed
//
b = TRUE;
queueNode=QueueNode;
} else {
//
// tagfile may also be a cabfile
// but process all files outside the cabfile first
//
for(queueNode=QueueNode; queueNode; queueNode=queueNode->Next) {
if(queueNode->InternalFlags & IQF_PROCESSED) {
//
// Already processed. Skip to next file.
//
continue;
}
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
queueNode,
FullSourcePath
);
rc = SetupDetermineSourceFileName(FullSourcePath,&b,&p,NULL);
if(rc == NO_ERROR || SkipMedia) {
//
// Found the file outside a cabinet. Process it now.
//
if(rc == NO_ERROR) {
rc = pSetupCopySingleQueuedFile(
Queue,
queueNode,
p,
MsgHandler,
Context,
UserOverride,
IsMsgHandlerNativeCharWidth,
0
);
MyFree(p);
} else {
//
// We didn't find the source file, but we're going to try
// to copy it anyway since we've decided not to skip the
// prompt for media.
//
rc = pSetupCopySingleQueuedFile(
Queue,
queueNode,
FullSourcePath,
MsgHandler,
Context,
UserOverride,
IsMsgHandlerNativeCharWidth,
0
);
}
if(rc != NO_ERROR) {
return(rc);
}
//
// See if we have a new source path.
//
if(UserOverride[0]) {
pSetupSetPathOverrides(
Queue->StringTable,
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
queueNode->SourcePath,
UserOverride
);
}
}
}
//
// See if any files still need to be processed.
//
for(b=FALSE,queueNode=QueueNode; queueNode; queueNode=queueNode->Next) {
if(!(queueNode->InternalFlags & IQF_PROCESSED)) {
b = TRUE;
break;
}
}
}
//
// If any files still need to be processed and we have a potential
// cabinet file, go try to extract them from a cabinet.
//
if(b && (Cabfile != -1) && FirstIteration) {
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
queueNode,
FullSourcePath
);
temp = _tcsrchr(FullSourcePath,TEXT('\\'));
MYASSERT( temp );
if(temp) {
*(temp+1) = 0;
}
//
// obtain path of (potential) cab file
//
pSetupConcatenatePaths( FullSourcePath, pSetupStringTableStringFromId(Queue->StringTable,Cabfile), MAX_PATH, NULL );
if(DiamondIsCabinet(FullSourcePath)) {
QData.Queue = Queue;
QData.SourceMedia = SourceMediaInfo;
QData.MsgHandler = MsgHandler;
QData.IsMsgHandlerNativeCharWidth = IsMsgHandlerNativeCharWidth;
QData.Context = Context;
QData.LogContext = Queue->LogContext;
rc = DiamondProcessCabinet(
FullSourcePath,
0,
pSetupCabinetQueueCallback,
&QData,
TRUE
);
if(rc != NO_ERROR) {
return(rc);
}
//
// Now reset the cabfile to indicate that there is no cabinet.
// If we don't do this and there are still files that have not
// been processed, we'll end up in an infinite loop -- the prompt
// will come back successfully, and we'll just keep going around
// and around looking through the cabinet, etc.
//
Cabfile = -1;
Tagfile = -1; // for compatability
}
}
//
// If we get here and files *still* need to be processed,
// assume the files are in a different directory somewhere
// and start all over with this media.
//
FirstIteration = FALSE;
DiskPromptGetDriveType(FullSourcePath, &DriveType, &IsRemovable);
AnyProcessed = FALSE;
AnyNotProcessed = FALSE;
for(QueueNode = SourceMediaInfo->CopyQueue;
QueueNode;
QueueNode=QueueNode->Next) {
if(IsRemovable) {
if(!(QueueNode->InternalFlags & IQF_PROCESSED)) {
if(Tagfile != -1) {
SkipMedia = TRUE;
}
goto RepromptMedia;
}
} else { // Fixed media
if(QueueNode->InternalFlags & IQF_PROCESSED) {
AnyProcessed = TRUE;
} else {
AnyNotProcessed = TRUE;
}
}
}
if(!IsRemovable) {
if(AnyNotProcessed) {
//
// If some of the files are present on fixed media, we don't
// want to look elsewhere.
//
if(AnyProcessed) {
SkipMedia = TRUE;
}
//
// Find the first unprocessed file
//
for(QueueNode = SourceMediaInfo->CopyQueue;
QueueNode;
QueueNode = QueueNode->Next) {
if(!(QueueNode->InternalFlags & IQF_PROCESSED)) {
break;
}
}
MYASSERT(QueueNode);
goto RepromptMedia;
}
}
} // end for each source media info
//
// Tell handler we're done with the copy queue and return.
//
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_COPY,
0
);
return(NO_ERROR);
}
VOID
pSetupBuildSourceForCopy(
IN PCTSTR UserRoot,
IN PCTSTR UserPath,
IN LONG MediaRoot,
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
OUT PTSTR FullPath
)
{
PCTSTR p;
//
// If there is a user-specified override root path, use that instead of
// the root path specified in the source media descriptor.
//
MYASSERT(Queue);
MYASSERT(QueueNode);
MYASSERT(FullPath);
p = (UserRoot && UserRoot[0])
? UserRoot
: pSetupStringTableStringFromId(Queue->StringTable,MediaRoot);
lstrcpyn(FullPath,p,MAX_PATH);
//
// If there is a user-specified override path, use that instead of any
// path specified in the copy node.
//
if(UserPath && UserPath[0]) {
p = UserPath;
} else {
if(QueueNode->SourcePath == -1) {
p = NULL;
} else {
p = pSetupStringTableStringFromId(Queue->StringTable,QueueNode->SourcePath);
}
}
if(p) {
pSetupConcatenatePaths(FullPath,p,MAX_PATH,NULL);
}
//
// Fetch the filename and append.
//
p = pSetupStringTableStringFromId(Queue->StringTable,QueueNode->SourceFilename),
pSetupConcatenatePaths(FullPath,p,MAX_PATH,NULL);
}
VOID
pSetupSetPathOverrides(
IN PVOID StringTable,
IN OUT PTSTR RootPath,
IN OUT PTSTR SubPath,
IN LONG RootPathId,
IN LONG SubPathId,
IN PTSTR NewPath
)
{
PCTSTR root,path;
UINT u,l;
//
// See if the existing root override or root path is a prefix
// of the path the user gave us.
//
MYASSERT(RootPath);
MYASSERT(SubPath);
root = RootPath[0] ? RootPath : pSetupStringTableStringFromId(StringTable,RootPathId);
u = lstrlen(root);
path = SubPath[0]
? SubPath
: ((SubPathId == -1) ? NULL : pSetupStringTableStringFromId(StringTable,SubPathId));
if(path && (*path == TEXT('\\'))) {
path++;
}
if(_tcsnicmp(NewPath,root,u)) {
//
// Root path does not match what we're currently using, ie, the user
// supplied a new path. In this case, we will see if the currently in-use
// subpath matches the suffix of the new path, and if so, we'll assume
// that is the override subpath and shorten the override root path.
//
lstrcpy(RootPath,NewPath);
if(path) {
u = lstrlen(NewPath);
l = lstrlen(path);
if((u > l) && (NewPath[(u-l)-1] == TEXT('\\')) && !lstrcmpi(NewPath+u-l,path)) {
//
// Subpath tail matches. Truncate the root override and
// leave the subpath override alone.
//
RootPath[(u-l)-1] = 0;
} else {
//
// In this case, we need to indicate an override subpath of the root,
// or else all subsequent accesses will still try to append the subpath
// specified in the copy node, which is not what we want.
//
SubPath[0] = TEXT('\\');
SubPath[1] = 0;
}
}
} else {
//
// Root path matches what we are currently using.
//
// See if the tail of the user-specified path matches the existing
// subpath. If not, then use the rest of the root path as the subpath
// override. If the tail matches, then extend the user override root.
//
// Examples:
//
// File was queued with root = f:\, subpath = \amd64
//
// User override path is f:\amd64
//
// The new status will be leave override root alone;
// override subpath = \amd64
//
// File was queued with root = \\foo\bar, subpath = \i386
//
// User override path is \\foo\bar\new\i386
//
// The new status will be a root override of \\foo\bar\new;
// no override subpath.
//
NewPath += u;
if(*NewPath == TEXT('\\')) {
NewPath++;
}
if(path) {
u = lstrlen(NewPath);
l = lstrlen(path);
if((u >= l) && !lstrcmpi(NewPath+u-l,path)) {
//
// Change root override and indicate no override subpath.
//
SubPath[0] = TEXT('\0');
NewPath[u-l] = TEXT('\0');
lstrcpy(RootPath,root);
pSetupConcatenatePaths(RootPath,NewPath,MAX_PATH,NULL);
u = lstrlen(RootPath);
if(u && (*CharPrev(RootPath,RootPath+u) == TEXT('\\'))) {
RootPath[u-1] = TEXT('\0'); // valid to do if last char is '\'
}
} else {
//
// Leave override root alone but change subpath.
//
lstrcpy(SubPath,NewPath);
if(!SubPath[0]) {
SubPath[0] = TEXT('\\');
SubPath[1] = TEXT('\0');
}
}
} else {
//
// File was queued without a subpath. If there's a subpath
// in what the user gave us, use it as the override.
//
if(*NewPath) {
lstrcpy(SubPath,NewPath);
}
}
}
}
UINT
pSetupCabinetQueueCallback(
IN PVOID Context,
IN UINT Notification,
IN UINT_PTR Param1,
IN UINT_PTR Param2
)
{
UINT rc;
PCABINET_INFO CabinetInfo;
PFILE_IN_CABINET_INFO FileInfo;
TCHAR TempPath[MAX_PATH];
PTSTR CabinetFile;
PTSTR QueuedFile;
PTSTR FilePart1,FilePart2;
PTSTR FullTargetPath;
PFILEPATHS FilePaths;
PSP_FILE_QUEUE_NODE QueueNode,FirstNode,LastNode;
PQ_CAB_CB_DATA QData;
UINT h;
SOURCE_MEDIA SourceMedia;
DWORD status;
QData = (PQ_CAB_CB_DATA)Context;
switch(Notification) {
case SPFILENOTIFY_CABINETINFO:
//
// We don't do anything with this.
//
rc = NO_ERROR;
break;
case SPFILENOTIFY_FILEINCABINET:
//
// New file within a cabinet.
//
// Determine whether we want to copy this file.
// The context we get has all the stuff we need in it
// to make this determination.
//
// Note that the queue could contain multiple copy operations
// involving this file, but we only want to extract it once!
//
FileInfo = (PFILE_IN_CABINET_INFO)Param1;
CabinetFile = (PTSTR)Param2;
if(FilePart1 = _tcsrchr(FileInfo->NameInCabinet,TEXT('\\'))) {
FilePart1++;
} else {
FilePart1 = (PTSTR)FileInfo->NameInCabinet;
}
rc = FILEOP_SKIP;
FileInfo->Win32Error = NO_ERROR;
FirstNode = NULL;
//
// Find ALL instances of this file in the queue and mark them.
//
for(QueueNode=QData->SourceMedia->CopyQueue; QueueNode; QueueNode=QueueNode->Next) {
if(QueueNode->InternalFlags & IQF_PROCESSED) {
//
// This file was already processed. Ignore it.
//
continue;
}
//
// Check the filename in the cabinet against the file
// in the media's copy queue.
//
QueuedFile = pSetupStringTableStringFromId(
QData->Queue->StringTable,
QueueNode->SourceFilename
);
if(FilePart2 = _tcsrchr(QueuedFile,TEXT('\\'))) {
FilePart2++;
} else {
FilePart2 = QueuedFile;
}
if(!lstrcmpi(FilePart1,FilePart2)) {
//
// We want this file.
//
rc = FILEOP_DOIT;
QueueNode->InternalFlags |= IQF_PROCESSED | IQF_MATCH;
if(!FirstNode) {
FirstNode = QueueNode;
}
LastNode = QueueNode;
}
}
if(rc == FILEOP_DOIT) {
//
// We want this file. Tell the caller the full target pathname
// to be used, which is a temporary file in the directory
// where the first instance of the file will ultimately go.
// We do this so we can call SetupInstallFile later (perhaps
// multiple times), which will handle version checks, etc.
//
// Before attempting to create a temp file make sure the path exists.
//
lstrcpyn(
TempPath,
pSetupStringTableStringFromId(QData->Queue->StringTable,FirstNode->TargetDirectory),
MAX_PATH
);
pSetupConcatenatePaths(TempPath,TEXT("x"),MAX_PATH,NULL); // last component ignored
status = pSetupMakeSurePathExists(TempPath);
if(status == NO_ERROR) {
LastNode->InternalFlags |= IQF_LAST_MATCH;
if(GetTempFileName(
pSetupStringTableStringFromId(QData->Queue->StringTable,FirstNode->TargetDirectory),
TEXT("SETP"),
0,
FileInfo->FullTargetName
)) {
QData->CurrentFirstNode = FirstNode;
} else {
status = GetLastError();
if(status == ERROR_ACCESS_DENIED) {
FileInfo->Win32Error = ERROR_INVALID_TARGET;
} else {
FileInfo->Win32Error = status;
}
rc = FILEOP_ABORT;
SetLastError(FileInfo->Win32Error);
}
} else {
if(status == ERROR_ACCESS_DENIED) {
FileInfo->Win32Error = ERROR_INVALID_TARGET;
} else {
FileInfo->Win32Error = status;
}
rc = FILEOP_ABORT;
SetLastError(FileInfo->Win32Error);
}
}
break;
case SPFILENOTIFY_FILEEXTRACTED:
FilePaths = (PFILEPATHS)Param1;
//
// The current file was extracted. If this was successful,
// then we need to call SetupInstallFile on it to perform version
// checks and move it into its final location or locations.
//
// The .Source member of FilePaths is the cabinet file.
//
// The .Target member is the name of the temporary file, which is
// very useful, as it is the name if the file to use as the source
// in copy operations.
//
// Process each file in the queue that we care about.
//
if((rc = FilePaths->Win32Error) == NO_ERROR) {
for(QueueNode=QData->CurrentFirstNode; QueueNode && (rc==NO_ERROR); QueueNode=QueueNode->Next) {
//
// If we don't care about this file, skip it.
//
if(!(QueueNode->InternalFlags & IQF_MATCH)) {
continue;
}
QueueNode->InternalFlags &= ~IQF_MATCH;
rc = pSetupCopySingleQueuedFileCabCase(
QData->Queue,
QueueNode,
FilePaths->Source,
FilePaths->Target,
QData->MsgHandler,
QData->Context,
QData->IsMsgHandlerNativeCharWidth
);
//
// If this was the last file that matched, break out.
//
if(QueueNode->InternalFlags & IQF_LAST_MATCH) {
QueueNode->InternalFlags &= ~IQF_LAST_MATCH;
break;
}
}
}
//
// Delete the temporary file we extracted -- we don't need it any more.
//
DeleteFile(FilePaths->Target);
break;
case SPFILENOTIFY_NEEDNEWCABINET:
//
// Need a new cabinet.
//
CabinetInfo = (PCABINET_INFO)Param1;
SourceMedia.Tagfile = NULL;
SourceMedia.Description = CabinetInfo->DiskName;
SourceMedia.SourcePath = CabinetInfo->CabinetPath;
SourceMedia.SourceFile = CabinetInfo->CabinetFile;
SourceMedia.Flags = SP_FLAG_CABINETCONTINUATION | SP_COPY_NOSKIP;
h = pSetupCallMsgHandler(
QData->LogContext,
QData->MsgHandler,
QData->IsMsgHandlerNativeCharWidth,
QData->Context,
SPFILENOTIFY_NEEDMEDIA,
(UINT_PTR)&SourceMedia,
Param2
);
switch(h) {
case FILEOP_NEWPATH:
case FILEOP_DOIT:
rc = NO_ERROR;
break;
case FILEOP_ABORT:
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
break;
default:
rc = ERROR_OPERATION_ABORTED;
break;
}
//
// in this case, rc is a status code
// but also set it as last error
//
SetLastError(rc);
break;
default:
MYASSERT(0);
rc = 0; // indeterminate
}
return(rc);
}
DWORD
pSetupCopySingleQueuedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
OUT PTSTR NewSourcePath,
IN BOOL IsMsgHandlerNativeCharWidth,
IN DWORD CopyStyleFlags
)
{
PTSTR FullTargetName;
FILEPATHS FilePaths;
UINT u;
BOOL InUse;
TCHAR source[MAX_PATH],PathBuffer[MAX_PATH];
DWORD rc;
BOOL b;
BOOL BackupInUse = FALSE;
BOOL SignatureVerifyFailed;
NewSourcePath[0] = 0;
PathBuffer[0] = 0;
QueueNode->InternalFlags |= IQF_PROCESSED;
//
// Form the full target path of the file.
//
FullTargetName = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetName) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
lstrcpyn(source,FullSourceName,MAX_PATH);
//
// check if we need to backup before we copy
//
if((rc=pSetupDoLastKnownGoodBackup(Queue,
FullTargetName,
0,
NULL)) != NO_ERROR) {
MyFree(FullTargetName);
goto clean0;
}
rc = pSetupCommitSingleBackup(Queue,
FullTargetName,
QueueNode->TargetDirectory,
-1,
QueueNode->TargetFilename,
MsgHandler,
Context,
IsMsgHandlerNativeCharWidth,
(QueueNode->StyleFlags & SP_COPY_REPLACE_BOOT_FILE),
&BackupInUse
);
if (rc != NO_ERROR) {
MyFree(FullTargetName);
goto clean0;
}
if (BackupInUse) {
//
// if we couldn't do backup, force the IN_USE flag
//
QueueNode->StyleFlags |= SP_COPY_FORCE_IN_USE;
}
do {
//
// Form the full source name.
//
FilePaths.Source = source;
FilePaths.Target = FullTargetName;
FilePaths.Win32Error = NO_ERROR;
//
// Also, pass the callback routine the CopyStyle flags we're about to
// use.
//
// Callback flags are read-only.
//
FilePaths.Flags = QueueNode->StyleFlags;
//
// Notify the callback that the copy is starting.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTCOPY,
(UINT_PTR)&FilePaths,
FILEOP_COPY
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
MSG_LOG_STARTCOPY_ABORT,
NULL);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
rc);
break;
}
if(u == FILEOP_DOIT) {
//
// Attempt the copy.
//
//
b = _SetupInstallFileEx(
Queue,
QueueNode,
NULL, // no inf handle
NULL, // no inf context
source,
NULL, // source path root is part of FullSourcePath
FullTargetName,
QueueNode->StyleFlags | SP_COPY_SOURCE_ABSOLUTE | CopyStyleFlags,
MsgHandler,
Context,
&InUse,
IsMsgHandlerNativeCharWidth,
&SignatureVerifyFailed
);
rc = b ? NO_ERROR : GetLastError();
if(b || (rc == NO_ERROR)) {
if(!InUse && (QueueNode->SecurityDesc != -1)){
//
// Set security on the file
//
rc = pSetupCallSCE(ST_SCE_SET,
FullTargetName,
Queue,
NULL,
QueueNode->SecurityDesc,
NULL
);
}
}
if(rc == NO_ERROR) {
//
// File was copied or not copied, but it if was not copied
// the callback funtcion was already notified about why
// (version check failed, etc).
//
if(QueueNode->StyleFlags & SP_COPY_REPLACE_BOOT_FILE) {
//
// _SetupInstallFileEx is responsible for failing the copy
// when some yahoo comes and copies over a new file (and
// locks it) before we get a chance to.
//
MYASSERT(!InUse);
//
// If the file was copied, we need to set the wants-reboot
// flag. Otherwise, we need to put back the original file.
//
if(b) {
QueueNode->InternalFlags |= INUSE_INF_WANTS_REBOOT;
} else {
RestoreBootReplacedFile(Queue, QueueNode);
}
} else {
if(InUse) {
QueueNode->InternalFlags |= (QueueNode->StyleFlags & SP_COPY_IN_USE_NEEDS_REBOOT)
? INUSE_INF_WANTS_REBOOT
: INUSE_IN_USE;
}
}
} else {
DWORD LogTag = 0;
//
// File was not copied and a real error occurred.
// Notify the callback (unless the failure was due to a
// signature verification problem). Disallow skip if that is
// specified in the node's flags.
//
if(SignatureVerifyFailed) {
break;
} else {
LogTag = AllocLogInfoSlotOrLevel(Queue->LogContext,SETUP_LOG_INFO,FALSE);
FilePaths.Win32Error = rc;
FilePaths.Flags = QueueNode->StyleFlags & (SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP | SP_COPY_NOBROWSE);
WriteLogEntry(
Queue->LogContext,
LogTag,
MSG_LOG_COPYERROR,
NULL,
FilePaths.Source,
FilePaths.Target,
FilePaths.Flags,
FilePaths.Win32Error
);
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_COPYERROR,
(UINT_PTR)&FilePaths,
(UINT_PTR)PathBuffer
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
}
}
if(u == FILEOP_ABORT) {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
MSG_LOG_COPYERROR_ABORT,
NULL
);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
rc
);
ReleaseLogInfoSlot(Queue->LogContext,LogTag);
LogTag = 0;
break;
} else {
if(u == FILEOP_SKIP) {
//
// If this file was a bootfile replacement, then we need
// to restore the original file that was renamed to a
// temporary filename.
//
if(QueueNode->StyleFlags & SP_COPY_REPLACE_BOOT_FILE) {
RestoreBootReplacedFile(Queue, QueueNode);
}
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_WARNING,
MSG_LOG_COPYERROR_SKIP,
NULL
);
ReleaseLogInfoSlot(Queue->LogContext,LogTag);
LogTag = 0;
//
// Force termination of processing for this file.
//
rc = NO_ERROR;
break;
} else {
if((u == FILEOP_NEWPATH) || ((u == FILEOP_RETRY) && PathBuffer[0])) {
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_WARNING,
MSG_LOG_COPYERROR_NEWPATH,
NULL,
u,
PathBuffer
);
ReleaseLogInfoSlot(Queue->LogContext,LogTag);
LogTag = 0;
//
// Note that rc is already set to something other than
// NO_ERROR or we wouldn't be here.
//
lstrcpyn(NewSourcePath,PathBuffer,MAX_PATH);
lstrcpyn(source,NewSourcePath,MAX_PATH);
pSetupConcatenatePaths(
source,
pSetupStringTableStringFromId(Queue->StringTable,QueueNode->SourceFilename),
MAX_PATH,
NULL
);
}
//
// Else we don't have a new path.
// Just keep using the one we had.
//
}
}
if (LogTag != 0) {
//
// haven't done anything regards logging yet, do it now
//
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO,
MSG_LOG_COPYERROR_RETRY,
NULL,
u
);
ReleaseLogInfoSlot(Queue->LogContext,LogTag);
LogTag = 0;
}
}
} else {
//
// skip file
//
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_INFO, // info level as this would be due to override of callback
MSG_LOG_STARTCOPY_SKIP,
NULL,
u
);
rc = NO_ERROR;
}
} while(rc != NO_ERROR);
//
// Notify the callback that the copy is done.
//
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDCOPY,
(UINT_PTR)&FilePaths,
0
);
MyFree(FullTargetName);
clean0:
return(rc);
}
DWORD
pSetupCopySingleQueuedFileCabCase(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR CabinetName,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
{
PTSTR FullTargetName;
FILEPATHS FilePaths;
UINT u;
BOOL InUse;
TCHAR PathBuffer[MAX_PATH];
DWORD rc;
BOOL b;
BOOL BackupInUse = FALSE;
BOOL DontCare;
DWORD LogTag = 0;
LPCTSTR SourceName;
//
// Form the full target path of the file.
//
SourceName = pSetupStringTableStringFromId(Queue->StringTable,QueueNode->SourceFilename);
FullTargetName = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetName) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
LogTag = AllocLogInfoSlotOrLevel(Queue->LogContext,SETUP_LOG_INFO,FALSE);
WriteLogEntry(
Queue->LogContext,
LogTag,
MSG_LOG_COPY_FROM_CAB,
NULL,
CabinetName,
SourceName,
FullSourceName,
FullTargetName
);
//
// check if we need to backup before we copy
//
if((rc=pSetupDoLastKnownGoodBackup(Queue,
FullTargetName,
0,
NULL)) != NO_ERROR) {
MyFree(FullTargetName);
goto clean0;
}
rc = pSetupCommitSingleBackup(Queue,
FullTargetName,
QueueNode->TargetDirectory,
-1,
QueueNode->TargetFilename,
MsgHandler,
Context,
IsMsgHandlerNativeCharWidth,
(QueueNode->StyleFlags & SP_COPY_REPLACE_BOOT_FILE),
&BackupInUse
);
if (rc != NO_ERROR) {
MyFree(FullTargetName);
goto clean0;
}
if (BackupInUse) {
//
// if we couldn't do backup, force the IN_USE flag
//
QueueNode->StyleFlags |= SP_COPY_FORCE_IN_USE;
}
//
// We use the cabinet name as the source name so the display looks right
// to the user. Otherwise he sees the name of some temp file in the
// source field.
//
FilePaths.Source = CabinetName;
FilePaths.Target = FullTargetName;
FilePaths.Win32Error = NO_ERROR;
//
// Also, pass the callback routine the CopyStyle flags we're about to
// use.
//
// Callback flags are read-only.
//
FilePaths.Flags = QueueNode->StyleFlags;
do {
//
// Notify the callback that the copy is starting.
//
u = pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTCOPY,
(UINT_PTR)&FilePaths,
FILEOP_COPY
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
if(!rc) {
rc = ERROR_OPERATION_ABORTED;
}
break;
}
if(u == FILEOP_DOIT) {
//
// Attempt the copy.
//
b = _SetupInstallFileEx(
Queue,
QueueNode,
NULL, // no inf handle
NULL, // no inf context
FullSourceName,
NULL, // source path root is part of FullSourcePath
FullTargetName,
QueueNode->StyleFlags | SP_COPY_SOURCE_ABSOLUTE,
MsgHandler,
Context,
&InUse,
IsMsgHandlerNativeCharWidth,
&DontCare
);
if(b || ((rc = GetLastError()) == NO_ERROR)) {
if(!InUse && (QueueNode->SecurityDesc != -1) ){
// Set security on the file
rc = pSetupCallSCE(
ST_SCE_SET,
FullTargetName,
Queue,
NULL,
QueueNode->SecurityDesc,
NULL
);
SetLastError( rc );
}
}
if(b || ((rc = GetLastError()) == NO_ERROR)) {
//
// File was copied or not copied, but it if was not copied
// the callback funtcion was already notified about why
// (version check failed, etc).
//
if(InUse) {
QueueNode->InternalFlags |= (QueueNode->StyleFlags & SP_COPY_IN_USE_NEEDS_REBOOT)
? INUSE_INF_WANTS_REBOOT
: INUSE_IN_USE;
}
rc = NO_ERROR;
} else {
//
// File was not copied and a real error occurred.
// Break out and return the error.
//
break;
}
} else {
//
// skip file
//
rc = NO_ERROR;
}
} while(rc != NO_ERROR);
//
// Notify the callback that the copy is done.
//
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
Queue->LogContext,
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDCOPY,
(UINT_PTR)&FilePaths,
0
);
MyFree(FullTargetName);
clean0:
if(LogTag) {
ReleaseLogInfoSlot(Queue->LogContext,LogTag);
}
return(rc);
}
PTSTR
pSetupFormFullPath(
IN PVOID StringTable,
IN LONG PathPart1,
IN LONG PathPart2, OPTIONAL
IN LONG PathPart3 OPTIONAL
)
/*++
Routine Description:
Form a full path based on components whose strings are in a string
table.
Arguments:
StringTable - supplies handle to string table.
PathPart1 - Supplies first part of path
PathPart2 - if specified, supplies second part of path
PathPart3 - if specified, supplies third part of path
Return Value:
Pointer to buffer containing full path. Caller can free with MyFree().
NULL if out of memory.
--*/
{
UINT RequiredSize;
PCTSTR p1,p2,p3;
TCHAR Buffer[MAX_PATH];
p1 = pSetupStringTableStringFromId(StringTable,PathPart1);
if (!p1) {
return NULL;
}
p2 = (PathPart2 == -1) ? NULL : pSetupStringTableStringFromId(StringTable,PathPart2);
p3 = (PathPart3 == -1) ? NULL : pSetupStringTableStringFromId(StringTable,PathPart3);
lstrcpy(Buffer,p1);
if(!p2 || pSetupConcatenatePaths(Buffer,p2,MAX_PATH,NULL)) {
if(p3) {
pSetupConcatenatePaths(Buffer,p3,MAX_PATH,NULL);
}
}
return(DuplicateString(Buffer));
}
DWORD
pSetupVerifyQueuedCatalogs(
IN HSPFILEQ FileQueue
)
/*++
Routine Description:
Silently verify all catalog nodes in the specified queue.
Arguments:
FileQueue - supplies a handle to the file queue containing catalog nodes
to be verified.
Return Value:
If all catalog nodes are valid, the return value is NO_ERROR. Otherwise,
it is a Win32 error code indicating the problem.
--*/
{
return _SetupVerifyQueuedCatalogs(NULL, // No UI, thus no HWND needed
(PSP_FILE_QUEUE)FileQueue,
VERCAT_NO_PROMPT_ON_ERROR,
NULL,
NULL
);
}
DWORD
_SetupVerifyQueuedCatalogs(
IN HWND Owner,
IN PSP_FILE_QUEUE Queue,
IN DWORD Flags,
OUT PTSTR DeviceInfFinalName, OPTIONAL
OUT PBOOL DeviceInfNewlyCopied OPTIONAL
)
/*++
Routine Description:
This routine verifies catalogs and infs in a given queue by traversing
the catalog node list associated with the queue and operating on the
catalog/inf pair described by each one.
If any catalog/inf fails verification, the user is notified via a dialog,
depending on current policy.
** Behavior for native platform verification (w/o catalog override)
If an INF is from a system location, we assume that the catalog is
already installed on the system. Really there is no other option here,
since we would have no idea where to get the catalog in order to install it
even if we wanted to try. But the inf might have originally been an
oem inf which was copied and renamed by the Di stuff at device install
time. The catalog file knows nothing about the renamed file, so we must
track mappings from current inf filename to original inf filename.
In this case, we calculate the inf's hash value and then using that,
we ask the system for a catalog file that contains signing data
for that hash value. We then ask the system for info
about that catalog file. We keep repeating this process until we get
at the catalog we want (based on name). Finally we can call WinVerifyTrust
verify the catalog itself and the inf.
If an INF file is instead from an oem location, we copy the oem inf to a
unique name in the system inf directory (or create a zero-length placeholder
there, depending on whether or not the VERCAT_INSTALL_INF_AND_CAT flag is
set), and add the catalog using a filename based on that unique filename.
** Behavior for non-native platform verification (w/o catalog override) **
We will validate the catalogs and INFs using the alternate platform info
provided in the file queue. Otherwise, the logic is the same as in the
native case.
** Behavior for verification (w/catalog override) **
The actual verification will be done using native or non-native parameters
as discussed above, but INFs without a CatalogFile= entry will be validated
against the specified overriding catalog. This means that system INFs won't
get validated globally, and INF in OEM locations can be validated even if
they don't have a CatalogFile= entry. The overriding catalog file will be
installed under its current name, thus blowing away any existing catalog
having that name.
** Behavior for verification via Authenticode catalog **
If the specified queue has the DRIVERSIGN_ALLOW_AUTHENTICODE bit set in its
driver signing policy, then we'll allow catalogs to be signed via
Authenticode, instead of our default (requiring MS cert chain and OS code-
signing usage OID).
Here's the algorithm for how we'll do digital signature verification for a
self-contained driver package:
* Check for WHQL signature on driver package INF using corresponding CAT.
* If valid WHQL signature - Install Driver (no UI)
* If invalid or no WHQL signature - log an entry to setupapi.log, check
if there's a WHQL program for this device setup class (list of
classes in %windir%\Inf\certclas.inf)
* If yes - check driver signing policy
* If Block - terminate installation
* If Warn - issue warning, install driver (customer option)
* If Ignore - install driver (no UI)
* If no - check for Authenticode (TM) signature on driver package
INF using corresponding CAT. (signature must chain up through an
existing root certificate)
* If valid Authenticode signature - check for matching signing
certificate in Authenticode certificate store
* If Authenticode signing certificate installed, install
driver (no UI)
* If Authenticode signing certificate not installed, check
driver signing policy
* If Block - terminate installation (Authenticode
signature does not allow vendors to by-pass Block
policy)
* If Warn - issue warning (but indicate that package is
signed by vendor), install driver (customer option)
* If Ignore - install driver (no UI)
* If invalid or no Authenticode signature, check driver signing
policy
* If Block, terminate installation
* If Warn, issue warning (indicating package's author/
integrity cannot be established), install driver
(customer option)
* If Ignore, install driver (no UI)
See the documentation on SetupSetFileQueueAlternatePlatform for more
details.
Arguments:
Owner - supplies window handle of window to own any ui. This HWND is stored
away in the queue for use later if any individual files fail verification.
Queue - supplies pointer to queue structure.
Flags - supplies flags that control behavior of this routine.
VERCAT_INSTALL_INF_AND_CAT - if this flag is set, any infs from
oem locations will be installed on the system, along with
their catalog files.
VERCAT_NO_PROMPT_ON_ERROR - if this flag is set, the user will _not_ be
notified about verification failures we encounter. If this flag is
set, then this was only a 'test', and no user prompting should take
place (nor should any PSS logging take place). If this flag is set,
then the VERCAT_INSTALL_INF_AND_CAT _should not_ be specified.
VERCAT_PRIMARY_DEVICE_INF_FROM_INET - specifies that the primary device
INF in the queue is from the internet, and should be marked as such
in the corresponding PNF when installed into the %windir%\Inf
directory via _SetupCopyOEMInf.
DeviceInfFinalName - optionally, supplies the address of a character buffer,
_at least_ MAX_PATH characters long, that upon success receives the
final name given to the INF under the %windir%\Inf directory (this will
be different than the INF's original name if it was an OEM INF).
DeviceInfNewlyCopied - optionally, supplies the address of a boolean
variable that, upon success, is set to indicate whether the INF name
returned in DeviceInfFinalName was newly-created. If this parameter is
supplied, then DeviceInfFinalName must also be specified.
Return Value:
If all catalogs/infs were verified and installed, or the user accepted
the risk if a verification failed, then the return value is NO_ERROR.
If one or more catalogs/infs were not verified, the return value is a Win32
error code indicating the cause of the failure. NOTE: This error will
only be returned if the policy is "block", or it it's "warn" and the
user decided to abort. In this case, the error returned is for the
catalog/INF where the error was encountered, and any subsequent catalog
nodes will not have been verified. An exception to this is when the
VERCAT_NO_PROMPT_ON_ERROR flag is set. In that case, we'll verify all
catalogs, even if we encounter improperly-signed ones.
Remarks:
There are some system INFs (for which global verification is required) that
don't live in %windir%\Inf. The OCM INFs are an example of this. Those
INFs use layout.inf (which _is_ located in %windir%\Inf) for the source
media information for any files they copy. There are other INFs that don't
live in %windir%\Inf which are extracted out of a binary as-needed (into a
temporary filename), processed in order to do registry munging, and then
deleted. Such INFs do not do file copying (thus their 'package' consists
of just the INF). To accommodate such INFs, we allow "OEM" INFs (i.e.,
those INFs not in %windir%\Inf) to be verified globally, but we remember the
fact that these INFs didn't contain a CatalogFile= entry, and if any files
are ever queued for copy using such INFs for source media information, then
we'll fail digital signature verification for such files, since there's no
way for us to know what catalog should be used for verification.
--*/
{
PSPQ_CATALOG_INFO CatalogNode;
LPCTSTR InfFullPath;
LPCTSTR CatName;
TCHAR PathBuffer[MAX_PATH];
TCHAR InfNameBuffer[MAX_PATH];
TCHAR CatalogName[MAX_PATH];
TCHAR *p;
DWORD Err, CatalogNodeStatus, ReturnStatus;
SetupapiVerifyProblem Problem;
LPCTSTR ProblemFile;
BOOL DeleteOemInfOnError;
BOOL OriginalNameDifferent;
LPCTSTR AltCatalogFile;
LONG CatStringId;
ULONG RequiredSize;
DWORD InfVerifyType;
DWORD SCOIFlags;
HANDLE hWVTStateData = NULL;
DWORD AuthSigPromptCount = 0;
DWORD CopyStyleFlags;
BOOL OemInfIsDeviceInf;
//
// Define values used to indicate how validation should be done on the INFs.
//
#define VERIFY_INF_AS_OEM 0 // verify solely against the specific
// catalog referenced by the INF
#define VERIFY_INF_AS_SYSTEM 1 // verify globally (using all catalogs)
#define VERIFY_OEM_INF_GLOBALLY 2 // verify OEM INF globally, but remember the
// original error, in case copy operations
// are queued using media descriptor info
// within this INF.
MYASSERT((Flags & (VERCAT_INSTALL_INF_AND_CAT | VERCAT_NO_PROMPT_ON_ERROR))
!= (VERCAT_INSTALL_INF_AND_CAT | VERCAT_NO_PROMPT_ON_ERROR)
);
MYASSERT(!DeviceInfNewlyCopied || DeviceInfFinalName);
if(Queue->Flags & FQF_DID_CATALOGS_OK) {
//
// If the caller wants information about the primary device INF, then
// find the applicable catalog node.
//
if(DeviceInfFinalName) {
for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
if(CatalogNode->Flags & CATINFO_FLAG_PRIMARY_DEVICE_INF) {
MYASSERT(CatalogNode->InfFinalPath != -1);
InfFullPath = pSetupStringTableStringFromId(Queue->StringTable, CatalogNode->InfFinalPath);
lstrcpy(DeviceInfFinalName, InfFullPath);
if(DeviceInfNewlyCopied) {
*DeviceInfNewlyCopied = (CatalogNode->Flags & CATINFO_FLAG_NEWLY_COPIED);
}
}
}
}
return NO_ERROR;
}
if(Queue->Flags & FQF_DID_CATALOGS_FAILED) {
//
// Scan the catalog nodes until we find the first one that failed
// verification, and return that failure code.
//
for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
if(CatalogNode->VerificationFailureError != NO_ERROR) {
return CatalogNode->VerificationFailureError;
}
}
//
// We didn't find a failed catalog node in our catalog list--something's
// seriously wrong!
//
MYASSERT(0);
return ERROR_INVALID_DATA;
}
if(Queue->Flags & FQF_DID_CATALOGS_PROMPT_FOR_TRUST) {
//
// We've previously validated these catalogs, but we came across one
// Authenticode-signed catalog that we needed to prompt the user for in
// order to establish trust of the publisher. Unfortunately, we were
// invoked in "silent mode" (i.e., VERCAT_NO_PROMPT_ON_ERROR), so we
// simply flagged the queue as having an outstanding issue to resolve
// this.
//
for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
if(CatalogNode->Flags & CATINFO_FLAG_PROMPT_FOR_TRUST) {
//
// Count how many of these we find (there'd better be exactly
// one).
//
AuthSigPromptCount++;
MYASSERT(CatalogNode->VerificationFailureError == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
//
// ...and we'd better have some WinVerifyTrust state data!
//
MYASSERT(CatalogNode->hWVTStateData);
if(Flags & VERCAT_NO_PROMPT_ON_ERROR) {
//
// We _still_ can't make any progress here. :-(
//
return CatalogNode->VerificationFailureError;
} else {
//
// OK, now we can finally ask the user if they trust this
// catalog's publisher!
//
MYASSERT(!(Queue->Flags & FQF_DIGSIG_ERRORS_NOUI));
if(_HandleFailedVerification(
Owner,
SetupapiVerifyCatalogProblem,
CatalogNode->CatalogFilenameOnSystem,
((Queue->DeviceDescStringId == -1)
? NULL
: pStringTableStringFromId(Queue->StringTable, Queue->DeviceDescStringId)),
Queue->DriverSigningPolicy,
FALSE,
CatalogNode->VerificationFailureError,
Queue->LogContext,
NULL,
NULL,
CatalogNode->hWVTStateData)) {
//
// The user said they trust the publisher--this catalog
// node can now be marked as successfully validated.
//
CatalogNode->Flags &= ~CATINFO_FLAG_PROMPT_FOR_TRUST;
CatalogNode->Flags |= CATINFO_FLAG_AUTHENTICODE_SIGNED;
CatalogNode->InfFinalPath = CatalogNode->InfFullPath;
//
// Unless policy is "Ignore", we want to update the
// error value to indicate the user confirmed their
// trust of this Authenticode publisher.
//
MYASSERT(Queue->DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE);
MYASSERT((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_BLOCKING);
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
CatalogNode->VerificationFailureError = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
//
// We can free the WinVerifyTrust state data now that
// the user has indicated they trust the publisher.
//
pSetupCloseWVTStateData(CatalogNode->hWVTStateData);
CatalogNode->hWVTStateData = NULL;
} else {
//
// The user doesn't trust the publisher--clear the
// string buffer containing the Authenticode catalog's
// filename.
//
CatalogNode->CatalogFilenameOnSystem[0] = TEXT('\0');
//
// Also, clear the "prompt for trust" flag on this
// catalog node, since we've already done this.
//
CatalogNode->Flags &= ~CATINFO_FLAG_PROMPT_FOR_TRUST;
//
// Likewise, we can clear the "prompt for trust" flag
// for the queue, and in its place set the "catalog
// verification failed" flag.
//
Queue->Flags &= ~FQF_DID_CATALOGS_PROMPT_FOR_TRUST;
Queue->Flags |= FQF_DID_CATALOGS_FAILED;
//
// Go ahead and free the WinVerifyTrust state data...
//
pSetupCloseWVTStateData(CatalogNode->hWVTStateData);
CatalogNode->hWVTStateData = NULL;
//
// Change our error to now indicate that the user
// explicitly indicated they didn't want to trust the
// publisher (unless policy was block, in which case
// they didn't have a choice).
//
CatalogNode->VerificationFailureError =
ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED;
return CatalogNode->VerificationFailureError;
}
}
} else {
//
// We'd better have a successful validation reported for this
// node!
//
MYASSERT((CatalogNode->VerificationFailureError == NO_ERROR) ||
(CatalogNode->VerificationFailureError == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER));
//
// ...and we'd better not have any WinVerifyTrust state data!
//
MYASSERT(!(CatalogNode->hWVTStateData));
}
}
MYASSERT(AuthSigPromptCount == 1);
//
// OK, we've gotten confirmation from the user that they trust the
// Authenticode publisher (thus, the validation of the catalogs in this
// queue can be considered successful).
//
Queue->Flags &= ~FQF_DID_CATALOGS_PROMPT_FOR_TRUST;
Queue->Flags |= FQF_DID_CATALOGS_OK;
//
// If the caller wants information about the primary device INF, then
// find the applicable catalog node.
//
if(DeviceInfFinalName) {
for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
if(CatalogNode->Flags & CATINFO_FLAG_PRIMARY_DEVICE_INF) {
MYASSERT(CatalogNode->InfFinalPath != -1);
InfFullPath = pSetupStringTableStringFromId(Queue->StringTable, CatalogNode->InfFinalPath);
lstrcpy(DeviceInfFinalName, InfFullPath);
if(DeviceInfNewlyCopied) {
*DeviceInfNewlyCopied = (CatalogNode->Flags & CATINFO_FLAG_NEWLY_COPIED);
}
}
}
}
return NO_ERROR;
}
//
// If the queue has an alternate default catalog file associated with it,
// then retrieve that catalog's name for use later.
//
AltCatalogFile = (Queue->AltCatalogFile != -1)
? pSetupStringTableStringFromId(Queue->StringTable, Queue->AltCatalogFile)
: NULL;
Queue->hWndDriverSigningUi = Owner;
ReturnStatus = NO_ERROR;
for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
//
// Assume success for verification of this catalog node.
//
CatalogNodeStatus = NO_ERROR;
MYASSERT(CatalogNode->InfFullPath != -1);
InfFullPath = pStringTableStringFromId(Queue->StringTable, CatalogNode->InfFullPath);
if(Queue->Flags & FQF_USE_ALT_PLATFORM) {
//
// We have an alternate platform override, so use the alternate
// platform's CatalogFile= entry.
//
CatStringId = CatalogNode->AltCatalogFileFromInf;
} else {
//
// We're running native--use the native CatalogFile= entry.
//
CatStringId = CatalogNode->CatalogFileFromInf;
}
CatName = (CatStringId != -1)
? pStringTableStringFromId(Queue->StringTable, CatStringId)
: NULL;
InfVerifyType = pSetupInfIsFromOemLocation(InfFullPath, TRUE)
? VERIFY_INF_AS_OEM
: VERIFY_INF_AS_SYSTEM;
if(InfVerifyType == VERIFY_INF_AS_OEM) {
//
// If the caller wants us to, we'll now install the catalog. In
// addition, if it's a (native platform) device installation, we'll
// install the INF as well.
//
// (Note: we specify the 'no overwrite' switch so that we won't
// blow away any existing PNF source path information for this INF.
// We'll only consider an OEM INF to match up with an existing
// %windir%\Inf\Oem*.INF entry if the catalogs also match up, so
// we're not going to get into any trouble doing this.
//
if(Flags & VERCAT_INSTALL_INF_AND_CAT) {
//
// Check to see whether the INF is a device INF. The queue may
// not be marked as a device install queue, even though we have
// a device INF. This could happen if a device INF is being
// used for some purpose other than a device install. It could
// also happen if we're explicitly forcing non-driver signing
// policy (i.e., the INF's class isn't listed in certclas.inf).
//
OemInfIsDeviceInf = TRUE; // assume INF is a device INF.
if(!(Queue->Flags & FQF_DEVICE_INSTALL)) {
HINF hInf;
//
// The queue isn't marked as a device install queue, but we
// still may be dealing with a device INF. Check to be
// sure...
//
hInf = SetupOpenInfFile(InfFullPath,
NULL,
INF_STYLE_WIN4,
NULL
);
if(hInf != INVALID_HANDLE_VALUE) {
try {
//
// We don't need to lock the INF because it'll
// never be accessible outside of this routine.
//
if(!IsInfForDeviceInstall(Queue->LogContext,
NULL,
(PLOADED_INF)hInf,
NULL,
NULL,
NULL,
NULL,
FALSE)) {
//
// The INF really isn't a device INF.
//
OemInfIsDeviceInf = FALSE;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(),
ERROR_INVALID_PARAMETER,
NULL
);
}
SetupCloseInfFile(hInf);
} else {
//
// This shouldn't happen. For now, just assume the INF
// isn't a device INF. We're probably going to fail
// down below when we actually try to copy the INF.
//
OemInfIsDeviceInf = FALSE;
}
}
CopyStyleFlags = SP_COPY_NOOVERWRITE;
if(OemInfIsDeviceInf) {
SCOIFlags = 0;
//
// If we're doing a non-native install, then we only want
// to install the INF's associated catalog (if any).
//
if(Queue->Flags & FQF_USE_ALT_PLATFORM) {
CopyStyleFlags |= SP_COPY_OEMINF_CATALOG_ONLY;
}
} else {
//
// Since we're not working with a device INF, we want to
// suppress popups and error log entries if the INF doesn't
// reference a catalog. This is because we want to allow
// such INFs to be validated globally, unless they
// subsequently try to copy files.
//
SCOIFlags = SCOI_NO_ERRLOG_ON_MISSING_CATALOG;
//
// We always want to do catalog-only installs for
// non-device INFs.
//
CopyStyleFlags |= SP_COPY_OEMINF_CATALOG_ONLY;
}
//
// If we're not supposed to generate popups/log entries at all
// for signature verification failures (e.g., because we've
// already done so previously), then set that flag as well.
//
if(Queue->Flags & FQF_DIGSIG_ERRORS_NOUI) {
SCOIFlags |= SCOI_NO_UI_ON_SIGFAIL;
}
if(Queue->Flags & FQF_KEEP_INF_AND_CAT_ORIGINAL_NAMES) {
SCOIFlags |= SCOI_KEEP_INF_AND_CAT_ORIGINAL_NAMES;
//
// This is an exception package, so we'd better not even
// think about allowing Authenticode signed files!
//
MYASSERT(!(Queue->DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE));
}
if(Queue->Flags & FQF_ABORT_IF_UNSIGNED) {
SCOIFlags |= SCOI_ABORT_IF_UNSIGNED;
}
CatalogNodeStatus =
GLE_FN_CALL(FALSE,
_SetupCopyOEMInf(
InfFullPath,
NULL, // default source location to where INF presently is
((Flags & VERCAT_PRIMARY_DEVICE_INF_FROM_INET)
? SPOST_URL
: SPOST_PATH),
CopyStyleFlags,
PathBuffer,
SIZECHARS(PathBuffer),
NULL,
&p,
((CatalogNode->InfOriginalName != -1)
? pStringTableStringFromId(Queue->StringTable,
CatalogNode->InfOriginalName)
: pSetupGetFileTitle(InfFullPath)),
CatName,
Owner,
((Queue->DeviceDescStringId == -1)
? NULL
: pStringTableStringFromId(Queue->StringTable,
Queue->DeviceDescStringId)),
Queue->DriverSigningPolicy,
SCOIFlags,
AltCatalogFile,
((Queue->Flags & FQF_USE_ALT_PLATFORM)
? &(Queue->AltPlatformInfo)
: Queue->ValidationPlatform),
&Err,
CatalogNode->CatalogFilenameOnSystem,
Queue->LogContext,
&(Queue->VerifyContext),
&hWVTStateData)
);
if(CatalogNodeStatus == NO_ERROR) {
//
// If we got back a WinVerifyTrust state data handle, we'd
// better have gotten one of our two Authenticode status
// codes back...
//
MYASSERT(!hWVTStateData ||
((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) || (Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)));
//
// If Err indicates that there was a digital signature
// problem that the user chose to ignore (or was silently
// ignored), then set a flag in the queue indicating the
// user should not be warned about subsequent failures.
// Don't set this flag if the queue's policy is "Ignore",
// however, on the chance that the policy might be altered
// later, and we'd want the user to get informed on any
// subsequent errors.
//
// (Note: if the error was due to the INF not having a
// CatalogFile= entry, and if we're supposed to ignore such
// problems, then just set the flag to do global validation
// later.)
//
if((Err == ERROR_NO_CATALOG_FOR_OEM_INF) &&
(SCOIFlags & SCOI_NO_ERRLOG_ON_MISSING_CATALOG)) {
MYASSERT(!hWVTStateData);
InfVerifyType = VERIFY_OEM_INF_GLOBALLY;
} else if(Err != NO_ERROR) {
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
MYASSERT(Err != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
}
if(*PathBuffer) {
//
// Store the INF's final path into our catalog node.
// This will be under %windir%\Inf unless the INF didn't
// specify a CatalogFile= entry and we did an alternate
// catalog installation (i.e., because the file queue had
// an associated alternate catalog).
//
CatalogNode->InfFinalPath = pSetupStringTableAddString(
Queue->StringTable,
PathBuffer,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
} else {
//
// _SetupCopyOEMInf returned an empty string for the
// destination INF name, which means that we were doing
// a catalog-only install, and it didn't find the INF
// already existing in %windir%\Inf. In this case, just
// use the INF's original pathname as its final pathname.
//
CatalogNode->InfFinalPath = CatalogNode->InfFullPath;
}
if(CatalogNode->InfFinalPath == -1) {
CatalogNodeStatus = ERROR_NOT_ENOUGH_MEMORY;
if(Err == NO_ERROR) {
Err = CatalogNodeStatus;
}
//
// Since we couldn't add this filename to the string
// table, we won't be able to undo this copy later--it
// must be done here. Delete the INF, PNF, and CAT.
//
// NOTE: we should never get here if we did an alternate
// catalog file-only install, because in that case our
// new INF name is the same as the INF's original name,
// thus the string is already in the buffer and there's
// no way we could run out of memory.
//
MYASSERT(lstrcmpi(PathBuffer, InfFullPath));
pSetupUninstallOEMInf(PathBuffer,
Queue->LogContext,
SUOI_FORCEDELETE,
NULL
);
} else {
//
// Set a flag in the catalog node indicating that this
// INF was newly-copied into %windir%\Inf. If the
// string ID for our INF's original name and that of its
// new name are equal, then we know we did an alternate
// catalog installation only, and we don't want to set
// this flag.
//
if(CatalogNode->InfFinalPath != CatalogNode->InfFullPath) {
CatalogNode->Flags |= CATINFO_FLAG_NEWLY_COPIED;
}
//
// If this is the primary device INF, and the caller
// requested information about that INF's final
// pathname, then store that information in the caller-
// supplied buffer(s) now.
//
if(DeviceInfFinalName &&
(CatalogNode->Flags & CATINFO_FLAG_PRIMARY_DEVICE_INF)) {
//
// We'd better not just've done an alternate catalog
// installation.
//
MYASSERT(CatalogNode->InfFinalPath != CatalogNode->InfFullPath);
lstrcpy(DeviceInfFinalName, PathBuffer);
if(DeviceInfNewlyCopied) {
*DeviceInfNewlyCopied = TRUE;
}
}
//
// If this INF was signed by an Authenticode catalog,
// then set a flag indicating that.
//
if((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// In either case, we should trust the catalog's
// publisher, because either the cert was in the
// TrustedPublisher store, or the user agreed to
// trust the publisher.
//
CatalogNode->Flags |= CATINFO_FLAG_AUTHENTICODE_SIGNED;
MYASSERT(hWVTStateData);
pSetupCloseWVTStateData(hWVTStateData);
hWVTStateData = NULL;
}
}
} else {
if(CatalogNodeStatus == ERROR_FILE_EXISTS) {
//
// INF and CAT already there--this isn't a failure.
//
// Store the name under which we found this OEM INF
// into the catalog node's InfFinalPath field.
//
CatalogNode->InfFinalPath = pSetupStringTableAddString(
Queue->StringTable,
PathBuffer,
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
);
if(CatalogNode->InfFinalPath == -1) {
CatalogNodeStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
CatalogNodeStatus = NO_ERROR;
//
// If Err indicates that there was a digital
// signature problem that the user chose to ignore
// (or was silently ignored), then set a flag in
// the queue indicating the user should not be
// warned about subsequent failures. Don't set
// this flag if the queue's policy is "Ignore",
// however, on the chance that the policy might be
// altered later, and we'd want the user to get
// informed on any subsequent errors.
//
if(Err != NO_ERROR) {
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
MYASSERT(Err != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
}
//
// If this is the primary device INF, and the
// caller requested information about that INF's
// final pathname, then store that information in
// the caller-supplied buffer(s) now.
//
if(DeviceInfFinalName &&
(CatalogNode->Flags & CATINFO_FLAG_PRIMARY_DEVICE_INF)) {
lstrcpy(DeviceInfFinalName, PathBuffer);
if(DeviceInfNewlyCopied) {
*DeviceInfNewlyCopied = FALSE;
}
}
//
// If this INF was signed by an Authenticode
// catalog, then set a flag indicating that.
//
if((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// In either case, we should trust the
// catalog's publisher, because either the cert
// was in the TrustedPublisher store, or the
// user agreed to trust the publisher.
//
CatalogNode->Flags |= CATINFO_FLAG_AUTHENTICODE_SIGNED;
pSetupCloseWVTStateData(hWVTStateData);
hWVTStateData = NULL;
}
}
} else {
//
// For any error other than ERROR_FILE_EXISTS, we
// shouldn't be getting any WinVerifyTrust state data.
//
MYASSERT(!hWVTStateData);
if(CatalogNodeStatus == ERROR_SET_SYSTEM_RESTORE_POINT) {
//
// We should only get this error if the queue flag is
// set that causes us to abort unsigned installations.
//
MYASSERT(Queue->Flags & FQF_ABORT_IF_UNSIGNED);
//
// We don't want the user to see the driver signing
// UI again when the queue is re-committed...
//
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
//
// Make sure that Err is also set to this same
// "special" error code...
//
Err = CatalogNodeStatus;
}
}
//
// If we had a real failure from _SetupCopyOEMInf (or we're
// out of memory and couldn't add a string to the string
// table above), then we need to propagate the value of
// CatalogNodeStatus to Err, if Err doesn't already have a
// failure code.
//
if((CatalogNodeStatus != NO_ERROR) && (Err == NO_ERROR)) {
Err = CatalogNodeStatus;
}
}
} else {
//
// We were told not to copy any files, but we've encountered an
// OEM INF that needs to be installed. Hence, we have a failure.
// Note that we _don't_ look to see if this OEM INF (and its
// corresponding catalog) might happen to already be properly
// installed. That isn't necessary, because
// _SetupDiInstallDevice calls _SetupVerifyQueuedCatalogs with
// the VERCAT_INSTALL_INF_AND_CAT flag _before_ calling
// SetupScanFileQueue, thus all INFs/CATs should be present when
// we're called to do simple verification of the catalog nodes.
//
Err = CatalogNodeStatus = ERROR_CANNOT_COPY;
}
}
if(InfVerifyType != VERIFY_INF_AS_OEM) {
//
// Inf is in system location (%windir%\Inf), or we're going to try
// validating an "OEM" INF globally. Figure out the expected name
// of the catalog file. If the file was originally copied in by the
// Di stuff, then we need to use a name based on the name Di gave
// the inf. Otherwise we use the name from the inf's CatalogFile=
// entry, if present. Finally, if the INF doesn't specify a
// CatalogFile= entry, we assume it's a system component and
// attempt to validate against any catalog that we find a hash
// match in.
//
Err = NO_ERROR; // assume success
ProblemFile = PathBuffer; // default buffer to store problem file
if(CatalogNode->InfOriginalName != -1) {
RequiredSize = SIZECHARS(InfNameBuffer);
if(pSetupStringTableStringFromIdEx(Queue->StringTable,
CatalogNode->InfOriginalName,
InfNameBuffer,
&RequiredSize)) {
OriginalNameDifferent = TRUE;
} else {
//
// This should never fail!
//
MYASSERT(0);
Err = ERROR_INVALID_DATA;
//
// Blame the INF!
//
Problem = SetupapiVerifyInfProblem;
MYVERIFY(SUCCEEDED(StringCchCopy(PathBuffer,
SIZECHARS(PathBuffer),
InfFullPath)));
}
} else {
OriginalNameDifferent = FALSE;
}
if(Err == NO_ERROR) {
if(CatName) {
//
// If there is a catalog name, then we'd better not be
// doing our "verify OEM INF globally" trick!
//
MYASSERT(InfVerifyType == VERIFY_INF_AS_SYSTEM);
if(OriginalNameDifferent) {
//
// If the INF specified a catalog file, then we know we
// would've installed that catalog file using a name
// based on the unique name we assigned the INF when
// copying it into the INF directory.
//
lstrcpy(CatalogName, pSetupGetFileTitle(InfFullPath));
p = _tcsrchr(CatalogName, TEXT('.'));
if(!p) {
p = CatalogName + lstrlen(CatalogName);
}
lstrcpy(p, pszCatSuffix);
} else {
lstrcpy(CatalogName, CatName);
}
} else {
//
// This system INF didn't specify a CatalogFile= entry. If
// an alternate catalog is associated with this file queue,
// then use that catalog for verification.
//
if(AltCatalogFile) {
lstrcpy(CatalogName, AltCatalogFile);
CatName = pSetupGetFileTitle(CatalogName);
}
}
//
// (Note: in the call below, we don't want to store the
// validating catalog filename in our CatalogFilenameOnSystem
// field if the INF didn't specify a CatalogFile= entry (and
// there was no alternate catalog specified), because we want
// any queue nodes that reference this catalog entry to use
// global validation as well.)
//
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// Don't attempt to call _VerifyFile, because we're
// asking for the validating catalog's name, and that makes
// no sense in the "minimal embedded" case.
//
*(CatalogNode->CatalogFilenameOnSystem) = TEXT('\0');
//
// (Err is already set to NO_ERROR.)
//
} else {
if(!CatName) {
*(CatalogNode->CatalogFilenameOnSystem) = TEXT('\0');
}
Err = _VerifyFile(
Queue->LogContext,
&(Queue->VerifyContext),
(CatName ? CatalogName : NULL),
NULL,
0,
(OriginalNameDifferent ? InfNameBuffer : pSetupGetFileTitle(InfFullPath)),
InfFullPath,
&Problem,
PathBuffer,
FALSE,
((Queue->Flags & FQF_USE_ALT_PLATFORM)
? &(Queue->AltPlatformInfo)
: Queue->ValidationPlatform),
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
(CatName ? CatalogNode->CatalogFilenameOnSystem : NULL),
NULL,
NULL,
NULL,
NULL
);
if((Err != NO_ERROR) && (Err != ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH) &&
CatName &&
!(Queue->Flags & FQF_QUEUE_FORCE_BLOCK_POLICY) &&
(Queue->DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// We failed to validate via OS codesigning policy--now
// we should see if the INF validates using
// Authenticode policy.
//
Err = _VerifyFile(Queue->LogContext,
&(Queue->VerifyContext),
CatalogName,
NULL,
0,
(OriginalNameDifferent
? InfNameBuffer
: pSetupGetFileTitle(InfFullPath)),
InfFullPath,
&Problem,
PathBuffer,
FALSE,
((Queue->Flags & FQF_USE_ALT_PLATFORM)
? &(Queue->AltPlatformInfo)
: Queue->ValidationPlatform),
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
CatalogNode->CatalogFilenameOnSystem,
NULL,
NULL,
NULL,
&hWVTStateData
);
if(Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) {
CatalogNode->VerificationFailureError = Err;
//
// Treat this as success...
//
Err = NO_ERROR;
Problem = SetupapiVerifyNoProblem;
CatalogNode->Flags |= CATINFO_FLAG_AUTHENTICODE_SIGNED;
pSetupCloseWVTStateData(hWVTStateData);
hWVTStateData = NULL;
} else if(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) {
//
// This isn't really a verification error, but
// since we're going to have to prompt the user
// to establish trust of this publisher, we
// need to have a problem and problem file
// identified. We'll blame the catalog...
//
Problem = SetupapiVerifyCatalogProblem;
ProblemFile = CatalogNode->CatalogFilenameOnSystem;
//
// We only support one Authenticode-signed
// catalog (that isn't pre-trusted) per file
// queue, so we keep a count to make sure we
// don't find more than one.
//
AuthSigPromptCount++;
}
}
}
}
if(Err == NO_ERROR) {
//
// INF/CAT was successfully verified--store the INF's final
// path (which is the same as its current path) into the
// catalog node.
//
CatalogNode->InfFinalPath = CatalogNode->InfFullPath;
} else {
MYASSERT(Problem != SetupapiVerifyNoProblem);
if(Problem != SetupapiVerifyCatalogProblem) {
//
// If the problem was not a catalog problem, then it's an
// INF problem (the _VerifyFile routine doesn't know the
// file we passed it is an INF).
//
Problem = SetupapiVerifyInfProblem;
}
if(AuthSigPromptCount > 1) {
//
// We don't want to popup more than one Authenticode trust
// dialog!
//
CatalogNodeStatus = Err = ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED;
MYASSERT(hWVTStateData);
pSetupCloseWVTStateData(hWVTStateData);
hWVTStateData = NULL;
} else if(Flags & VERCAT_NO_PROMPT_ON_ERROR) {
if(hWVTStateData) {
//
// The INF is signed via an Authenticode catalog, but
// the signing certificate isn't in the trusted
// publisher store. Since we're prevented from asking
// the user whether they trust the publisher, we'll
// simply set a flag on this catalog node for the time
// being indicating that the user needs to be so
// prompted. We'll also store the WinVerifyTrust data
// in the catalog node so that when we do get around to
// prompting the user, we'll be able to give them info
// on who the publisher was, when the catalog was
// signed, etc.
//
MYASSERT(AuthSigPromptCount == 1);
MYASSERT(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
CatalogNode->Flags |= CATINFO_FLAG_PROMPT_FOR_TRUST;
CatalogNode->hWVTStateData = hWVTStateData;
hWVTStateData = NULL; // successfully transferred to catalog node
}
CatalogNodeStatus = Err;
} else if(Queue->Flags & FQF_QUEUE_FORCE_BLOCK_POLICY) {
//
// Don't notify the caller or log anything--just remember
// the error. (Note: we never want to consider
// Authenticode signatures in this case.
//
CatalogNodeStatus = Err;
MYASSERT(!hWVTStateData);
} else {
//
// Notify the caller of the failure (based on policy).
//
if(_HandleFailedVerification(
Owner,
Problem,
ProblemFile,
((Queue->DeviceDescStringId == -1)
? NULL
: pStringTableStringFromId(Queue->StringTable, Queue->DeviceDescStringId)),
Queue->DriverSigningPolicy,
Queue->Flags & FQF_DIGSIG_ERRORS_NOUI,
Err,
Queue->LogContext,
NULL,
NULL,
hWVTStateData))
{
if(hWVTStateData) {
//
// The user agreed that they trust the publisher of
// this Authenticode catalog (or, trust was
// implicitly granted because policy was Ignore).
//
CatalogNode->Flags |= CATINFO_FLAG_AUTHENTICODE_SIGNED;
//
// Unless policy is "Ignore", we want to update the
// error value to indicate the user confirmed their
// trust of this Authenticode publisher.
//
MYASSERT((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_BLOCKING);
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
Err = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
//
// INF/CAT was successfully verified--store the
// INF's final path (which is the same as its
// current path) into the catalog node.
//
CatalogNode->InfFinalPath = CatalogNode->InfFullPath;
} else {
//
// If the user actally saw UI (i.e., policy isn't
// "Ignore", then set a flag so we don't popup any
// more digital signature verification UI...
//
if((Queue->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE)
!= DRIVERSIGN_NONE) {
Queue->Flags |= FQF_DIGSIG_ERRORS_NOUI;
}
//
// If the caller wants a chance to set a system
// restore point prior to doing any unsigned
// installations, then we abort now with a
// "special" error code that tells them what to
// do...
//
if(Queue->Flags & FQF_ABORT_IF_UNSIGNED) {
CatalogNodeStatus = Err = ERROR_SET_SYSTEM_RESTORE_POINT;
} else {
//
// Since we're going to use the INF/CAT anyway,
// in spite of digital signature problems, then
// we need to set the INF's final path to be
// the same as its current path.
//
CatalogNode->InfFinalPath = CatalogNode->InfFullPath;
}
}
} else {
//
// The caller doesn't want to proceed (or policy was
// block)
//
if(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) {
//
// Change error to indicate that we have a real
// failure (instead of a "wait and see" condition).
//
Err = ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED;
}
CatalogNodeStatus = Err;
}
if(hWVTStateData) {
//
// Don't need the WinVerifyTrust state data any
// longer...
//
pSetupCloseWVTStateData(hWVTStateData);
hWVTStateData = NULL;
}
}
}
if((CatalogNodeStatus == NO_ERROR) ||
(CatalogNodeStatus == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// If this is the primary device INF, and the caller requested
// information about that INF's final pathname, then store that
// information in the caller-supplied buffer(s) now.
//
if(DeviceInfFinalName &&
(CatalogNode->Flags & CATINFO_FLAG_PRIMARY_DEVICE_INF)) {
lstrcpy(DeviceInfFinalName, InfFullPath);
if(DeviceInfNewlyCopied) {
*DeviceInfNewlyCopied = FALSE;
}
}
}
}
//
// At this point, there are really 3 successful status codes we can
// have...
//
if((Err == NO_ERROR) ||
(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) ||
(Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER)) {
//
// If we successfully validated an "OEM" INF globally, then we want
// to remember this fact. This will allow us to generate a
// signature verification failure against any file copy nodes
// associated with this catalog node.
//
if(InfVerifyType == VERIFY_OEM_INF_GLOBALLY) {
MYASSERT(!(CatalogNode->Flags &
(CATINFO_FLAG_PROMPT_FOR_TRUST | CATINFO_FLAG_AUTHENTICODE_SIGNED)));
MYASSERT(Err == NO_ERROR);
CatalogNode->VerificationFailureError = ERROR_NO_CATALOG_FOR_OEM_INF;
} else {
CatalogNode->VerificationFailureError = Err;
}
} else {
//
// CatalogNodeStatus may or may not be NO_ERROR, since it's
// possible we encountered a digital signature verification failure
// for this catalog node, but the user elected to proceed anyway.
// Mark this node with the failure encountered...
//
CatalogNode->VerificationFailureError = Err;
CatalogNode->CatalogFilenameOnSystem[0] = TEXT('\0');
}
if((ReturnStatus == NO_ERROR) && (CatalogNodeStatus != NO_ERROR)) {
//
// First critical error we've encountered--propagate the failure
// for this catalog to our return status that will be returned to
// the caller once we've finished looking at all the catalogs.
//
ReturnStatus = CatalogNodeStatus;
//
// Unless the VERCAT_NO_PROMPT_ON_ERROR flag has been set, we
// should abort right now--there's no sense in going any further.
//
if(!(Flags & VERCAT_NO_PROMPT_ON_ERROR)) {
break;
}
//
// If we've found more than one untrusted publisher, we should also
// break.
//
if(AuthSigPromptCount > 1) {
MYASSERT(CatalogNodeStatus == ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED);
break;
}
}
}
//
// If the caller requested no prompting, then we don't want to mark this
// queue as 'failed', since the user never heard about it. However, if the
// verification succeeded, then we _do_ want to mark it as successful.
//
if(Flags & VERCAT_NO_PROMPT_ON_ERROR) {
if(ReturnStatus == NO_ERROR) {
Queue->Flags |= FQF_DID_CATALOGS_OK;
//
// We'd better not have any outstanding Authenticode trust issues.
//
MYASSERT(AuthSigPromptCount == 0);
} else {
//
// If we were still need to confirm with the user that they trust
// the publisher of an Authenticode-signed catalog, then set a flag
// so we can track that.
//
if((ReturnStatus == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) &&
(AuthSigPromptCount == 1)) {
Queue->Flags |= FQF_DID_CATALOGS_PROMPT_FOR_TRUST;
}
}
} else {
Queue->Flags |= (ReturnStatus == NO_ERROR) ? FQF_DID_CATALOGS_OK
: FQF_DID_CATALOGS_FAILED;
//
// If we were successful, then we would've encountered at most 1
// Authenticode-signed catalog that required user-prompting to confirm
// trust of the publisher...
//
MYASSERT((ReturnStatus != NO_ERROR) || (AuthSigPromptCount < 2));
}
return ReturnStatus;
}
VOID
LogFailedVerification(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN DWORD MessageId,
IN DWORD Error,
IN LPCTSTR ProblemFile,
IN LPCTSTR DeviceDesc, OPTIONAL
IN DWORD LogLevel
)
/*++
Routine Description:
This routine logs when a verification failed but the file was installed
anyway.
Arguments:
LogContext - optionally supplies a pointer to the context for logging.
If this is not supplied, errors will be logged to the default context.
MessageId - Message to display
Error - supplies the code the the error that caused the failure.
ProblemFile - supplies the file path to the file associated with
the problem. In some cases this is a full path, in others it's just a
filename. The caller decides which makes sense in a particular
scenario. For example, a system catalog is in some funky directory
and there is no need to tell the user the full path. But in the case
where a catalog comes from an oem location, there might be some benefit
to telling the user the full path.
DeviceDesc - Optionally, supplies the device description to be used in the
digital signature verification error dialogs that may be popped up.
LogLevel - Either SETUP_LOG_ERROR, SETUP_LOG_WARNING, or SETUP_LOG_INFO.
Return Value:
NONE.
--*/
{
PSETUP_LOG_CONTEXT lc = NULL;
MYASSERT(Error != NO_ERROR);
MYASSERT(ProblemFile && *ProblemFile);
if (!LogContext) {
if (CreateLogContext(NULL, TRUE, &lc) == NO_ERROR) {
//
// success
//
LogContext = lc;
} else {
lc = NULL;
}
}
//
// a device install failed
//
WriteLogEntry(
LogContext,
LogLevel | SETUP_LOG_BUFFER,
MessageId,
NULL,
ProblemFile,
DeviceDesc);
WriteLogError(
LogContext,
LogLevel,
Error);
if (lc) {
DeleteLogContext(lc);
}
}
BOOL
pSetupHandleFailedVerification(
IN HWND Owner,
IN SetupapiVerifyProblem Problem,
IN LPCTSTR ProblemFile,
IN LPCTSTR DeviceDesc, OPTIONAL
IN DWORD DriverSigningPolicy,
IN BOOL NoUI,
IN DWORD Error,
IN PVOID LogContext, OPTIONAL
OUT PDWORD Flags, OPTIONAL
IN LPCTSTR TargetFile OPTIONAL
)
/*++
Routine Description:
This routine deals with a failed verification.
System policy is checked. If the policy is block, UI is displayed telling
the user that they're hosed. If the policy is ask-user, then ui is
displayed requesting the user's decision about whether to ignore the
verification failure and take the risk. If the policy is ignore, nothing
is done.
Arguments:
Owner - supplies window to own the dialog.
Problem - supplies a constant indicating what caused the failure. This
value indicates what type of file is specified in the ProblemFile
argument.
ProblemFile - supplies the file path to the file associated with
the problem. In some cases this is a full path, in others it's just a
filename. The caller decides which makes sense in a particular
scenario. For example, a system catalog is in some funky directory
and there is no need to tell the user the full path. But in the case
where a catalog comes from an oem location, there might be some benefit
to telling the user the full path.
NOTE: if this API is being called because of a blocked driver then a
full path should always be passed in.
DeviceDesc - Optionally, supplies the device description to be used in the
digital signature verification error dialogs that may be popped up.
DriverSigningPolicy - supplies the driver signing policy currently in
effect. May be one of the three following values:
DRIVERSIGN_NONE - silently succeed installation of unsigned/
incorrectly-signed files. A PSS log entry will
be generated, however.
DRIVERSIGN_WARNING - warn the user, but let them choose whether or not
they still want to install the problematic file.
If the user elects to proceed with the
installation, A PSS log entry will be generated
noting this fact.
DRIVERSIGN_BLOCKING - do not allow the file to be installed
The above values may be OR'ed with DRIVERSIGN_ALLOW_AUTHENTICODE. This
indicates that Authenticode-signed catalogs are permissable. The fact
that we were called with this bit set means that either:
(a) the catalog was Authenticode-signed, but the publisher's cert
wasn't contained in the TrustedPublisher cert store. Thus, the
user must be prompted in order to establish their trust of the
publisher. (This is only allowed for "Warn"--in the "Block"
case, the user doesn't get the chance to trust the publisher--
that requires that the publisher's cert is in the
TrustedPublisher store.)
(b) the file isn't signed at all. The fact that this bit is set
indicates that we should give the user that tells them that the
package could've been signed by Authenticode, but wasn't.
Without this bit, we want to give the standard driver signing
dialog that extolls the merits of WHQL.
NoUI - if TRUE, then a dialog box should not be displayed to the user, even
if policy is warn or block. This will typically be set to TRUE after
the user has previously been informed of a digital signature problem
with the package they're attempting to install, but have elected to
proceed with the installation anyway. The behavior of the "Yes" button,
then, is really a "yes to all".
Error - supplies the code of the error that caused the failure.
LogContext - optionally supplies a pointer to the context for logging.
If this is not supplied, errors will be logged to the default context.
This is declared as a PVOID so external functions don't need to know
what a SETUP_LOG_CONTEXT is.
Flags - optionally supplies a pointer to a DWORD that receives one or more
of the following file queue node flags indicating that we made an
exemption for installing a protected system file:
IQF_TARGET_PROTECTED - TargetFile (see below) is a protected system
file.
IQF_ALLOW_UNSIGNED - An exception has been granted so that TargetFile
(see below) may be replaced by an unsigned file.
TargetFile - optionally supplies a pointer to a string that specifies a
destination file if one exists. This is only used if we want to exempt
a file operation on this file. If this parameter is not specified, then
it is assumed the file will _not_ be replaced (i.e., it may already be
on the system in its unsigned state), and no SFP exemption will be
attempted.
Return Value:
Boolean value indicating whether the caller should continue.
If FALSE, then the current operation should be aborted, as the combination
of system policy and user input indicated that the risk should not
be taken.
--*/
{
//
// This routine should not be called when Authenticode validation is a
// possibility...
//
MYASSERT((DriverSigningPolicy == DRIVERSIGN_NONE) ||
(DriverSigningPolicy == DRIVERSIGN_WARNING) ||
(DriverSigningPolicy == DRIVERSIGN_BLOCKING));
MYASSERT(Problem != SetupapiVerifyNoProblem);
MYASSERT(ProblemFile && *ProblemFile);
MYASSERT(Error != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
return _HandleFailedVerification(Owner,
Problem,
ProblemFile,
DeviceDesc,
DriverSigningPolicy,
NoUI,
Error,
LogContext,
Flags,
TargetFile,
NULL
);
}
BOOL
_HandleFailedVerification(
IN HWND Owner,
IN SetupapiVerifyProblem Problem,
IN LPCTSTR ProblemFile,
IN LPCTSTR DeviceDesc, OPTIONAL
IN DWORD DriverSigningPolicy,
IN BOOL NoUI,
IN DWORD Error,
IN PVOID LogContext, OPTIONAL
OUT PDWORD Flags, OPTIONAL
IN LPCTSTR TargetFile, OPTIONAL
IN HANDLE hWVTStateData OPTIONAL
)
/*++
Routine Description:
(See pSetupHandleFailedVerification)
Arguments:
See pSetupHandleFailedVerification, with following differences:
DriverSigningPolicy - supplies the driver signing policy currently in
effect. May be one of the three following values...
DRIVERSIGN_NONE - silently succeed installation of unsigned/
incorrectly-signed files. A PSS log entry will
be generated, however.
DRIVERSIGN_WARNING - warn the user, but let them choose whether or not
they still want to install the problematic file.
If the user elects to proceed with the
installation, A PSS log entry will be generated
noting this fact.
DRIVERSIGN_BLOCKING - do not allow the file to be installed
...potentially OR'ed with DRIVERSIGN_ALLOW_AUTHENTICODE (i.e., high bit
set). This flag indicates that policy allows for validation via an
Authenticode catalog.
hWVTStateData - optionally supplies a handle that provides WinVerifyTrust
state data retrieved upon successful validation of an Authenticode
catalog, in the case where the publisher isn't in the TrustedPublisher
store. This handle is used for the Authenticode confirmation dialog
in order to present the user with explicit information about the
Authenticode package they're about to install (i.e., publisher, signed
date, etc.). NOTE: if this handle is non-NULL, then the
DRIVERSIGN_ALLOW_AUTHENTICODE bit must also be set in the specified
DriverSigningPolicy.
Return Value:
Boolean value indicating whether the caller should continue.
If FALSE, then the current operation should be aborted, as the combination
of system policy and user input indicated that the risk should not
be taken.
NOTE: If this function returns TRUE (i.e., non-zero), and if the caller
specified (via the AuthSigPromptUser) that the user was to be prompted
regarding their trust of the publisher, then the caller should subsequently
treat the file and catalog as valid, and validate subsequent files via that
catalog as if its publisher were in the trusted store (because the user's
choice last for the duration of the queue committal currently underway).
--*/
{
BOOL b;
INT_PTR iRes;
HANDLE hDialogEvent = NULL;
MYASSERT(Error != NO_ERROR);
MYASSERT((Problem != SetupapiVerifyNoProblem) && ProblemFile && *ProblemFile);
//
// If we're being called to prompt the user about whether they trust an
// Authenticode publisher, then our ProblemFile had better be a catalog...
//
MYASSERT((Error != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) ||
(Problem == SetupapiVerifyCatalogProblem));
//
// If we already established that the Authenticode publisher should be
// trusted, then we shouldn't be calling this routine...
//
MYASSERT(Error != ERROR_AUTHENTICODE_TRUSTED_PUBLISHER);
//
// If we've been asked to prompt the user about whether they trust the
// publisher of an Authenticode catalog, then policy had better indicate
// that this is OK!
//
MYASSERT(!hWVTStateData || (DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE));
//
// If we're running non-interactive, then we always silently block,
// regardless of policy.
//
if(GlobalSetupFlags & PSPGF_NONINTERACTIVE) {
//
// SPLOG -- log a PSS entry recording this event.
//
if(Problem == SetupapiVerifyDriverBlocked) {
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_BLOCKED_FOR_DEVICE_ERROR_NONINTERACTIVE : MSG_LOG_DRIVER_BLOCKED_ERROR_NONINTERACTIVE,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
} else {
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_NONINTERACTIVE : MSG_LOG_SIGNING_ERROR_NONINTERACTIVE,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
}
return FALSE;
}
if(GuiSetupInProgress) {
hDialogEvent = CreateEvent(NULL,TRUE,FALSE,SETUP_HAS_OPEN_DIALOG_EVENT);
}
if(Problem == SetupapiVerifyDriverBlocked) {
//
// Handle a driver block failure.
// only applicable to UNICODE
// ANSI won't report this problem code
//
HSDB hSDBDrvMain = NULL;
TAGREF tagref = TAGREF_NULL;
DRIVERBLOCK_PROMPT DriverBlockPrompt = {0};
//
// Never continue if the driver is in the bad driver database!
//
b = FALSE;
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_BLOCKED_FOR_DEVICE_ERROR : MSG_LOG_DRIVER_BLOCKED_ERROR,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
if(!(GlobalSetupFlags & PSPGF_UNATTENDED_SETUP)) {
//
// Show the driver blocking UI
//
DriverBlockPrompt.lpszFile = (TargetFile != NULL)
? TargetFile
: ProblemFile;
if((hSDBDrvMain = SdbInitDatabaseEx(SDB_DATABASE_MAIN_DRIVERS,
NULL,
DEFAULT_IMAGE))) {
HANDLE hFile = INVALID_HANDLE_VALUE;
//
// We are probably dealing with a temp file name at this point,
// so we need to get a file handle to pass to SdbGetDatabaseMatch
// along with the final destination file name.
//
hFile = CreateFile(ProblemFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hFile != INVALID_HANDLE_VALUE) {
//
// Pass the TargetFile (the destination filename) to
// SdbGetDatabaseMatch because that will be what is
// in the bad driver database.
//
tagref = SdbGetDatabaseMatch(hSDBDrvMain,
(TargetFile != NULL)
? pSetupGetFileTitle(TargetFile)
: ProblemFile,
hFile,
NULL,
0);
if (tagref != TAGREF_NULL) {
SdbReadDriverInformation(hSDBDrvMain,
tagref,
&(DriverBlockPrompt.entryinfo));
}
CloseHandle(hFile);
}
SdbReleaseDatabase(hSDBDrvMain);
}
//
// Always call the dialog code, even if we could access the database.
//
iRes = DialogBoxParam(MyDllModuleHandle,
MAKEINTRESOURCE(IDD_DRIVERBLOCK),
IsWindow(Owner) ? Owner : NULL,
DriverBlockDlgProc,
(LPARAM)&DriverBlockPrompt
);
}
} else {
//
// Handle a digital signature failure.
//
// If the policy is block, then the user always gets informed of a
// problem (i.e., there is no "yes" option, hence no "yes to all"
// semantics).
//
CERT_PROMPT CertPrompt;
AUTHENTICODE_CERT_PROMPT AuthenticodeCertPrompt;
MYASSERT(((DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE) != DRIVERSIGN_BLOCKING)
|| !NoUI);
CertPrompt.lpszDescription = DeviceDesc;
CertPrompt.lpszFile = ProblemFile;
CertPrompt.ProblemType = Problem;
CertPrompt.DriverSigningPolicy = DriverSigningPolicy;
AuthenticodeCertPrompt.lpszDescription = DeviceDesc;
AuthenticodeCertPrompt.hWVTStateData = hWVTStateData;
AuthenticodeCertPrompt.Error = Error;
switch(DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE) {
case DRIVERSIGN_NONE :
//
// SPLOG -- log a PSS entry recording this event.
//
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_POLICY_NONE : MSG_LOG_SIGNING_ERROR_POLICY_NONE,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_WARNING
);
//
// If requested, find out if the file is protected (we may need
// to skip it if it's being queued up for delayed copy).
//
if(Flags && TargetFile) {
MYASSERT(Error != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED);
if(IsFileProtected(TargetFile,
(PSETUP_LOG_CONTEXT)LogContext,
NULL)) {
*Flags = IQF_TARGET_PROTECTED;
}
}
b = TRUE;
goto exit;
case DRIVERSIGN_WARNING :
if(NoUI) {
//
// SPLOG -- log a PSS entry recording this event.
//
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_AUTO_YES : MSG_LOG_SIGNING_ERROR_AUTO_YES,
Error,
ProblemFile,
DeviceDesc,
((Error == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? DRIVER_LOG_INFO
: DRIVER_LOG_WARNING)
);
iRes = IDC_VERIFY_WARN_YES;
} else if(GlobalSetupFlags & PSPGF_UNATTENDED_SETUP) {
//
// SPLOG -- log a PSS entry recording this event.
//
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_AUTO_NO : MSG_LOG_SIGNING_ERROR_AUTO_NO,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
iRes = IDC_VERIFY_WARN_NO;
} else {
if (hDialogEvent) {
SetEvent(hDialogEvent);
}
if(DriverSigningPolicy & DRIVERSIGN_ALLOW_AUTHENTICODE) {
if(AuthenticodeCertPrompt.Error == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED) {
iRes = DialogBoxParam(MyDllModuleHandle,
AuthenticodeCertPrompt.lpszDescription ?
MAKEINTRESOURCE(IDD_DEVICE_VERIFY_AUTHENTICODE) :
MAKEINTRESOURCE(IDD_SOFTWARE_VERIFY_AUTHENTICODE),
IsWindow(Owner) ? Owner : NULL,
AuthenticodeCertifyDlgProc,
(LPARAM)&AuthenticodeCertPrompt
);
} else {
iRes = DialogBoxParam(MyDllModuleHandle,
AuthenticodeCertPrompt.lpszDescription ?
MAKEINTRESOURCE(IDD_DEVICE_VERIFY_NO_AUTHENTICODE) :
MAKEINTRESOURCE(IDD_SOFTWARE_VERIFY_NO_AUTHENTICODE),
IsWindow(Owner) ? Owner : NULL,
NoAuthenticodeCertifyDlgProc,
(LPARAM)&AuthenticodeCertPrompt
);
}
} else {
iRes = DialogBoxParam(MyDllModuleHandle,
CertPrompt.lpszDescription ?
MAKEINTRESOURCE(IDD_DEVICE_VERIFY_WARNING) :
MAKEINTRESOURCE(IDD_SOFTWARE_VERIFY_WARNING),
IsWindow(Owner) ? Owner : NULL,
CertifyDlgProc,
(LPARAM)&CertPrompt
);
}
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc
?(iRes == IDC_VERIFY_WARN_YES ? MSG_LOG_DRIVER_SIGNING_ERROR_WARN_YES : MSG_LOG_DRIVER_SIGNING_ERROR_WARN_NO)
:(iRes == IDC_VERIFY_WARN_YES ? MSG_LOG_SIGNING_ERROR_WARN_YES : MSG_LOG_SIGNING_ERROR_WARN_NO),
Error,
ProblemFile,
DeviceDesc,
((Error == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)
? ((iRes == IDC_VERIFY_WARN_YES) ? DRIVER_LOG_INFO : DRIVER_LOG_ERROR)
: ((iRes == IDC_VERIFY_WARN_YES) ? DRIVER_LOG_WARNING : DRIVER_LOG_ERROR))
);
}
break;
case DRIVERSIGN_BLOCKING :
if(GlobalSetupFlags & PSPGF_UNATTENDED_SETUP) {
//
// During UNATTENDED, we block silently
//
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_SILENT_BLOCK : MSG_LOG_SIGNING_ERROR_SILENT_BLOCK,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
iRes = IDC_VERIFY_BLOCK_OK;
} else {
LogFailedVerification(
(PSETUP_LOG_CONTEXT) LogContext,
DeviceDesc ? MSG_LOG_DRIVER_SIGNING_ERROR_POLICY_BLOCK : MSG_LOG_SIGNING_ERROR_POLICY_BLOCK,
Error,
ProblemFile,
DeviceDesc,
DRIVER_LOG_ERROR
);
if (hDialogEvent) {
SetEvent(hDialogEvent);
}
iRes = DialogBoxParam(MyDllModuleHandle,
CertPrompt.lpszDescription ?
MAKEINTRESOURCE(IDD_DEVICE_VERIFY_BLOCK) :
MAKEINTRESOURCE(IDD_SOFTWARE_VERIFY_BLOCK),
IsWindow(Owner) ? Owner : NULL,
CertifyDlgProc,
(LPARAM)&CertPrompt
);
}
break;
default :
//
// We don't know about any other policy values!
//
MYASSERT(0);
b = FALSE;
goto exit;
}
switch(iRes) {
case IDC_VERIFY_WARN_NO:
case IDC_VERIFY_BLOCK_OK:
b = FALSE;
break;
case IDC_VERIFY_WARN_YES:
if(TargetFile) {
pSetupExemptFileFromProtection(TargetFile,
(DWORD) -1,
(PSETUP_LOG_CONTEXT)LogContext,
Flags
);
}
b = TRUE;
break;
default:
//
// Shouldn't get any other values.
//
MYASSERT(0);
b = FALSE;
}
}
exit:
if(hDialogEvent) {
ResetEvent(hDialogEvent);
CloseHandle(hDialogEvent);
}
return b;
}
INT_PTR
CALLBACK
CertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
)
/*++
Routine Description:
This is the dialog procedure for the driver signing UI that is presented to
the user when a verification failure is encountered. This dialog handles
both the 'warn' and 'block' cases.
--*/
{
LOGFONT LogFont;
HFONT hFontBold = NULL;
HICON hIcon = NULL;
OSVERSIONINFOEX osVersionInfoEx;
PCERT_PROMPT lpCertPrompt;
lpCertPrompt = (PCERT_PROMPT)GetWindowLongPtr(hwnd, DWLP_USER);
switch(msg) {
case WM_INITDIALOG:
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
MessageBeep(MB_ICONASTERISK);
lpCertPrompt = (PCERT_PROMPT)lParam;
//
// If lpszDescription is not NULL then this is the device verify
// warning dialog, otherwise it is the software warning dialog.
//
if(lpCertPrompt->lpszDescription != NULL) {
SetDlgItemText(hwnd, IDC_VERIFY_FILENAME, lpCertPrompt->lpszDescription);
SetDlgText(hwnd, IDC_VERIFY_BOLD, IDS_DEVICE_VERIFY_MSG1, IDS_DEVICE_VERIFY_MSG2);
} else {
SetDlgText(hwnd, IDC_VERIFY_BOLD, IDS_SOFTWARE_VERIFY_MSG1, IDS_SOFTWARE_VERIFY_MSG2);
}
//
// Create the bold font and bold any necessary text.
//
hFontBold = (HFONT)SendMessage(GetDlgItem(hwnd, IDC_VERIFY_BOLD),
WM_GETFONT, 0, 0);
GetObject(hFontBold, sizeof(LogFont), &LogFont);
LogFont.lfWeight = FW_BOLD;
hFontBold = CreateFontIndirect(&LogFont);
if (hFontBold) {
SetWindowFont(GetDlgItem(hwnd, IDC_VERIFY_BOLD), hFontBold, TRUE);
}
//
// Set the appropriate warning or error icon.
//
// (We shouldn't be here if policy is "Ignore", nor if we're
// allowing for Authenticode signatures.)
//
MYASSERT((lpCertPrompt->DriverSigningPolicy == DRIVERSIGN_WARNING) ||
((lpCertPrompt->DriverSigningPolicy & ~DRIVERSIGN_ALLOW_AUTHENTICODE) == DRIVERSIGN_BLOCKING));
hIcon = LoadIcon(NULL,
(lpCertPrompt->DriverSigningPolicy == DRIVERSIGN_WARNING) ?
IDI_WARNING :
IDI_ERROR
);
SendDlgItemMessage(hwnd, IDC_VERIFY_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
//
// The link won't work in GUI mode setup since help center has not yet
// been installed, so we will just show the static text instead.
//
ShowWindow(GetDlgItem(hwnd, IDC_VERIFY_TESTING_LINK), !GuiSetupInProgress);
ShowWindow(GetDlgItem(hwnd, IDC_VERIFY_TESTING_TEXT), GuiSetupInProgress);
//
// If we are in GUI mode setup then we want to change the text of
// the buttons to be "Yes" and "No". We also add the following line
// of text: "Do you want to continue installing the software for
// this hardware?"
//
ShowWindow(GetDlgItem(hwnd, IDC_VERIFY_SETUP_TEXT), GuiSetupInProgress);
if (GuiSetupInProgress) {
TCHAR szButtonText[MAX_PATH];
if (LoadString(MyDllModuleHandle, IDS_YES, szButtonText, SIZECHARS(szButtonText))) {
SetDlgItemText(hwnd, IDC_VERIFY_WARN_YES, szButtonText);
}
if (LoadString(MyDllModuleHandle, IDS_NO, szButtonText, SIZECHARS(szButtonText))) {
SetDlgItemText(hwnd, IDC_VERIFY_WARN_NO, szButtonText);
}
}
//
// Make sure this dialog is in the foreground (at least for this
// process).
//
SetForegroundWindow(hwnd);
if(lpCertPrompt->DriverSigningPolicy == DRIVERSIGN_WARNING) {
SetFocus(GetDlgItem(hwnd, IDC_VERIFY_WARN_NO));
}
return FALSE;
case WM_DESTROY:
if (hFontBold) {
DeleteObject(hFontBold);
hFontBold = NULL;
}
if (hIcon) {
DestroyIcon(hIcon);
}
break;
case WM_NOTIFY:
switch (((NMHDR FAR *)lParam)->code) {
case NM_RETURN:
case NM_CLICK:
//
// We need to know if this is a server machine or a workstation
// machine since there are different help topic structures for
// the different products.
//
ZeroMemory(&osVersionInfoEx, sizeof(osVersionInfoEx));
osVersionInfoEx.dwOSVersionInfoSize = sizeof(osVersionInfoEx);
if (!GetVersionEx((LPOSVERSIONINFO)&osVersionInfoEx)) {
//
// If GetVersionEx fails then assume this is a workstation
// machine.
//
osVersionInfoEx.wProductType = VER_NT_WORKSTATION;
}
ShellExecute(hwnd,
TEXT("open"),
TEXT("HELPCTR.EXE"),
(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
? TEXT("HELPCTR.EXE -url hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm")
: TEXT("HELPCTR.EXE -url hcp://services/subsite?node=Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm"),
NULL,
SW_SHOWNORMAL
);
break;
}
break;
case WM_COMMAND:
switch(wParam) {
case IDC_VERIFY_WARN_NO:
case IDC_VERIFY_WARN_YES:
case IDC_VERIFY_BLOCK_OK:
EndDialog(hwnd, (int)wParam);
break;
default:
break;
}
break;
default:
break;
}
return FALSE;
}
PTSTR
GetCryptoErrorString(
HRESULT hr
)
/*++
Routine Description:
This routine takes in an HRESULT error result returned by WinVerifyTrust
and returns a buffer containing a friendly error string that can be
presented to the user.
NOTE: This API calls FormatMessage and so the returned string MUST be freed
using LocalFree and not MyFree
Arguments:
hr - HRESULT returned from WinVerifyTrust.
Return Value:
Pointer to the error string, or NULL if an error occured.
--*/
{
UINT ResourceId = 0;
DWORD_PTR MessageArgument;
ULONG CchLength;
PTSTR CryptoError = NULL;
PTSTR TempBuffer = NULL;
TCHAR Error[33];
try {
//
// See if it maps to some non system error code
//
switch (hr) {
case TRUST_E_SYSTEM_ERROR:
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_INVALID_PARAMETER:
//
// Set the resourceid to zero... these will be mapped to
// IDS_SPC_UNKNOWN and the error code displayed.
//
ResourceId = 0;
break;
case HRESULT_FROM_WIN32(ERROR_NOT_FOUND):
ResourceId = IDS_ELEMENT_NOT_FOUND;
break;
case CRYPT_E_FILE_ERROR:
ResourceId = IDS_FILE_NOT_FOUND;
break;
case TRUST_E_PROVIDER_UNKNOWN:
ResourceId = IDS_SPC_PROVIDER;
break;
case TRUST_E_SUBJECT_FORM_UNKNOWN:
ResourceId = IDS_SPC_SUBJECT;
break;
case TRUST_E_NOSIGNATURE:
ResourceId = IDS_SPC_NO_SIGNATURE;
break;
case CRYPT_E_BAD_MSG:
ResourceId = IDS_SPC_BAD_SIGNATURE;
break;
case TRUST_E_BAD_DIGEST:
ResourceId = IDS_SPC_BAD_FILE_DIGEST;
break;
case CRYPT_E_NO_SIGNER:
ResourceId = IDS_SPC_NO_VALID_SIGNER;
break;
case TRUST_E_NO_SIGNER_CERT:
ResourceId = IDS_SPC_SIGNER_CERT;
break;
case TRUST_E_COUNTER_SIGNER:
ResourceId = IDS_SPC_VALID_COUNTERSIGNER;
break;
case CERT_E_EXPIRED:
ResourceId = IDS_SPC_CERT_EXPIRED;
break;
case TRUST_E_CERT_SIGNATURE:
ResourceId = IDS_SPC_CERT_SIGNATURE;
break;
case CERT_E_CHAINING:
ResourceId = IDS_SPC_CHAINING;
break;
case CERT_E_UNTRUSTEDROOT:
ResourceId = IDS_SPC_UNTRUSTED_ROOT;
break;
case CERT_E_UNTRUSTEDTESTROOT:
ResourceId = IDS_SPC_UNTRUSTED_TEST_ROOT;
break;
case CERT_E_VALIDITYPERIODNESTING:
ResourceId = IDS_SPC_INVALID_CERT_NESTING;
break;
case CERT_E_PURPOSE:
ResourceId = IDS_SPC_INVALID_PURPOSE;
break;
case TRUST_E_BASIC_CONSTRAINTS:
ResourceId = IDS_SPC_INVALID_BASIC_CONSTRAINTS;
break;
case TRUST_E_FINANCIAL_CRITERIA:
ResourceId = IDS_SPC_INVALID_FINANCIAL;
break;
case TRUST_E_TIME_STAMP:
ResourceId = IDS_SPC_TIMESTAMP;
break;
case CERT_E_REVOKED:
ResourceId = IDS_SPC_CERT_REVOKED;
break;
case CERT_E_REVOCATION_FAILURE:
ResourceId = IDS_SPC_REVOCATION_ERROR;
break;
case CRYPT_E_SECURITY_SETTINGS:
ResourceId = IDS_SPC_SECURITY_SETTINGS;
break;
case CERT_E_MALFORMED:
ResourceId = IDS_SPC_INVALID_EXTENSION;
break;
case CERT_E_WRONG_USAGE:
ResourceId = IDS_WRONG_USAGE;
break;
default:
ResourceId = 0;
break;
}
//
// If it does, load the string out of our resource string tables and
// return that. Otherwise, try to format the message from the system
//
if (ResourceId != 0) {
CryptoError = LocalAlloc(LPTR, (MAX_PATH*sizeof(TCHAR)));
if (CryptoError) {
CchLength = LoadString(MyDllModuleHandle,
ResourceId,
CryptoError,
MAX_PATH);
//
// Assert that CchLength is between 0 and MAX_PATH, if it is
// greater than MAX_PATH then the whole string won't fit into
// the buffer.
//
MYASSERT((CchLength > 0) && (CchLength < MAX_PATH));
//
// if LoadString returned 0 then free the memory we just allocated
// and return NULL.
//
if (!CchLength) {
LocalFree(CryptoError);
CryptoError = NULL;
leave;
}
}
} else if (( hr >= 0x80093000) && (hr <= 0x80093999)) {
TempBuffer = LocalAlloc(LPTR, (MAX_PATH*sizeof(TCHAR)));
if (!TempBuffer) {
leave;
}
CchLength = LoadString(MyDllModuleHandle,
IDS_SPC_OSS_ERROR,
TempBuffer,
MAX_PATH);
//
// Assert that CchLength is between 0 and MAX_PATH, if it is
// greater than MAX_PATH then the whole string won't fit into
// the buffer.
//
MYASSERT((CchLength > 0) && (CchLength < MAX_PATH));
//
// if LoadString returned 0 then free the memory we just allocated
// and return NULL.
//
if (!CchLength) {
LocalFree(TempBuffer);
TempBuffer = NULL;
leave;
}
StringCchPrintf(Error, SIZECHARS(Error), TEXT("%lx"), hr);
MessageArgument = (DWORD_PTR)Error;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
TempBuffer,
0,
0,
(LPWSTR)&CryptoError,
0,
(va_list *)&MessageArgument
) == 0) {
CryptoError = NULL;
leave;
}
} else {
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
0,
(LPWSTR)&CryptoError,
0,
NULL
) == 0) {
TempBuffer = LocalAlloc(LPTR, (MAX_PATH*sizeof(TCHAR)));
if (!TempBuffer) {
leave;
}
CchLength = LoadString(MyDllModuleHandle,
IDS_SPC_UNKNOWN,
TempBuffer,
MAX_PATH);
//
// Assert that CchLength is between 0 and MAX_PATH, if it is
// greater than MAX_PATH then the whole string won't fit into
// the buffer.
//
MYASSERT((CchLength > 0) && (CchLength < MAX_PATH));
//
// if LoadString returned 0 then free the memory we just allocated
// and return NULL.
//
if (!CchLength) {
LocalFree(TempBuffer);
TempBuffer = NULL;
leave;
}
StringCchPrintf(Error, SIZECHARS(Error), TEXT("%lx"), hr);
MessageArgument = (DWORD_PTR)Error;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
TempBuffer,
0,
0,
(LPWSTR)&CryptoError,
0,
(va_list *)&MessageArgument
) == 0) {
MYASSERT(0);
CryptoError = NULL;
leave;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
if(CryptoError) {
LocalFree(CryptoError);
CryptoError = NULL;
}
}
if (TempBuffer) {
LocalFree(TempBuffer);
}
return CryptoError;
}
INT_PTR
CALLBACK
AuthenticodeCertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
)
/*++
Routine Description:
This is the dialog procedure for the Authenticode driver signing UI that is
presented to the user when a verification failure is encountered for a non-
WHQL tested class but there is a valid Authenticode signature associated
with the package.
--*/
{
HICON hIcon = NULL;
PAUTHENTICODE_CERT_PROMPT AuthenticodePrompt;
PCRYPT_PROVIDER_DATA ProviderData;
PCRYPT_PROVIDER_SGNR ProviderSigner;
PCRYPT_PROVIDER_CERT ProviderCert;
FILETIME ftTimestamp;
SYSTEMTIME stTimestamp;
PTSTR Provider, Issuer, Timestamp;
ULONG CchSize;
TCHAR UnknownBuffer[MAX_PATH];
AuthenticodePrompt = (PAUTHENTICODE_CERT_PROMPT)GetWindowLongPtr(hwnd, DWLP_USER);
switch(msg) {
case WM_INITDIALOG:
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
MessageBeep(MB_ICONASTERISK);
AuthenticodePrompt = (PAUTHENTICODE_CERT_PROMPT)lParam;
//
// If lpszDescription is not NULL then this is the device verify
// warning dialog, otherwise it is the software warning dialog.
//
if(AuthenticodePrompt->lpszDescription != NULL) {
SetDlgItemText(hwnd, IDC_VERIFY_FILENAME, AuthenticodePrompt->lpszDescription);
}
//
// In order to get the publisher, issuer, and timestamp, we have
// to get the PCRYPT_PROVIDER_CERT structure, which we get from
// the CRYPT_PROVIDER_SGNR structure, which we get from the
// CRYPT_PROVIDER_DATA structure.
//
Provider = Issuer = Timestamp = NULL;
ProviderData = WTHelperProvDataFromStateData(AuthenticodePrompt->hWVTStateData);
MYASSERT(ProviderData);
if (ProviderData) {
ProviderSigner = WTHelperGetProvSignerFromChain(ProviderData,
0,
FALSE,
0);
MYASSERT(ProviderSigner);
if (ProviderSigner) {
ProviderCert = WTHelperGetProvCertFromChain(ProviderSigner,
0);
MYASSERT(ProviderCert);
if (ProviderCert) {
//
// Get the publisher.
// Note that we want the string to be of the form:
// <A>publisher</A> so it will show up as a link.
//
CchSize = CertGetNameString(ProviderCert->pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0);
if (CchSize > 1) {
CchSize += lstrlen(LINK_START) + lstrlen(LINK_END);
Provider = MyMalloc(CchSize * sizeof(TCHAR));
if (Provider) {
if (FAILED(StringCchCopy(Provider, CchSize, LINK_START)) ||
(0 == CertGetNameString(ProviderCert->pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
&Provider[lstrlen(LINK_START)],
CchSize)) ||
FAILED(StringCchCat(Provider, CchSize, LINK_END))) {
//
// We failed to create the string so just
// free the memory and set Provider to NULL,
// which will cause us to use the generic
// string down below.
//
MYASSERT(0);
MyFree(Provider);
Provider = NULL;
}
}
}
//
// Get the issuer
//
CchSize = CertGetNameString(ProviderCert->pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0);
if (CchSize > 1) {
Issuer = MyMalloc(CchSize * sizeof(TCHAR));
if (0 == CertGetNameString(ProviderCert->pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
Issuer,
CchSize)) {
//
// We failed to create the Issuer string so just
// free the memory and set Issuer to NULL, which
// will cause us to use the generic string down
// below.
//
MYASSERT(0);
MyFree(Issuer);
Issuer = NULL;
}
}
//
// Get the timestamp
//
if (FileTimeToLocalFileTime(&ProviderSigner->sftVerifyAsOf,
&ftTimestamp) &&
FileTimeToSystemTime(&ftTimestamp, &stTimestamp)) {
int CchDate, CchTime;
CchDate = GetDateFormat(LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
&stTimestamp,
NULL,
NULL,
0);
CchTime = GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_NOSECONDS,
&stTimestamp,
NULL,
NULL,
0);
MYASSERT(CchDate);
MYASSERT(CchTime);
if ((CchDate > 0) && (CchTime > 0)) {
//
// Allocate enough memory to hold the date, the
// time, plus a space inbetween them as well as
// the terminating NULL.
//
Timestamp = MyMalloc((CchDate + CchTime + 2) * sizeof(TCHAR));
if (Timestamp) {
if ((0 == GetDateFormat(LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
&stTimestamp,
NULL,
Timestamp,
CchDate + 1)) ||
FAILED(StringCchCat(Timestamp,
(CchDate + CchTime + 2),
TEXT(" "))) ||
(0 == GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_NOSECONDS,
&stTimestamp,
NULL,
&Timestamp[CchDate+1],
CchTime + 1))) {
//
// We failed to create the Timestamp
// string so just free the memory and
// set Timestamp to NULL, which will
// cause us to use the generic string
// down below.
//
MYASSERT(0);
MyFree(Timestamp);
Timestamp = NULL;
}
}
}
}
}
}
}
//
// Set the Provider, Issuer, and Timestamp strings. Note that we
// will provide "unknown" defaults for Provider and Issuer if
// they don't exist.
//
if (Provider) {
SetDlgItemText(hwnd, IDC_VERIFY_PUBLISHER_LINK, Provider);
} else {
if (LoadString(MyDllModuleHandle,
IDS_UNKNOWNPUBLISHER,
UnknownBuffer,
SIZECHARS(UnknownBuffer))) {
SetDlgItemText(hwnd, IDC_VERIFY_PUBLISHER_LINK, UnknownBuffer);
}
}
if (Issuer) {
if (LoadString(MyDllModuleHandle,
IDS_AUTHENTICITY,
UnknownBuffer,
SIZECHARS(UnknownBuffer))) {
StringCchCat(UnknownBuffer, SIZECHARS(UnknownBuffer), Issuer);
SetDlgItemText(hwnd, IDC_VERIFY_IDENTITY, UnknownBuffer);
}
} else {
if (LoadString(MyDllModuleHandle,
IDS_AUTHENTICITY,
UnknownBuffer,
SIZECHARS(UnknownBuffer)) &&
LoadString(MyDllModuleHandle,
IDS_UNKNOWNPUBLISHERCERTISSUER,
&UnknownBuffer[lstrlen(UnknownBuffer)],
(SIZECHARS(UnknownBuffer) - lstrlen(UnknownBuffer)))) {
SetDlgItemText(hwnd, IDC_VERIFY_IDENTITY, UnknownBuffer);
}
}
if (Timestamp) {
SetDlgItemText(hwnd, IDC_VERIFY_DATE_PUBLISHED, Timestamp);
}
//
// Set the security alert icon.
//
hIcon = LoadIcon(MyDllModuleHandle, MAKEINTRESOURCE(IDI_SECURITY));
SendDlgItemMessage(hwnd, IDC_VERIFY_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
//
// Make sure this dialog is in the foreground (at least for this
// process).
//
SetForegroundWindow(hwnd);
//
// Set the focus on the "No" button.
//
SetFocus(GetDlgItem(hwnd, IDC_VERIFY_WARN_NO));
return FALSE;
case WM_DESTROY:
if (hIcon) {
DestroyIcon(hIcon);
}
break;
case WM_NOTIFY:
switch (((NMHDR FAR *)lParam)->code) {
case NM_RETURN:
case NM_CLICK:
switch (((LPNMHDR)lParam)->idFrom) {
case IDC_VERIFY_TESTING_LINK:
//
// NTRAID#NTBUG9-707966-2002/09/24-jasonc
// We need to change the URL to point to the Authenticode
// topic.
//
ShellExecute(hwnd,
TEXT("open"),
TEXT("HELPCTR.EXE"),
TEXT("HELPCTR.EXE -url hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm"),
NULL,
SW_SHOWNORMAL
);
break;
case IDC_VERIFY_PUBLISHER_LINK:
ProviderData = WTHelperProvDataFromStateData(AuthenticodePrompt->hWVTStateData);
MYASSERT(ProviderData);
if(ProviderData) {
ProviderSigner = WTHelperGetProvSignerFromChain(ProviderData,
0,
FALSE,
0);
MYASSERT(ProviderSigner);
if(ProviderSigner) {
ProviderCert = WTHelperGetProvCertFromChain(ProviderSigner,
0);
MYASSERT(ProviderCert);
if(ProviderCert) {
CRYPTUI_VIEWCERTIFICATE_STRUCT vcs;
BOOL bPropertiesChanged = FALSE;
ZeroMemory(&vcs, sizeof(vcs));
vcs.dwSize = sizeof(vcs);
vcs.hwndParent = hwnd;
vcs.pCryptProviderData = ProviderData;
vcs.fpCryptProviderDataTrustedUsage = TRUE;
vcs.pCertContext = ProviderCert->pCert;
CryptUIDlgViewCertificate(
&vcs,
&bPropertiesChanged);
}
}
}
break;
}
break;
}
break;
case WM_COMMAND:
switch(wParam) {
case IDC_VERIFY_WARN_NO:
case IDC_VERIFY_WARN_YES:
EndDialog(hwnd, (int)wParam);
break;
case IDC_VERIFY_WARN_MORE_INFO:
WinHelp(hwnd, TEXT("SECAUTH.HLP"), HELP_CONTEXT, IDH_SECAUTH_SIGNED);
break;
default:
break;
}
break;
default:
break;
}
return FALSE;
}
INT_PTR
CALLBACK
NoAuthenticodeCertifyDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
)
/*++
Routine Description:
This is the dialog procedure for the Authenticode driver signing UI that is
presented to the user when a verification failure is encountered for a non-
WHQL tested class.
--*/
{
HICON hIcon = NULL;
PAUTHENTICODE_CERT_PROMPT AuthenticodePrompt;
PTSTR ErrorString;
AuthenticodePrompt = (PAUTHENTICODE_CERT_PROMPT)GetWindowLongPtr(hwnd, DWLP_USER);
switch(msg) {
case WM_INITDIALOG:
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
MessageBeep(MB_ICONASTERISK);
AuthenticodePrompt = (PAUTHENTICODE_CERT_PROMPT)lParam;
//
// If lpszDescription is not NULL then this is the device verify
// warning dialog, otherwise it is the software warning dialog.
//
if(AuthenticodePrompt->lpszDescription != NULL) {
SetDlgItemText(hwnd, IDC_VERIFY_FILENAME, AuthenticodePrompt->lpszDescription);
}
ErrorString = GetCryptoErrorString(HRESULT_FROM_SETUPAPI(AuthenticodePrompt->Error));
if (ErrorString) {
SetDlgItemText(hwnd, IDC_VERIFY_AUTHENTICODE_PROBLEM, ErrorString);
}
//
// Set the security alert icon.
//
hIcon = LoadIcon(NULL,
IDI_WARNING
);
SendDlgItemMessage(hwnd, IDC_VERIFY_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
//
// Make sure this dialog is in the foreground (at least for this
// process).
//
SetForegroundWindow(hwnd);
//
// Set the focus on the "No" button.
//
SetFocus(GetDlgItem(hwnd, IDC_VERIFY_WARN_NO));
return FALSE;
case WM_DESTROY:
if (hIcon) {
DestroyIcon(hIcon);
}
break;
case WM_NOTIFY:
switch (((NMHDR FAR *)lParam)->code) {
case NM_RETURN:
case NM_CLICK:
switch (((LPNMHDR)lParam)->idFrom) {
case IDC_VERIFY_TESTING_LINK:
//
// NTRAID#NTBUG9-707966-2002/09/24-jasonc
// We need to change the URL to point to the Authenticode
// topic.
//
ShellExecute(hwnd,
TEXT("open"),
TEXT("HELPCTR.EXE"),
TEXT("HELPCTR.EXE -url hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Csysdm.chm%3A%3A/logo_testing.htm"),
NULL,
SW_SHOWNORMAL
);
break;
}
break;
}
break;
case WM_COMMAND:
switch(wParam) {
case IDC_VERIFY_WARN_NO:
case IDC_VERIFY_WARN_YES:
EndDialog(hwnd, (int)wParam);
break;
case IDC_VERIFY_WARN_MORE_INFO:
WinHelp(hwnd, TEXT("SECAUTH.HLP"), HELP_CONTEXT, IDH_SECAUTH_UNSIGNED);
break;
default:
break;
}
break;
default:
break;
}
return FALSE;
}
INT_PTR
CALLBACK
DriverBlockDlgProc(
IN HWND hwnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
)
/*++
Routine Description:
This is the dialog procedure for the driver blocking UI that is presented to
the user when a a driver that is about to be installed is found in the bad
driver database.
--*/
{
HICON hIcon = NULL;
LPTSTR pBuffer = NULL;
ULONG BufferSize;
static HAPPHELPINFOCONTEXT hAppHelpInfoContext = NULL;
static SDBENTRYINFO SdbEntryInfo;
PDRIVERBLOCK_PROMPT lpDriverBlockPrompt;
lpDriverBlockPrompt = (PDRIVERBLOCK_PROMPT)GetWindowLongPtr(hwnd, DWLP_USER);
switch(msg) {
case WM_INITDIALOG:
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
MessageBeep(MB_ICONASTERISK);
lpDriverBlockPrompt = (PDRIVERBLOCK_PROMPT)lParam;
hIcon = LoadIcon(MyDllModuleHandle,
MAKEINTRESOURCE(IDI_DRIVERBLOCK));
SendDlgItemMessage(hwnd, IDC_DRIVERBLOCK_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
hAppHelpInfoContext = SdbOpenApphelpInformation(&lpDriverBlockPrompt->entryinfo.guidDB,
&lpDriverBlockPrompt->entryinfo.guidID);
if ((hAppHelpInfoContext) &&
((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpAppName,
NULL,
0)) != 0) &&
(pBuffer = MyMalloc(BufferSize)) &&
((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpAppName,
pBuffer,
BufferSize)) != 0)) {
SetDlgItemText(hwnd, IDC_DRIVERBLOCK_APPNAME, pBuffer);
MyFree(pBuffer);
} else if (lpDriverBlockPrompt->lpszFile) {
SetDlgItemText(hwnd, IDC_DRIVERBLOCK_APPNAME, pSetupGetFileTitle(lpDriverBlockPrompt->lpszFile));
}
if ((hAppHelpInfoContext) &&
((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpDetails,
NULL,
0)) != 0) &&
(pBuffer = MyMalloc(BufferSize)) &&
((BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpDetails,
pBuffer,
BufferSize)) != 0)) {
SetDlgItemText(hwnd, IDC_DRIVERBLOCK_SUMMARY, pBuffer);
MyFree(pBuffer);
}
//
// Make sure this dialog is in the foreground (at least for this
// process).
//
SetForegroundWindow(hwnd);
return FALSE;
case WM_DESTROY:
if (hIcon) {
DestroyIcon(hIcon);
}
if (hAppHelpInfoContext) {
SdbCloseApphelpInformation(hAppHelpInfoContext);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDCANCEL:
EndDialog(hwnd, (int)wParam);
break;
case IDC_DRIVERBLOCK_DETAILS:
if (hAppHelpInfoContext) {
BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpHelpCenterURL,
NULL,
0);
if (BufferSize && (pBuffer = MyMalloc(BufferSize + (lstrlen(TEXT("HELPCTR.EXE -url ")) * sizeof(TCHAR))))) {
lstrcpy(pBuffer, TEXT("HELPCTR.EXE -url "));
BufferSize = SdbQueryApphelpInformation(hAppHelpInfoContext,
ApphelpHelpCenterURL,
(PVOID)&pBuffer[lstrlen(TEXT("HELPCTR.EXE -url "))],
BufferSize);
if (BufferSize) {
ShellExecute(hwnd,
TEXT("open"),
TEXT("HELPCTR.EXE"),
pBuffer,
NULL,
SW_SHOWNORMAL);
}
MyFree(pBuffer);
}
}
break;
default:
break;
}
break;
default:
break;
}
return FALSE;
}
DWORD
pGetInfOriginalNameAndCatalogFile(
IN PLOADED_INF Inf, OPTIONAL
IN LPCTSTR CurrentName, OPTIONAL
OUT PBOOL DifferentName, OPTIONAL
OUT LPTSTR OriginalName, OPTIONAL
IN DWORD OriginalNameSize,
OUT LPTSTR OriginalCatalogName, OPTIONAL
IN DWORD OriginalCatalogNameSize,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo OPTIONAL
)
/*++
Routine Description:
This routine determines whether a specified inf once had a different
original name, such as in the case where the Di stuff copied and renamed a
device inf.
(Information about an INF's original name comes from the PNF.)
This routine can also optionally return the original name of the catalog
file for this INF.
Arguments:
Inf - optionally, supplies a pointer to a LOADED_INF whose original name
and catalog file are to be queried. If this parameter isn't specified,
then CurrentName must be specified.
CurrentName - optionally, supplies the path to the INF whose original name
is to be queried. If Inf parameter is specified, this parameter is
ignored.
DifferentName - optionally, supplies the address of a boolean variable that,
upon successful return, is set to TRUE if the INF's current name is
different than its original name.
OriginalName - if this routine returns successfully, and the DifferentName
boolean was set to TRUE, then this optional buffer receives
the INF's original name, which _will not_ be the same as the current
name.
OriginalNameSize - supplies size of buffer (bytes for ansi, chars for
unicode) of OriginalName buffer, or zero if OriginalName is NULL.
OriginalCatalogName - optionally, supplies a buffer that receives the
original name of the catalog specified by this INF. If the catalog
doesn't specify a catalog file, this buffer will be set to an empty
string.
OriginalCatalogNameSize - supplies size, in characters, of
OriginalCatalogName buffer (zero if buffer not supplied).
AltPlatformInfo - optionally, supplies the address of a structure describing
the platform parameters that should be used in formulating the decorated
CatalogFile= entry to be used when searching for the INF's associated
catalog file.
Return Value:
If information is successfully retrieved from the INF, the return value is
NO_ERROR. Otherwise, it is a Win32 error code indicating the cause of
failure.
--*/
{
DWORD d;
HINF hInf = INVALID_HANDLE_VALUE;
MYASSERT((DifferentName && OriginalName && OriginalNameSize) ||
!(DifferentName || OriginalName || OriginalNameSize));
MYASSERT((OriginalCatalogName && OriginalCatalogNameSize) ||
!(OriginalCatalogName || OriginalCatalogNameSize));
MYASSERT(Inf || CurrentName);
if(DifferentName) {
*DifferentName = FALSE;
}
if(!Inf) {
//
// Open the INF.
//
hInf = SetupOpenInfFile(CurrentName,
NULL,
INF_STYLE_OLDNT | INF_STYLE_WIN4,
NULL
);
if(hInf == INVALID_HANDLE_VALUE) {
return GetLastError();
}
//
// We don't need to lock the INF because it'll never be accessible
// outside of this routine.
//
Inf = (PLOADED_INF)hInf;
}
//
// Enclose in try/except in case we hit an inpage error while using this
// memory-mapped image.
//
d = NO_ERROR;
try {
if(DifferentName) {
if(Inf->OriginalInfName) {
lstrcpyn(OriginalName, Inf->OriginalInfName, OriginalNameSize);
*DifferentName = TRUE;
}
}
if(OriginalCatalogName) {
if(!pSetupGetCatalogFileValue(&(Inf->VersionBlock),
OriginalCatalogName,
OriginalCatalogNameSize,
AltPlatformInfo)) {
//
// The INF didn't specify an associated catalog file
//
*OriginalCatalogName = TEXT('\0');
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If we hit an AV, then use invalid parameter error, otherwise, assume
// an inpage error when dealing with a mapped-in file.
//
d = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
if(hInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hInf);
}
return d;
}
PSECURITY_DESCRIPTOR
pSetupConvertTextToSD(
IN PCWSTR SDS,
OUT PULONG SecDescSize
)
/*++
Routine Description:
Helper for cfgmgr.lib
Obtains a binary security descriptor from an SDS
Resulting buffer must be free'd using LocalFree (not MyFree)
returns NULL if not supported and sets last error
Arguments:
SDS - string to obtain security descriptor from
SecDescSize - filled in with size of security descriptor
Return Value:
returns security descriptor (use LocalFree to release)
or NULL with GetLastError indicating error
--*/
{
SCESTATUS status;
PSECURITY_DESCRIPTOR pSD = NULL;
ULONG ulSDSize;
SECURITY_INFORMATION siSeInfo;
//
// If we're in "Disable SCE" mode on embedded, don't do security stuff...
//
if(GlobalSetupFlags & PSPGF_NO_SCE_EMBEDDED) {
SetLastError(ERROR_SCE_DISABLED);
return NULL;
}
try {
status = SceSvcConvertTextToSD((PWSTR)SDS,&pSD,&ulSDSize,&siSeInfo);
switch (status ) {
case SCESTATUS_SUCCESS:
MYASSERT(pSD);
MYASSERT(ulSDSize);
if (SecDescSize) {
*SecDescSize = ulSDSize;
}
SetLastError(NO_ERROR);
break;
case SCESTATUS_INVALID_PARAMETER:
SetLastError(ERROR_INVALID_PARAMETER);
pSD = NULL;
break;
case SCESTATUS_NOT_ENOUGH_RESOURCE:
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
pSD = NULL;
break;
case SCESTATUS_RECORD_NOT_FOUND:
default:
SetLastError(ERROR_INVALID_DATA);
pSD = NULL;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If we hit an AV, then use invalid parameter error, otherwise, assume
// an inpage error when dealing with a mapped-in file.
//
SetLastError(ERROR_INVALID_DATA);
pSD = NULL;
}
return pSD;
}
PWSTR
pSetupConvertSDToText(
IN PSECURITY_DESCRIPTOR SD,
OUT PULONG pSDSSize
)
/*++
Routine Description:
Helper for cfgmgr.lib
Obtains an SDS from a binary security descriptor
Resulting buffer must be free'd using LocalFree (not MyFree)
returns NULL if not supported and sets last error
Arguments:
SD - security descriptor to convert to a string
pSDSSize - return size of string
Return Value:
returns security descriptor string (use LocalFree to release)
or NULL with GetLastError indicating error
--*/
{
HINSTANCE Dll_Handle;
FARPROC SceFileProc;
SCESTATUS status;
DWORD LoadStatus;
SECURITY_INFORMATION securityInformation = 0;
PSID sid;
PACL acl;
BOOLEAN tmp,present;
LPWSTR SDS = NULL;
ULONG ulSSDSize;
SECURITY_INFORMATION siSeInfo;
//
// If we're in "Disable SCE" mode on embedded, don't do security stuff...
//
if(GlobalSetupFlags & PSPGF_NO_SCE_EMBEDDED) {
//
// Report an empty string
//
return LocalAlloc(LPTR, sizeof(WCHAR)); // LPTR zeroes out the char
}
try {
//
// find out what relevent information is in the descriptor
// up a securityInformation block to go with it.
//
status = RtlGetOwnerSecurityDescriptor(SD, &sid, &tmp);
if(NT_SUCCESS(status) && (sid != NULL)) {
securityInformation |= OWNER_SECURITY_INFORMATION;
}
status = RtlGetGroupSecurityDescriptor(SD, &sid, &tmp);
if(NT_SUCCESS(status) && (sid != NULL)) {
securityInformation |= GROUP_SECURITY_INFORMATION;
}
status = RtlGetSaclSecurityDescriptor(SD,
&present,
&acl,
&tmp);
if(NT_SUCCESS(status) && (present)) {
securityInformation |= SACL_SECURITY_INFORMATION;
}
status = RtlGetDaclSecurityDescriptor(SD,
&present,
&acl,
&tmp);
if(NT_SUCCESS(status) && (present)) {
securityInformation |= DACL_SECURITY_INFORMATION;
}
//
// now obtain an SDS
//
status = SceSvcConvertSDToText(SD,securityInformation,&SDS,&ulSSDSize);
switch (status ) {
case SCESTATUS_SUCCESS:
MYASSERT(SDS);
MYASSERT(ulSSDSize);
if(pSDSSize != NULL) {
*pSDSSize = ulSSDSize;
}
SetLastError(NO_ERROR);
break;
case SCESTATUS_INVALID_PARAMETER:
SetLastError(ERROR_INVALID_PARAMETER);
SDS = NULL;
break;
case SCESTATUS_NOT_ENOUGH_RESOURCE:
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
SDS = NULL;
break;
case SCESTATUS_RECORD_NOT_FOUND:
default:
SetLastError(ERROR_INVALID_DATA);
SDS = NULL;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
if (SDS) {
LocalFree(SDS);
}
SetLastError(ERROR_INVALID_DATA);
SDS = NULL;
}
return SDS;
}
DWORD
pSetupCallSCE(
IN DWORD Operation,
IN PCWSTR FullName,
IN PSP_FILE_QUEUE Queue,
IN PCWSTR String1,
IN DWORD Index1,
IN PSECURITY_DESCRIPTOR SecDesc OPTIONAL
)
/*
Operation ST_SCE_SET : - Sets security on a File in File Queue and informs SCE database
FullName : - Filename (Needed)
Queue : - Pointer to FileQueue (Needed)
Index : - Index in String Table of Queue (Needed)
Operation ST_SCE_RENAME : - Sets security on a File in File Queue and informs SCE database to
record it for the filename mentioned in String1
FullName : - Filename (Needed)
Queue : - Pointer to FileQueue (Needed)
String1 ; - Filename to record in Database (Needed)
Index : - Index in String Table of Queue (Optional - only if it needs to be set otherwise -1)
Operation ST_SCE_DELETE : - Removes record of file in SCE database
FullName : - Filename (Needed)
Operation ST_SCE_UNWIND : - Used for Backup Unwinds when we reset the security on a dirty file
FullName : - Filename (Needed)
SecDesc : - Pointer to Security Descriptor for the original file that we unwind (Needed)
Operation ST_SCE_SERVICES : - Sets security on a Service and informs SCE database
FullName : - Service Name (Needed)
Index : - Service Style (Needed)
String1 ; - Security Descriptor string
Operation ST_SCE_SDS_TO_BIN : - Sets security on a Service and informs SCE database
FullName : - Service Name (Needed)
Index : - Service Style (Needed)
String1 ; - Security Descriptor string
In each case, return value is error or NO_ERROR
*/
{
FARPROC SceFileProc;
PCWSTR SecurityDescriptor;
HINSTANCE Dll_Handle;
DWORD ret, LoadStatus;
//
// If we're in "Disable SCE" mode on embedded, don't do security stuff...
//
if(GlobalSetupFlags & PSPGF_NO_SCE_EMBEDDED) {
return NO_ERROR;
}
try {
switch (Operation) {
case ST_SCE_SET:
//Get the Security descriptor from the String table of the node
if( Index1 != -1 ){
SecurityDescriptor = pSetupStringTableStringFromId( Queue->StringTable, Index1 );
if(!SecurityDescriptor) {
ret= NO_ERROR;
break;
}
}
else {
ret = NO_ERROR;
break;
}
ret = SceSetupUpdateSecurityFile((PWSTR)FullName, 0, (PWSTR)SecurityDescriptor );
break;
case ST_SCE_RENAME:
if( Index1 != -1 ) {
SecurityDescriptor = pSetupStringTableStringFromId( Queue->StringTable, Index1 );
} else {
SecurityDescriptor = NULL;
}
ret = SceSetupMoveSecurityFile( (PWSTR)FullName, (PWSTR)String1, (PWSTR)SecurityDescriptor );
break;
case ST_SCE_DELETE:
ret = SceSetupMoveSecurityFile( (PWSTR)FullName, NULL, NULL );
break;
case ST_SCE_UNWIND:
ret = SceSetupUnwindSecurityFile( (PWSTR)FullName, SecDesc );
break;
case ST_SCE_SERVICES:
if( String1 == NULL ){
ret = NO_ERROR;
} else {
ret = SceSetupUpdateSecurityService( (PWSTR)FullName, Index1, (PWSTR)String1 );
}
break;
default:
MYASSERT(0);
ret = ERROR_INVALID_DATA;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
ret = ERROR_INVALID_DATA;
}
return ret;
}
VOID
RestoreBootReplacedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode
)
/*++
Routine Description:
This routine restores a file that was renamed in preparation for a bootfile
installation.
Arguments:
Queue - queue that contains the bootfile copy operation
QueueNode - bootfile copy operation being aborted
Return Value:
None.
--*/
{
DWORD rc;
LONG TargetID;
SP_TARGET_ENT TargetInfo;
PCTSTR TargetFilename, RenamedFilename;
BOOL UnPostSucceeded;
//
// First, we need to find the corresponding target info node so
// we can find out what temporary name our file was renamed to.
//
rc = pSetupBackupGetTargetByPath((HSPFILEQ)Queue,
NULL, // use Queue's string table
NULL,
QueueNode->TargetDirectory,
-1,
QueueNode->TargetFilename,
&TargetID,
&TargetInfo
);
if(rc == NO_ERROR) {
//
// Has the file previously been renamed (and not yet
// restored)?
//
if((TargetInfo.InternalFlags & (SP_TEFLG_MOVED | SP_TEFLG_RESTORED)) == SP_TEFLG_MOVED) {
TargetFilename = pSetupFormFullPath(
Queue->StringTable,
TargetInfo.TargetRoot,
TargetInfo.TargetSubDir,
TargetInfo.TargetFilename
);
MYASSERT(TargetFilename);
RenamedFilename = pSetupStringTableStringFromId(
Queue->StringTable,
TargetInfo.NewTargetFilename
);
MYASSERT(RenamedFilename);
//
// Move the renamed file back to its original name.
//
RestoreRenamedOrBackedUpFile(TargetFilename,
RenamedFilename,
TRUE,
Queue->LogContext
);
//
// Set the flag indicating that this file has been
// restored, and save this info.
//
TargetInfo.InternalFlags |= SP_TEFLG_RESTORED;
pSetupBackupSetTargetByID((HSPFILEQ)Queue, TargetID, &TargetInfo);
//
// Finally, get rid of the delayed-move node that was to
// delete the renamed file upon reboot.
//
UnPostSucceeded = UnPostDelayedMove(Queue,
RenamedFilename,
NULL
);
MYASSERT(UnPostSucceeded);
}
}
}
VOID
pSetupExemptFileFromProtection(
IN PCTSTR FileName,
IN DWORD FileChangeFlags,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
OUT PDWORD QueueNodeFlags OPTIONAL
)
/*++
Routine Description:
This routine checks to see if the specified file is a protected system
file, and if so, it tells SFC to make a replacement exception for this file.
Arguments:
FileName - Supplies the name of the file for which an exception is being
requested.
FileChangeFlags - Supplies the flags to be passed to SfcFileException, if
this file is determined to be under the protection of SFP.
LogContext - Optionally, supplies the log context to be used when logging
information resulting from this request.
QueueNodeFlags - Optionally, supplies the address of a variable that
receives one or more of the following queue node flags indicating
whether the specified file is a protected system file, and whether an
exception was granted for its replacement:
IQF_TARGET_PROTECTED - File is a protected system file.
IQF_ALLOW_UNSIGNED - An exception has been granted so that the file
may be replaced by an unsigned file.
Return Value:
None.
--*/
{
HANDLE hSfp;
PSETUP_LOG_CONTEXT lc = NULL;
DWORD Result = NO_ERROR;
if(QueueNodeFlags) {
*QueueNodeFlags = 0;
}
//
// If the caller didn't supply us with a LogContext, then create our own.
// We want to do this so that all log entries generated herein will end up
// in the same section.
//
if(!LogContext) {
if(CreateLogContext(NULL, TRUE, &lc) == NO_ERROR) {
//
// success
//
LogContext = lc;
} else {
lc = NULL;
}
}
if(IsFileProtected(FileName, LogContext, &hSfp)) {
if(QueueNodeFlags) {
*QueueNodeFlags = IQF_TARGET_PROTECTED;
}
Result = SfcFileException(hSfp,
(PWSTR)FileName,
FileChangeFlags
);
if(Result == NO_ERROR) {
WriteLogEntry(
LogContext,
SETUP_LOG_ERROR,
MSG_LOG_SFC_EXEMPT_SUCCESS,
NULL,
FileName);
if(QueueNodeFlags) {
*QueueNodeFlags |= IQF_ALLOW_UNSIGNED;
}
} else {
WriteLogEntry(
LogContext,
SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
MSG_LOG_SFC_EXEMPT_FAIL,
NULL,
FileName);
WriteLogError(
LogContext,
SETUP_LOG_ERROR,
Result);
}
SfcClose(hSfp);
//
// If we created our own local LogContext, we can free it now.
//
if(lc) {
DeleteLogContext(lc);
}
}
}
BOOL
pSetupProtectedRenamesFlag(
BOOL bSet
)
{
HKEY hKey;
long rslt = ERROR_SUCCESS;
if (OSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) {
return(TRUE);
}
rslt = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\Session Manager"),
0,
KEY_SET_VALUE,
&hKey);
if (rslt == ERROR_SUCCESS) {
DWORD Value = bSet ? 1 : 0;
rslt = RegSetValueEx(
hKey,
TEXT("AllowProtectedRenames"),
0,
REG_DWORD,
(LPBYTE)&Value,
sizeof(DWORD));
RegCloseKey(hKey);
if (rslt != ERROR_SUCCESS) {
DebugPrintEx( DPFLTR_ERROR_LEVEL, TEXT("couldn't RegSetValueEx, ec = %d\n"), rslt );
}
} else {
DebugPrintEx( DPFLTR_ERROR_LEVEL, TEXT("couldn't RegOpenKeyEx, ec = %d\n"), rslt );
}
return(rslt == ERROR_SUCCESS);
}
VOID
pSetupUninstallNewCatalogNodes(
IN PSP_FILE_QUEUE Queue,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
/*++
Routine Description:
This routine uninstalls any newly-copied INFs/PNFs/CATs contained in the
specified linked list of catalog nodes.
Arguments:
Queue - Supplies a pointer to the file queue (potentially) containing
newly-copied catalog nodes to be uninstalled.
Return Value:
None.
--*/
{
PSPQ_CATALOG_INFO CatalogNode;
PTSTR InfToUninstall;
BOOL Locked = FALSE;
try {
if(!_pSpUtilsStringTableLock(Queue->StringTable)) {
leave;
}
Locked = TRUE;
for(CatalogNode = Queue->CatalogList;
CatalogNode;
CatalogNode = CatalogNode->Next) {
if(CatalogNode->Flags & CATINFO_FLAG_NEWLY_COPIED) {
InfToUninstall = _pSpUtilsStringTableStringFromId(
Queue->StringTable,
CatalogNode->InfFinalPath
);
MYASSERT(InfToUninstall);
if(InfToUninstall) {
pSetupUninstallOEMInf(InfToUninstall, LogContext, SUOI_FORCEDELETE, NULL);
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Reference the following variable so the compiler will respect
// statement ordering w.r.t. its assignment.
//
Locked = Locked;
}
if(Locked) {
_pSpUtilsStringTableUnlock(Queue->StringTable);
}
}
BOOL
WINAPI
SetupUninstallNewlyCopiedInfs(
IN HSPFILEQ QueueHandle,
IN DWORD Flags,
IN PVOID Reserved
)
/*++
Routine Description:
This API uninstalls any INFs (and their associated PNFs and CATs) that
were previously installed during committal of the specified file queue.
Arguments:
QueueHandle - Supplies a handle to a committed file queue (potentially)
containing newly-copied INFs to be uninstalled.
Flags - Supplies flags that alter the behavior of this API. Presently, no
flags are defined. This parameter must be zero.
Reserved - Reserved for future use. This parameter must be NULL.
Return Value:
If all the parameters were valid, the return value is non-zero (TRUE). Note
that this does _not_ necessarily mean that any newly-copied INFs were
uninstalled.
If there was a problem with the parameters passed in, the return value is
FALSE, and GetLastError provides more information on the problem.
--*/
{
PSP_FILE_QUEUE Queue;
BOOL Success;
PSETUP_LOG_CONTEXT LogContext;
if(Flags) {
SetLastError(ERROR_INVALID_FLAGS);
return FALSE;
}
if(Reserved) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Queue handle is actually a pointer to the queue structure.
//
Queue = (PSP_FILE_QUEUE)QueueHandle;
//
// do a quick handle validation before anything else
//
try {
Success = ((Queue != NULL) && (Queue != INVALID_HANDLE_VALUE) && (Queue->Signature == SP_FILE_QUEUE_SIG));
if(Success) {
LogContext = Queue->LogContext;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
if(!Success) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
pSetupUninstallNewCatalogNodes(Queue, LogContext);
return TRUE;
}