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.
2414 lines
84 KiB
2414 lines
84 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
restore.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of file restoration code.
|
|
|
|
Author:
|
|
|
|
Andrew Ritz (andrewr) 30-Jul-1999
|
|
|
|
Revision History:
|
|
|
|
Andrew Ritz (andrewr) 30-Jul-1999 : moved code from fileio.c and validate.c
|
|
|
|
--*/
|
|
|
|
#include "sfcp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <dbt.h>
|
|
#include <initguid.h>
|
|
#include <devguid.h>
|
|
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
|
|
//
|
|
// DEVICE_CHANGE is a private stucture used to indicate how to check for a file
|
|
// (either from a device change notification or from the user clicking "retry"
|
|
// on the prompt dialog.
|
|
//
|
|
typedef struct _DEVICE_CHANGE {
|
|
DWORD Mask;
|
|
DWORD Flags;
|
|
} DEVICE_CHANGE, *PDEVICE_CHANGE;
|
|
|
|
|
|
DWORD
|
|
pSfcRestoreFromMediaWorkerThread(
|
|
IN PRESTORE_QUEUE RestoreQueue
|
|
);
|
|
|
|
DWORD
|
|
SfcGetCabTagFile(
|
|
IN PSOURCE_INFO psi,
|
|
OUT PWSTR* ppFile
|
|
);
|
|
|
|
PVOID
|
|
pSfcRegisterForDevChange(
|
|
HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine registers for PNP device notification messages so we know when the
|
|
user has inserted the CD-ROM.
|
|
|
|
Arguments:
|
|
|
|
hDlg - dialog to post device change notification to.
|
|
|
|
Return Value:
|
|
|
|
A device change handle for success, otherwise NULL. If this function
|
|
succeeds, the hDlg will receive WM_DEVICECHANGE notification messages
|
|
|
|
--*/
|
|
{
|
|
PVOID hNotifyDevNode;
|
|
DEV_BROADCAST_DEVICEINTERFACE FilterData;
|
|
|
|
ASSERT(IsWindow(hDlg));
|
|
|
|
//
|
|
// register for cdrom change notifications
|
|
//
|
|
FilterData.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
|
FilterData.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
FilterData.dbcc_classguid = GUID_DEVCLASS_CDROM;
|
|
|
|
hNotifyDevNode = RegisterDeviceNotification( hDlg, &FilterData, DEVICE_NOTIFY_WINDOW_HANDLE );
|
|
if (hNotifyDevNode == NULL) {
|
|
DebugPrint1( LVL_VERBOSE, L"RegisterDeviceNotification failed, ec=%d", GetLastError() );
|
|
}
|
|
|
|
return hNotifyDevNode;
|
|
}
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
pSfcPromptForMediaDialogProc(
|
|
HWND hwndDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine is the dialog procedure for prompting the user to put in media.
|
|
|
|
We use the same dialog procedure for IDD_SFC_NETWORK_PROMPT and
|
|
IDD_SFC_CD_PROMPT.
|
|
|
|
We register for a device notification so we know when the user puts the
|
|
media into the drive. So we don't even need an "OK" button in this dialog,
|
|
just a cancel dialog in case the user cannot find the media, etc.
|
|
|
|
Arguments:
|
|
|
|
standard dialog proc arguments
|
|
|
|
Return Value:
|
|
|
|
standard dialog proc return code
|
|
|
|
--*/
|
|
{
|
|
#define WM_TRYAGAIN (WM_APP + 1)
|
|
DEV_BROADCAST_VOLUME *dbv;
|
|
static UINT QueryCancelAutoPlay = 0;
|
|
static PVOID hNotifyDevNode = NULL;
|
|
static PPROMPT_INFO pi;
|
|
WCHAR buf1[128];
|
|
WCHAR buf2[128];
|
|
WCHAR conn[128];
|
|
PWSTR s;
|
|
PDEVICE_CHANGE DeviceChangeStruct;
|
|
DWORD Mask, Flags, i;
|
|
DWORD rcid;
|
|
static CancelId;
|
|
WCHAR Path[16];
|
|
WCHAR SourcePath[MAX_PATH];
|
|
static PSFC_WINDOW_DATA WindowData;
|
|
static bInModalLoop = FALSE;
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
pi = (PPROMPT_INFO) lParam;
|
|
ASSERT(NULL != pi);
|
|
|
|
//
|
|
// register for cdrom notification.
|
|
//
|
|
hNotifyDevNode = pSfcRegisterForDevChange( hwndDlg );
|
|
|
|
//
|
|
// try to turn off the autorun junk that the shell creates
|
|
//
|
|
QueryCancelAutoPlay = RegisterWindowMessage( L"QueryCancelAutoPlay" );
|
|
|
|
//
|
|
// center the dialog and try to put it in the user's face
|
|
//
|
|
CenterDialog( hwndDlg );
|
|
SetForegroundWindow( hwndDlg );
|
|
|
|
|
|
|
|
GetDlgItemText( hwndDlg, IDC_MEDIA_NAME, buf1, UnicodeChars(buf1) );
|
|
swprintf( buf2, buf1, pi->si->Description );
|
|
SetDlgItemText( hwndDlg, IDC_MEDIA_NAME, buf2 );
|
|
|
|
//
|
|
// if we're a network connection, put in the actual source path.
|
|
//
|
|
if (pi->NetPrompt) {
|
|
|
|
ASSERT( pi->SourcePath != NULL );
|
|
|
|
GetDlgItemText( hwndDlg, IDC_NET_NAME, buf1, UnicodeChars(buf1) );
|
|
|
|
if(!SfcGetConnectionName( pi->SourcePath, conn, UnicodeChars(conn), NULL, 0, FALSE, NULL )) {
|
|
conn[0] = 0;
|
|
}
|
|
|
|
(void) StringCchPrintf( buf2, UnicodeChars(buf2), buf1, conn );
|
|
SetDlgItemText( hwndDlg, IDC_NET_NAME, buf2 );
|
|
} else {
|
|
NOTHING;
|
|
//HideWindow( GetDlgItem( hwndDlg, IDC_RETRY ) );
|
|
//HideWindow( GetDlgItem( hwndDlg, IDC_INFO ) );
|
|
//SetFocus( GetDlgItem( hwndDlg, IDCANCEL ) );
|
|
}
|
|
|
|
//
|
|
// set the appropriate text based on what sort of prompt we are for
|
|
//
|
|
if (pi->Flags & PI_FLAG_COPY_TO_CACHE) {
|
|
rcid = IDS_CACHE_TEXT;
|
|
CancelId = IDS_CANCEL_CONFIRM_CACHE;
|
|
} else if (pi->Flags & PI_FLAG_INSTALL_PROTECTED) {
|
|
rcid = IDS_INSTALL_PROTECTED_TEXT;
|
|
CancelId = IDS_CANCEL_CONFIRM_INSTALL;
|
|
} else {
|
|
ASSERT(pi->Flags & PI_FLAG_RESTORE_FILE);
|
|
rcid = IDS_RESTORE_TEXT;
|
|
CancelId = IDS_CANCEL_CONFIRM;
|
|
}
|
|
|
|
LoadString(SfcInstanceHandle,rcid,SourcePath,UnicodeChars(SourcePath));
|
|
SetDlgItemText( hwndDlg, IDC_PROMPT_TEXT, SourcePath );
|
|
|
|
|
|
//
|
|
// remember our window handle so we can close it if we have to.
|
|
//
|
|
WindowData = pSfcCreateWindowDataEntry( hwndDlg );
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_RETRY:
|
|
PostMessage(hwndDlg, WM_TRYAGAIN, 0, (LPARAM)NULL);
|
|
break;
|
|
case IDC_INFO:
|
|
bInModalLoop = TRUE;
|
|
|
|
MyMessageBox(
|
|
NULL,
|
|
pi->NetPrompt ? IDS_MORE_INFORMATION_NET : IDS_MORE_INFORMATION_CD,
|
|
MB_ICONINFORMATION | MB_SERVICE_NOTIFICATION);
|
|
|
|
bInModalLoop = FALSE;
|
|
break;
|
|
case IDCANCEL:
|
|
|
|
//
|
|
// the user clicked cancel. Ask them if they really mean it and exit
|
|
//
|
|
ASSERT(CancelId != 0);
|
|
bInModalLoop = TRUE;
|
|
|
|
if (MyMessageBox(
|
|
hwndDlg,
|
|
CancelId,
|
|
MB_APPLMODAL | MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING ) == IDYES) {
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 0 );
|
|
}
|
|
|
|
bInModalLoop = FALSE;
|
|
break;
|
|
default:
|
|
NOTHING;
|
|
}
|
|
break;
|
|
case WM_WFPENDDIALOG:
|
|
DebugPrint(
|
|
LVL_VERBOSE,
|
|
L"Received WM_WFPENDDIALOG message, terminating dialog" );
|
|
|
|
|
|
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, ERROR_SUCCESS );
|
|
|
|
//
|
|
// returning 2 indicates that we are being force-terminated so we
|
|
// don't need to bother removing our SFC_WINDOW_DATA member
|
|
//
|
|
EndDialog( hwndDlg, 2 );
|
|
|
|
|
|
break;
|
|
case WM_TRYAGAIN:
|
|
DeviceChangeStruct = (PDEVICE_CHANGE) lParam;
|
|
if (DeviceChangeStruct) {
|
|
Mask = DeviceChangeStruct->Mask;
|
|
Flags = DeviceChangeStruct->Flags;
|
|
MemFree(DeviceChangeStruct);
|
|
DeviceChangeStruct = NULL;
|
|
} else {
|
|
Flags = DBTF_MEDIA;
|
|
Mask = (DWORD)-1;
|
|
}
|
|
|
|
if (pi->NetPrompt) {
|
|
EstablishConnection( hwndDlg, pi->SourcePath, !SFCNoPopUps );
|
|
if (TAGFILE(pi->si)) {
|
|
s = wcsrchr( TAGFILE(pi->si), L'.' );
|
|
if (s && _wcsicmp( s, L".cab" ) == 0) {
|
|
//
|
|
// yep, the tagfile is a cabfile
|
|
// look for that file on disk
|
|
// and if it is, use it.
|
|
//
|
|
|
|
BuildPathForFile(
|
|
pi->SourcePath,
|
|
pi->si->SourcePath,
|
|
TAGFILE(pi->si),
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// try without the subdir
|
|
//
|
|
|
|
BuildPathForFile(
|
|
pi->SourcePath,
|
|
pi->si->SourcePath,
|
|
TAGFILE(pi->si),
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
(!SFC_INCLUDE_ARCHSUBDIR),
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: cab file is missing from cd, [%ws]", SourcePath );
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE,
|
|
L"pSfcPromptForMediaDialogProc: the tag file [%ws] is not a cab file",
|
|
TAGFILE(pi->si) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// no cab file. look for the actual
|
|
// file on the media
|
|
//
|
|
|
|
BuildPathForFile(
|
|
pi->SourcePath,
|
|
pi->si->SourcePath,
|
|
pi->SourceFileName,
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// try again without the subdir
|
|
//
|
|
|
|
BuildPathForFile(
|
|
pi->SourcePath,
|
|
pi->si->SourcePath,
|
|
pi->SourceFileName,
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
(!SFC_INCLUDE_ARCHSUBDIR),
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
Path[0] = L'?';
|
|
Path[1] = L':';
|
|
Path[2] = L'\\';
|
|
Path[3] = 0;
|
|
Path[4] = 0;
|
|
|
|
//
|
|
// cycle through all drive letters A-Z looking for the file
|
|
//
|
|
for (i=0; i<26; i++) {
|
|
if (Mask&1) {
|
|
Path[0] = (WCHAR)(L'A' + i);
|
|
Path[3] = 0;
|
|
//
|
|
// is media present in the CD-ROM?
|
|
//
|
|
if (Flags == DBTF_MEDIA) {
|
|
if (GetDriveType( Path ) == DRIVE_CDROM) {
|
|
//
|
|
// look for the tag file so we're sure that the cd that
|
|
// was inserted is the correct one
|
|
//
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: found cdrom drive on [%ws]", Path );
|
|
if (TAGFILE(pi->si)) {
|
|
wcscpy( SourcePath, Path );
|
|
s = wcsrchr( TAGFILE(pi->si), L'.' );
|
|
if (s && _wcsicmp( s, L".cab" ) == 0) {
|
|
PWSTR szTagfile;
|
|
//
|
|
// get the cab's tagfile
|
|
//
|
|
if (SfcGetCabTagFile(pi->si, &szTagfile) == ERROR_SUCCESS) {
|
|
pSetupConcatenatePaths( SourcePath, szTagfile, UnicodeChars(SourcePath), NULL );
|
|
MemFree(szTagfile);
|
|
} else {
|
|
DebugPrint1(LVL_VERBOSE, L"SfcGetCabTagFile failed with error %d", GetLastError());
|
|
}
|
|
|
|
}else{
|
|
pSetupConcatenatePaths( SourcePath, TAGFILE(pi->si),UnicodeChars(SourcePath), NULL );
|
|
|
|
}
|
|
if (GetFileAttributes( SourcePath ) != (DWORD)-1) {
|
|
//
|
|
// the user has the correct cd inserted
|
|
// so now look to see if the file is on
|
|
// the cd
|
|
//
|
|
|
|
|
|
//
|
|
// first we have to look for the tagfile
|
|
// for the actual file because the tag-
|
|
// file may actually be a cabfile that
|
|
// the file is embedded in
|
|
//
|
|
if (TAGFILE(pi->si)) {
|
|
s = wcsrchr( TAGFILE(pi->si), L'.' );
|
|
if (s && _wcsicmp( s, L".cab" ) == 0) {
|
|
//
|
|
// yep, the tagfile is a cabfile
|
|
// look for that file on disk
|
|
// and if it is, use it.
|
|
//
|
|
BuildPathForFile(
|
|
Path,
|
|
pi->si->SourcePath,
|
|
TAGFILE(pi->si),
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: cab file is missing from cd, [%ws]", SourcePath );
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE,
|
|
L"pSfcPromptForMediaDialogProc: the tag file [%ws] is not a cab file",
|
|
TAGFILE(pi->si) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// no cab file. look for the actual
|
|
// file on the media
|
|
//
|
|
BuildPathForFile(
|
|
Path,
|
|
pi->si->SourcePath,
|
|
pi->SourceFileName,
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: source file is missing [%ws]", SourcePath );
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: media tag file [%ws] is missing, wrong CD", SourcePath );
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: could not get source information for layout.inf, ec=%d", GetLastError() );
|
|
}
|
|
}
|
|
} else if (Flags == DBTF_NET) {
|
|
//
|
|
// network share has changed... get the UNC
|
|
// pathname and check for the file
|
|
//
|
|
if (SfcGetConnectionName( Path, SourcePath, UnicodeChars(SourcePath), NULL, 0, FALSE, NULL)) {
|
|
|
|
|
|
//
|
|
// first we have to look for the tagfile
|
|
// for the actual file because the tag-
|
|
// file may actually be a cabfile that
|
|
// the file is embedded in
|
|
//
|
|
if (TAGFILE(pi->si)) {
|
|
s = wcsrchr( TAGFILE(pi->si), L'.' );
|
|
if (s && _wcsicmp( s, L".cab" ) == 0) {
|
|
//
|
|
// yep, the tagfile is a cabfile
|
|
// look for that file on disk
|
|
// and if it is, use it.
|
|
//
|
|
BuildPathForFile(
|
|
Path,
|
|
pi->si->SourcePath,
|
|
TAGFILE(pi->si),
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
return FALSE;
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: cab file is missing from cd, [%ws]", SourcePath );
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_VERBOSE,
|
|
L"pSfcPromptForMediaDialogProc: the tag file [%ws] is not a cab file",
|
|
TAGFILE(pi->si) );
|
|
}
|
|
}
|
|
|
|
BuildPathForFile(
|
|
SourcePath,
|
|
pi->si->SourcePath,
|
|
pi->SourceFileName,
|
|
SFC_INCLUDE_SUBDIRECTORY,
|
|
SFC_INCLUDE_ARCHSUBDIR,
|
|
SourcePath,
|
|
UnicodeChars(SourcePath) );
|
|
|
|
if (SfcIsFileOnMedia( SourcePath )) {
|
|
s = wcsrchr( SourcePath, L'\\' );
|
|
*s = L'\0';
|
|
wcscpy( pi->NewPath, SourcePath );
|
|
UnregisterDeviceNotification( hNotifyDevNode );
|
|
pSfcRemoveWindowDataEntry( WindowData );
|
|
EndDialog( hwndDlg, 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Mask = Mask >> 1;
|
|
}
|
|
|
|
//
|
|
// ok user made a mistake
|
|
// he put in a cd but it is either the wrong cd
|
|
// or it is damaged/corrupted.
|
|
//
|
|
bInModalLoop = TRUE;
|
|
|
|
MyMessageBox(
|
|
hwndDlg,
|
|
pi->NetPrompt
|
|
? IDS_WRONG_NETCD
|
|
: IDS_WRONG_CD,
|
|
MB_OK,
|
|
pi->si->Description );
|
|
|
|
bInModalLoop = FALSE;
|
|
|
|
//
|
|
// Received a volume change notification but we didn't find what
|
|
// we're looking for.
|
|
//
|
|
DebugPrint( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: didn't find file" );
|
|
|
|
break;
|
|
case WM_DEVICECHANGE:
|
|
//
|
|
// Don't process this while in a modal loop (i.e. displaying a message box)
|
|
//
|
|
if(bInModalLoop) {
|
|
break;
|
|
}
|
|
|
|
if (wParam == DBT_DEVICEARRIVAL) {
|
|
dbv = (DEV_BROADCAST_VOLUME*)lParam;
|
|
if (dbv->dbcv_devicetype == DBT_DEVTYP_VOLUME) {
|
|
//
|
|
// only care about volume type change notifications
|
|
//
|
|
|
|
DebugPrint( LVL_VERBOSE, L"pSfcPromptForMediaDialogProc: received a volume change notification" );
|
|
|
|
DeviceChangeStruct = MemAlloc( sizeof( DEVICE_CHANGE ) );
|
|
if (DeviceChangeStruct) {
|
|
DeviceChangeStruct->Mask = dbv->dbcv_unitmask;
|
|
DeviceChangeStruct->Flags = dbv->dbcv_flags;
|
|
if (!PostMessage(hwndDlg, WM_TRYAGAIN, 0, (LPARAM)DeviceChangeStruct)) {
|
|
DebugPrint1( LVL_MINIMAL ,
|
|
L"pSfcPromptForMediaDialogProc: PostMessage failed, ec = 0x%0xd",
|
|
GetLastError() );
|
|
MemFree(DeviceChangeStruct);
|
|
}
|
|
} else {
|
|
PostMessage(hwndDlg, WM_TRYAGAIN, 0, (LPARAM)NULL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (uMsg == QueryCancelAutoPlay) {
|
|
//
|
|
// disable autorun because it confuses the user
|
|
//
|
|
SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, 1 );
|
|
return 1;
|
|
}
|
|
} // end switch
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
UINT
|
|
SfcQueueCallback(
|
|
IN PFILE_COPY_INFO fci,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine is a setupapi queue callback routine. We override some of the
|
|
setupapi functions because we want to provide our own UI (or rather
|
|
disallow the setupapi UI).
|
|
|
|
Arguments:
|
|
|
|
fci - our context structure which setupapi passes to us for each
|
|
callback
|
|
Notification - SPFILENOTIFY_* code
|
|
Param1 - depends on notification
|
|
Param2 - depends on notification
|
|
|
|
Return Value:
|
|
|
|
depends on notification.
|
|
|
|
--*/
|
|
{
|
|
PSOURCE_MEDIA sm = (PSOURCE_MEDIA)Param1;
|
|
WCHAR fname[MAX_PATH*2];
|
|
WCHAR buf[MAX_PATH];
|
|
DWORD rVal;
|
|
INT_PTR rv;
|
|
DWORD RcId;
|
|
PFILEPATHS fp;
|
|
PFILEINSTALL_STATUS cs;
|
|
NTSTATUS Status;
|
|
HANDLE FileHandle;
|
|
PNAME_NODE Node;
|
|
PSFC_REGISTRY_VALUE RegVal;
|
|
DWORD FileSizeHigh;
|
|
DWORD FileSizeLow;
|
|
DWORD PathType;
|
|
PROMPT_INFO pi;
|
|
PSOURCE_INFO SourceInfo;
|
|
PVALIDATION_REQUEST_DATA vrd;
|
|
HCATADMIN hCatAdmin;
|
|
|
|
|
|
switch (Notification) {
|
|
case SPFILENOTIFY_ENDQUEUE:
|
|
//
|
|
// We might have impersonated the logged-on user in EstablishConnection during SPFILENOTIFY_NEEDMEDIA
|
|
//
|
|
RevertToSelf();
|
|
break;
|
|
|
|
//
|
|
// we had a copy error, record this and move onto the next file.
|
|
//
|
|
case SPFILENOTIFY_COPYERROR:
|
|
fp = (PFILEPATHS)Param1;
|
|
DebugPrint2(
|
|
LVL_MINIMAL,
|
|
L"Failed to copy file %ws, ec = 0x%08x...",
|
|
fp->Target,
|
|
fp->Win32Error );
|
|
//
|
|
// fall through
|
|
//
|
|
case SPFILENOTIFY_ENDCOPY:
|
|
//
|
|
// end copy means the file copy just completed
|
|
//
|
|
|
|
fp = (PFILEPATHS)Param1;
|
|
|
|
DebugPrint3( LVL_VERBOSE,
|
|
L"SfcQueueCallback: copy file %ws --> %ws, ec = 0x%08x",
|
|
fp->Source,
|
|
fp->Target,
|
|
fp->Win32Error );
|
|
|
|
//
|
|
// if the copy succeeded, clear any read-only or hidden attributes
|
|
// that may have been set by copying off of a cd, etc.
|
|
//
|
|
if (fp->Win32Error == ERROR_SUCCESS) {
|
|
SetFileAttributes( fp->Target,
|
|
GetFileAttributes(fp->Target) & (~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN)) );
|
|
}
|
|
|
|
//
|
|
// in the case that we're copying files due to an InstallProtectedFiles
|
|
// call, cs will be initialized, and we can loop throught the list of
|
|
// files, updating a status structure for each of these files
|
|
//
|
|
cs = fci->CopyStatus;
|
|
while (cs && cs->FileName) {
|
|
//
|
|
// cycle through the list of files we want to be copied
|
|
// and if the file was copied successfully, then get the
|
|
// filesize so we can post it to the caller's dialog
|
|
//
|
|
// also remember the version for returning to the caller
|
|
//
|
|
if ( (_wcsicmp(cs->FileName,fp->Target) == 0)
|
|
&& cs->Win32Error == ERROR_SUCCESS) {
|
|
cs->Win32Error = fp->Win32Error;
|
|
if (cs->Win32Error == ERROR_SUCCESS) {
|
|
Node = SfcFindProtectedFile( cs->FileName, UnicodeLen(cs->FileName) );
|
|
if (Node) {
|
|
RegVal = (PSFC_REGISTRY_VALUE)Node->Context;
|
|
Status = SfcOpenFile( &RegVal->FileName, RegVal->DirHandle, SHARE_ALL, &FileHandle );
|
|
if (NT_SUCCESS(Status)) {
|
|
if (fci->hWnd) {
|
|
FileSizeLow = GetFileSize( FileHandle, &FileSizeHigh );
|
|
PostMessage( fci->hWnd, WM_SFC_NOTIFY, (WPARAM)FileSizeLow, (LPARAM)FileSizeHigh );
|
|
}
|
|
SfcGetFileVersion( FileHandle, &cs->Version, NULL, fname );
|
|
NtClose( FileHandle );
|
|
}
|
|
}
|
|
} else {
|
|
DebugPrint2( LVL_MINIMAL, L"Failed to copy file %ws, ec = 0x%08x", fp->Target, fp->Win32Error );
|
|
}
|
|
break;
|
|
}
|
|
cs += 1;
|
|
}
|
|
|
|
if (fci->si) {
|
|
vrd = pSfcGetValidationRequestFromFilePaths( fci->si, fci->FileCount, fp );
|
|
if (vrd && vrd->Win32Error == ERROR_SUCCESS) {
|
|
vrd->Win32Error = fp->Win32Error;
|
|
if (fp->Win32Error == ERROR_SUCCESS) {
|
|
vrd->CopyCompleted = TRUE;
|
|
|
|
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverVerifyGuid, 0)) {
|
|
DebugPrint1( LVL_MINIMAL, L"CCAAC() failed, ec=%d", GetLastError() );
|
|
goto next;
|
|
}
|
|
|
|
//
|
|
// make sure the file is now valid
|
|
//
|
|
if (!SfcGetValidationData( &vrd->RegVal->FileName,
|
|
&vrd->RegVal->FullPathName,
|
|
vrd->RegVal->DirHandle,
|
|
hCatAdmin,
|
|
&vrd->ImageValData.New )) {
|
|
DebugPrint1( LVL_MINIMAL, L"SfcGetValidationData() failed, ec=%d", GetLastError() );
|
|
goto next;
|
|
}
|
|
|
|
if (vrd->ImageValData.New.SignatureValid == FALSE) {
|
|
vrd->ImageValData.New.DllVersion = 0;
|
|
} else {
|
|
//
|
|
// Cause a validation request. This should get us
|
|
// to sync the file in the cache.
|
|
//
|
|
SfcQueueValidationRequest(vrd->RegVal, SFC_ACTION_MODIFIED );
|
|
}
|
|
|
|
CryptCATAdminReleaseContext(hCatAdmin,0);
|
|
|
|
vrd->ImageValData.EventLog = MSG_DLL_CHANGE;
|
|
} else {
|
|
vrd->ImageValData.EventLog = MSG_RESTORE_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (Notification == SPFILENOTIFY_COPYERROR)
|
|
? FILEOP_SKIP
|
|
: FILEOP_DOIT;
|
|
break;
|
|
|
|
//
|
|
// This means that we're copying something from a new piece of media.
|
|
// Before we mess around with putting up a prompt for the file, let's just
|
|
// check for the file at the specified location and if it's there, we
|
|
// assume that the media is already present and we should just use it.
|
|
//
|
|
case SPFILENOTIFY_NEEDMEDIA:
|
|
|
|
DebugPrint3( LVL_MINIMAL, L"SfcQueueCallback: %ws - %ws, %ws", sm->SourcePath, sm->SourceFile, sm->Tagfile );
|
|
wcscpy( fname, sm->SourcePath );
|
|
pSetupConcatenatePaths( fname, sm->SourceFile, UnicodeChars(fname), NULL );
|
|
|
|
SourceInfo = pSfcGetSourceInfoFromSourceName( fci->si, fci->FileCount, sm );
|
|
ASSERT(ShuttingDown ? SourceInfo != NULL : TRUE);
|
|
|
|
//
|
|
// if we're in the process of shutting down, then abort the queue.
|
|
//
|
|
if (ShuttingDown) {
|
|
return(FILEOP_ABORT);
|
|
}
|
|
|
|
//
|
|
// if we didn't find the SOURCE_INFO for this file, we can't go on
|
|
// since we need that information to know where the proper location
|
|
// to retrieve the file is. we do make one last-ditch effort to
|
|
// see if the file is just where we said it would be earlier, however.
|
|
if (!SourceInfo) {
|
|
if (SfcIsFileOnMedia( fname )) {
|
|
return FILEOP_DOIT;
|
|
}
|
|
|
|
SetLastError(ERROR_CANCELLED);
|
|
return (FILEOP_ABORT);
|
|
}
|
|
|
|
//
|
|
// if this is a network share we try to establish a connection
|
|
// to the server before looking for the file. this may bring up
|
|
// UI
|
|
//
|
|
|
|
PathType = SfcGetPathType( (PWSTR)sm->SourcePath, buf,UnicodeChars(buf) );
|
|
if (PathType == PATH_NETWORK || PathType == PATH_UNC) {
|
|
EstablishConnection( NULL, sm->SourcePath, (fci->AllowUI && !SFCNoPopUps) );
|
|
}
|
|
|
|
rVal = SfcQueueLookForFile( sm, SourceInfo, fname, (PWSTR)Param2 );
|
|
if (SFCNoPopUps) {
|
|
if (rVal == FILEOP_ABORT) {
|
|
//
|
|
// media is necessary to copy the files but the user has
|
|
// configured wfp to not put up any ui. we make this look
|
|
// like a cancel
|
|
//
|
|
SetLastError(ERROR_CANCELLED);
|
|
}
|
|
|
|
return (rVal);
|
|
}
|
|
|
|
if (rVal != FILEOP_ABORT) {
|
|
//
|
|
// we have found the file so start copying
|
|
//
|
|
return (rVal);
|
|
}
|
|
|
|
//
|
|
// if we're not supposed to put up any dialogs, just abort copying
|
|
// this media and goto the next media.
|
|
//
|
|
// Note: it would be good to skip instead of aborting, since
|
|
// there might some set of files which we can restore from another
|
|
// media. In order to do this we really need to be able to know
|
|
// what files are on this media and set an error code for these
|
|
// files so that we know they weren't copied.
|
|
//
|
|
if (!fci->AllowUI) {
|
|
return (FILEOP_ABORT);
|
|
}
|
|
|
|
//
|
|
// otherwise let's just record that we're putting up media
|
|
// and then do just that.
|
|
//
|
|
fci->UIShown = TRUE;
|
|
//
|
|
// Note: make sure not to use this source media structure after the
|
|
// media has changed
|
|
//
|
|
switch (PathType) {
|
|
case PATH_LOCAL:
|
|
RcId = IDD_SFC_CD_PROMPT;
|
|
break;
|
|
|
|
case PATH_NETWORK:
|
|
case PATH_UNC:
|
|
RcId = IDD_SFC_NETWORK_PROMPT;
|
|
break;
|
|
|
|
case PATH_CDROM:
|
|
RcId = IDD_SFC_CD_PROMPT;
|
|
break;
|
|
|
|
default:
|
|
RcId = 0;
|
|
ASSERT( FALSE && "Unexpected PathType" );
|
|
break;
|
|
}
|
|
|
|
ASSERT((sm->SourceFile) && (sm->SourcePath) && (RcId != 0) );
|
|
pi.si = SourceInfo;
|
|
pi.SourceFileName = (PWSTR)sm->SourceFile;
|
|
pi.NewPath = buf;
|
|
pi.SourcePath = (PWSTR)sm->SourcePath;
|
|
pi.NetPrompt = (RcId == IDD_SFC_NETWORK_PROMPT);
|
|
pi.Flags = fci->Flags;
|
|
rv = MyDialogBoxParam(
|
|
RcId,
|
|
pSfcPromptForMediaDialogProc,
|
|
(LPARAM)&pi
|
|
);
|
|
if (rv == 1) {
|
|
//
|
|
// we're done. if we got a new path, pass that back to
|
|
// setup API else just copy the file from the current
|
|
// location
|
|
//
|
|
if (_wcsicmp( pi.NewPath, sm->SourcePath )) {
|
|
wcscpy( (PWSTR)Param2, pi.NewPath );
|
|
return ( FILEOP_NEWPATH );
|
|
}
|
|
return FILEOP_DOIT;
|
|
} else if (rv == 2) {
|
|
//
|
|
// we were forcefully aborted by receiving WM_WFPENDDIALOG
|
|
//
|
|
return FILEOP_ABORT;
|
|
} else {
|
|
ASSERT(rv == 0);
|
|
|
|
SetLastError(ERROR_CANCELLED);
|
|
return FILEOP_ABORT;
|
|
}
|
|
|
|
ASSERT(FALSE && "should not get here");
|
|
|
|
break;
|
|
|
|
default:
|
|
NOTHING;
|
|
}
|
|
|
|
next:
|
|
//
|
|
// just to the default for the rest of the callbacks
|
|
//
|
|
return SetupDefaultQueueCallback( fci->MsgHandlerContext, Notification, Param1, Param2 );
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcAddFileToQueue(
|
|
IN const HSPFILEQ hFileQ,
|
|
IN PCWSTR FileName,
|
|
IN PCWSTR TargetFileName,
|
|
IN PCWSTR TargetDirectory,
|
|
IN PCWSTR SourceFileName, OPTIONAL
|
|
IN PCWSTR SourceRootPath, OPTIONAL
|
|
IN PCWSTR InfName,
|
|
IN BOOL ExcepPackFile,
|
|
IN OUT PSOURCE_INFO SourceInfo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine adds the specified file to a file queue for copying.
|
|
|
|
Arguments:
|
|
|
|
hFileQ - contains a file queue handle that we are inserting this
|
|
copy node into
|
|
FileName - specifies the filename to be copied
|
|
TargetFileName - target filename
|
|
TargetDirectory - target destination directory
|
|
SourceFileName - source filename if it's different than the target
|
|
filename. if this is NULL, we assume the source filename
|
|
is the same as the target
|
|
SourceRootPath - the root path where we can find this file
|
|
InfName - the layout inf name
|
|
SourceInfo - SOURCE_INFO structure which gets set with additional
|
|
information about the file (like relative source
|
|
path, etc.) If this is supplied, it is assumed that
|
|
the structure was already initialized with a call to
|
|
SfcGetSourceInformation
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file was successfully added to the file queue.
|
|
|
|
--*/
|
|
{
|
|
BOOL b = FALSE;
|
|
SOURCE_INFO sibuf;
|
|
SP_FILE_COPY_PARAMS fcp;
|
|
|
|
RtlZeroMemory(&fcp, sizeof(fcp));
|
|
fcp.cbSize = sizeof(fcp);
|
|
fcp.LayoutInf = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// get the source information
|
|
//
|
|
if (SourceInfo == NULL) {
|
|
SourceInfo = &sibuf;
|
|
ZeroMemory( SourceInfo, sizeof(SOURCE_INFO) );
|
|
if (!SfcGetSourceInformation( SourceFileName == NULL ? FileName : SourceFileName, InfName, ExcepPackFile, SourceInfo )) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
ASSERT(SourceInfo != NULL);
|
|
|
|
//
|
|
// Open layout.inf
|
|
//
|
|
fcp.LayoutInf = SfcOpenInf(NULL, FALSE);
|
|
|
|
if(INVALID_HANDLE_VALUE == fcp.LayoutInf) {
|
|
goto exit;
|
|
}
|
|
|
|
fcp.QueueHandle = hFileQ;
|
|
fcp.SourceRootPath = SourceRootPath ? SourceRootPath : SourceInfo->SourceRootPath;
|
|
fcp.SourcePath = SourceInfo->SourcePath;
|
|
fcp.SourceFilename = SourceFileName == NULL ? FileName : SourceFileName;
|
|
fcp.SourceDescription = SourceInfo->Description;
|
|
fcp.SourceTagfile = TAGFILE(SourceInfo);
|
|
fcp.TargetDirectory = TargetDirectory;
|
|
fcp.TargetFilename = TargetFileName;
|
|
fcp.CopyStyle = SP_COPY_REPLACE_BOOT_FILE | PSP_COPY_USE_SPCACHE;
|
|
|
|
//
|
|
// add the file to the file queue
|
|
//
|
|
b = SetupQueueCopyIndirect(&fcp);
|
|
|
|
if (!b) {
|
|
DebugPrint1( LVL_VERBOSE, L"SetupQueueCopy failed, ec=%d", GetLastError() );
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
//
|
|
// cleanup and exit
|
|
//
|
|
if(fcp.LayoutInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(fcp.LayoutInf);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
SfcRestoreFileFromInstallMedia(
|
|
IN PVALIDATION_REQUEST_DATA vrd,
|
|
IN PCWSTR FileName,
|
|
IN PCWSTR TargetFileName,
|
|
IN PCWSTR TargetDirectory,
|
|
IN PCWSTR SourceFileName,
|
|
IN PCWSTR InfName,
|
|
IN BOOL ExcepPackFile,
|
|
IN BOOL TargetIsCache,
|
|
IN BOOL AllowUI,
|
|
OUT PDWORD UIShown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine restores the file specified from media. This routine only
|
|
handles one file at a time, and it is only used when populating the
|
|
DLLCache.
|
|
|
|
Arguments:
|
|
|
|
vrd
|
|
FileName
|
|
TargetFileName
|
|
TargetDirectory
|
|
SourceFileName
|
|
InfName
|
|
AllowUI
|
|
UIShown
|
|
|
|
Return Value:
|
|
|
|
If TRUE, the file was successfully restored from media.
|
|
|
|
--*/
|
|
{
|
|
HSPFILEQ hFileQ = INVALID_HANDLE_VALUE;
|
|
PVOID MsgHandlerContext = NULL;
|
|
BOOL b = FALSE;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
struct _info
|
|
{
|
|
FILE_COPY_INFO fci;
|
|
SOURCE_INFO si;
|
|
|
|
}* pinfo = NULL;
|
|
|
|
PSOURCE_INFO psi;
|
|
|
|
|
|
|
|
ASSERT(FileName != NULL);
|
|
|
|
//
|
|
// allocate SOURCE_INFO and FILE_COPY_INFO in the heap to minimize stack use
|
|
// note that the memory is zeroed by MemAlloc
|
|
//
|
|
|
|
pinfo = (struct _info*) MemAlloc(sizeof(*pinfo));
|
|
|
|
if(NULL == pinfo)
|
|
{
|
|
LastError = ERROR_NOT_ENOUGH_MEMORY;
|
|
DebugPrint( LVL_MINIMAL, L"Not enough memory in function SfcRestoreFileFromInstallMedia" );
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// get the source information for the first file
|
|
//
|
|
|
|
if (!SfcGetSourceInformation( SourceFileName == NULL ? FileName : SourceFileName, InfName, ExcepPackFile, &pinfo->si )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// create a file queue
|
|
//
|
|
|
|
hFileQ = SetupOpenFileQueue();
|
|
if (hFileQ == INVALID_HANDLE_VALUE) {
|
|
LastError = GetLastError();
|
|
DebugPrint1( LVL_MINIMAL, L"SetupOpenFileQueue failed, ec=%d", LastError );
|
|
b = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// add the file(s) to the queue
|
|
//
|
|
// at this time we know where to copy the files from and the media is
|
|
// present and available, but more files may have been queued up while
|
|
// we performed this effort and possibly spent a long time prompting
|
|
// the user for media. because of this we need to examine the queue and
|
|
// queue up all file copies so the user only gets one prompt.
|
|
//
|
|
// Old note:
|
|
// there may be a problem with this because we could have a situation
|
|
// where there are multiple file copies from different media. this
|
|
// could happen in the case of a service pack or a winpack.
|
|
//
|
|
// New note: (andrewr) setupapi is smart enough to copy one media's worth of files
|
|
// before copying the other media's files, so the prior concern isn't valid
|
|
//
|
|
|
|
b = SfcAddFileToQueue(
|
|
hFileQ,
|
|
FileName,
|
|
TargetFileName,
|
|
TargetDirectory,
|
|
SourceFileName,
|
|
NULL,
|
|
InfName,
|
|
ExcepPackFile,
|
|
&pinfo->si
|
|
);
|
|
if (!b) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// setup the default queue callback with the popups disabled
|
|
//
|
|
|
|
MsgHandlerContext = SetupInitDefaultQueueCallbackEx( NULL, INVALID_HANDLE_VALUE, 0, 0, 0 );
|
|
if (MsgHandlerContext == NULL) {
|
|
LastError = GetLastError();
|
|
DebugPrint1( LVL_MINIMAL, L"SetupInitDefaultQueueCallbackEx failed, ec=%d", LastError );
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Note: There can be more than one SOURCE_INFO for the entire queue, so
|
|
// this code is not strictly correct. But this is really only a problem
|
|
// in the case that we have to prompt the user for media. This will
|
|
// really work itself out in the NEED_MEDIA callback when we are actually
|
|
// trying to copy the file.
|
|
//
|
|
pinfo->fci.MsgHandlerContext = MsgHandlerContext;
|
|
pinfo->fci.si = ψ
|
|
pinfo->fci.FileCount = 1;
|
|
psi = &pinfo->si;
|
|
|
|
pinfo->si.ValidationRequestData = vrd;
|
|
pinfo->fci.AllowUI = AllowUI;
|
|
|
|
pinfo->fci.Flags |= TargetIsCache
|
|
? FCI_FLAG_COPY_TO_CACHE
|
|
: FCI_FLAG_RESTORE_FILE;
|
|
|
|
//
|
|
// force the file queue to require all files be signed
|
|
//
|
|
|
|
pSetupSetQueueFlags( hFileQ, pSetupGetQueueFlags( hFileQ ) | FQF_QUEUE_FORCE_BLOCK_POLICY );
|
|
|
|
//
|
|
// commit the file queue
|
|
//
|
|
|
|
b = SetupCommitFileQueue(
|
|
NULL,
|
|
hFileQ,
|
|
SfcQueueCallback,
|
|
&pinfo->fci
|
|
);
|
|
if (!b) {
|
|
LastError = GetLastError();
|
|
DebugPrint1( LVL_MINIMAL, L"SetupCommitFileQueue failed, ec=0x%08x", LastError );
|
|
}
|
|
|
|
if (UIShown) {
|
|
*UIShown = pinfo->fci.UIShown;
|
|
}
|
|
|
|
exit:
|
|
|
|
//
|
|
// cleanup and exit
|
|
//
|
|
|
|
if (MsgHandlerContext) {
|
|
SetupTermDefaultQueueCallback( MsgHandlerContext );
|
|
}
|
|
if (hFileQ != INVALID_HANDLE_VALUE) {
|
|
SetupCloseFileQueue( hFileQ );
|
|
}
|
|
|
|
if(pinfo != NULL)
|
|
{
|
|
MemFree(pinfo);
|
|
}
|
|
|
|
SetLastError( LastError );
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
SfcRestoreFromCache(
|
|
IN PVALIDATION_REQUEST_DATA vrd,
|
|
IN HCATADMIN hCatAdmin
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine takes a validated file and attempts to restore it from the cache.
|
|
|
|
The routine also does some extra book-keeping tasks, like syncing up the
|
|
copy of the dllcache file with that on disk
|
|
|
|
Arguments:
|
|
|
|
vrd - pointer to VALIDATION_REQUEST_DATA structure describing the file to
|
|
be restored.
|
|
hCatAdmin - crypto context handle to be used in checking file
|
|
|
|
Return Value:
|
|
|
|
always TRUE (indicates we successfully validated the DLL as good or bad)
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSFC_REGISTRY_VALUE RegVal = vrd->RegVal;
|
|
PCOMPLETE_VALIDATION_DATA ImageValData = &vrd->ImageValData;
|
|
UNICODE_STRING ActualFileName;
|
|
PWSTR FileName;
|
|
|
|
//
|
|
// if the original file isn't present, then we should try to restore it
|
|
// from cache
|
|
//
|
|
if (!ImageValData->Original.SignatureValid) {
|
|
|
|
//
|
|
// bad signature
|
|
//
|
|
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"%wZ signature is BAD, try to restore file from cache",
|
|
&RegVal->FileName );
|
|
|
|
//
|
|
// we always try to restore from cache first, even if there isn't a
|
|
// file in the cache
|
|
//
|
|
|
|
ImageValData->RestoreFromCache = TRUE;
|
|
ImageValData->NotifyUser = TRUE;
|
|
if (!vrd->SyncOnly) {
|
|
ImageValData->EventLog = MSG_DLL_CHANGE;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// good signature, let's do some book-keeping here to sync up the
|
|
// file in the dllcache with that on disk
|
|
//
|
|
|
|
|
|
if (ImageValData->Original.FilePresent == TRUE && ImageValData->Cache.FilePresent == FALSE) {
|
|
//
|
|
// the file is missing from the cache but the original file has
|
|
// a valid signature...
|
|
// so we put the original file into the cache
|
|
//
|
|
|
|
//
|
|
// Note that this doesn't really consider the SFCQuota policy, but
|
|
// it's only one file, so we assume that we won't blow the cache
|
|
// quota.
|
|
//
|
|
DebugPrint1( LVL_MINIMAL, L"Cache file doesn't exist; restoring from real - %wZ", &RegVal->FileName );
|
|
ImageValData->RestoreFromReal = TRUE;
|
|
ImageValData->NotifyUser = FALSE;
|
|
ImageValData->EventLog = 0;
|
|
vrd->SyncOnly = TRUE;
|
|
} else {
|
|
//
|
|
// it looks like both files are present and are valid,
|
|
// but we want to resynch the cach copy because someone
|
|
// may have replaced the real file with a new, valid signed
|
|
// file and now the cached copy doesn't match.
|
|
//
|
|
DebugPrint1( LVL_MINIMAL, L"Real file and cache are both present and valid, replace cache with newer - %wZ", &RegVal->FileName );
|
|
ImageValData->RestoreFromReal = TRUE;
|
|
ImageValData->NotifyUser = FALSE;
|
|
ImageValData->EventLog = 0;
|
|
vrd->SyncOnly = TRUE;
|
|
}
|
|
}
|
|
|
|
if (ImageValData->RestoreFromCache || ImageValData->RestoreFromReal) {
|
|
if (ImageValData->RestoreFromReal) {
|
|
//
|
|
// put the real file back in the cache
|
|
//
|
|
|
|
FileName = FileNameOnMedia( RegVal );
|
|
RtlInitUnicodeString( &ActualFileName, FileName );
|
|
|
|
ASSERT(FileName != NULL);
|
|
ASSERT(RegVal->DirHandle != NULL);
|
|
ASSERT(SfcProtectedDllFileDirectory != NULL);
|
|
|
|
Status = SfcCopyFile(
|
|
RegVal->DirHandle,
|
|
RegVal->DirName.Buffer,
|
|
SfcProtectedDllFileDirectory,
|
|
NULL,
|
|
&ActualFileName,
|
|
&RegVal->FileName
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
SfcGetValidationData( &RegVal->FileName,
|
|
&RegVal->FullPathName,
|
|
RegVal->DirHandle,
|
|
hCatAdmin,
|
|
&ImageValData->New );
|
|
|
|
if (ImageValData->New.SignatureValid == FALSE) {
|
|
ImageValData->New.DllVersion = 0;
|
|
}
|
|
|
|
if ((SFCDisable != SFC_DISABLE_SETUP) && (vrd->SyncOnly == FALSE)) {
|
|
SfcReportEvent( ImageValData->EventLog, RegVal->FullPathName.Buffer, ImageValData, 0 );
|
|
}
|
|
vrd->CopyCompleted = TRUE;
|
|
|
|
} else {
|
|
SfcReportEvent( MSG_CACHE_COPY_ERROR, RegVal->FullPathName.Buffer, ImageValData, GetLastError() );
|
|
}
|
|
} else { // restorefromcache == TRUE
|
|
|
|
//
|
|
// we need to put the cache copy back
|
|
// but only if the cache version is valid
|
|
//
|
|
if (ImageValData->Cache.FilePresent && ImageValData->Cache.SignatureValid) {
|
|
|
|
FileName = FileNameOnMedia( RegVal );
|
|
RtlInitUnicodeString( &ActualFileName, FileName );
|
|
|
|
ASSERT(FileName != NULL);
|
|
ASSERT(SfcProtectedDllFileDirectory != NULL);
|
|
ASSERT(RegVal->DirHandle != NULL);
|
|
|
|
Status = SfcCopyFile(
|
|
SfcProtectedDllFileDirectory,
|
|
SfcProtectedDllPath.Buffer,
|
|
RegVal->DirHandle,
|
|
RegVal->DirName.Buffer,
|
|
&RegVal->FileName,
|
|
&ActualFileName
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
vrd->CopyCompleted = TRUE;
|
|
ImageValData->NotifyUser = TRUE;
|
|
|
|
if (!vrd->SyncOnly) {
|
|
ImageValData->EventLog = MSG_DLL_CHANGE;
|
|
}
|
|
|
|
SfcGetValidationData(
|
|
&RegVal->FileName,
|
|
&RegVal->FullPathName,
|
|
RegVal->DirHandle,
|
|
hCatAdmin,
|
|
&ImageValData->New );
|
|
|
|
if (ImageValData->New.SignatureValid == FALSE) {
|
|
ImageValData->New.DllVersion = 0;
|
|
}
|
|
|
|
if (vrd->SyncOnly == FALSE) {
|
|
SfcReportEvent(
|
|
ImageValData->EventLog,
|
|
RegVal->FullPathName.Buffer,
|
|
ImageValData,
|
|
0 );
|
|
}
|
|
|
|
|
|
} else {
|
|
//
|
|
// we failed to copy the file from the cache so we have
|
|
// to restore from media
|
|
//
|
|
ImageValData->RestoreFromMedia = TRUE;
|
|
ImageValData->NotifyUser = TRUE;
|
|
if (!vrd->SyncOnly) {
|
|
ImageValData->EventLog = MSG_DLL_CHANGE;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// need to restore from cache but the cache copy is missing
|
|
// or invalid. Clear the crud out of the cache
|
|
//
|
|
FileName = FileNameOnMedia( RegVal );
|
|
RtlInitUnicodeString( &ActualFileName, FileName );
|
|
|
|
|
|
DebugPrint2( LVL_MINIMAL,
|
|
L"Cannot restore file from the cache because the "
|
|
L"cache file [%wZ] is invalid - %wZ ",
|
|
&ActualFileName,
|
|
&RegVal->FileName );
|
|
ImageValData->BadCacheEntry = TRUE;
|
|
ImageValData->NotifyUser = TRUE;
|
|
if (!vrd->SyncOnly) {
|
|
ImageValData->EventLog = MSG_DLL_CHANGE;
|
|
}
|
|
ImageValData->RestoreFromMedia = TRUE;
|
|
SfcDeleteFile(
|
|
SfcProtectedDllFileDirectory,
|
|
&ActualFileName );
|
|
SfcReportEvent(
|
|
ImageValData->EventLog,
|
|
RegVal->FullPathName.Buffer,
|
|
ImageValData,
|
|
0 );
|
|
}
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"Failed to restore a file from the cache - %wZ",
|
|
&RegVal->FileName );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcSyncCache(
|
|
IN PVALIDATION_REQUEST_DATA vrd,
|
|
IN HCATADMIN hCatAdmin
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine takes a validated file and attempts to sync a copy of the file in
|
|
the cache.
|
|
|
|
Arguments:
|
|
|
|
vrd - pointer to VALIDATION_REQUEST_DATA structure describing the file to
|
|
be synced.
|
|
hCatAdmin - crypto context handle to be used in checking file
|
|
|
|
Return Value:
|
|
|
|
TRUE indicates we successfully sunc a copy in the dllcache
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSFC_REGISTRY_VALUE RegVal = vrd->RegVal;
|
|
PCOMPLETE_VALIDATION_DATA ImageValData = &vrd->ImageValData;
|
|
UNICODE_STRING ActualFileName;
|
|
PWSTR FileName;
|
|
|
|
|
|
//
|
|
// caller should ensure that the signature is valid before proceeding
|
|
//
|
|
ASSERT(ImageValData->Original.SignatureValid == TRUE);
|
|
|
|
//
|
|
// if there is a copy in the dllcache already, let's assume that there is
|
|
// enough space to sync the new copy of the file. Otherwise we have to
|
|
// ensure that there is reasonable space before proceeding.
|
|
//
|
|
//
|
|
if (vrd->ImageValData.Cache.FilePresent == TRUE) {
|
|
ImageValData->RestoreFromReal = TRUE;
|
|
ImageValData->NotifyUser = FALSE;
|
|
ImageValData->EventLog = 0;
|
|
vrd->SyncOnly = TRUE;
|
|
} else {
|
|
|
|
ULONGLONG RequiredFreeSpace;
|
|
ULARGE_INTEGER FreeBytesAvailableToCaller;
|
|
ULARGE_INTEGER TotalNumberOfBytes;
|
|
ULARGE_INTEGER TotalNumberOfFreeBytes;
|
|
|
|
//
|
|
// The file is not in the cache.
|
|
//
|
|
RequiredFreeSpace = (GetPageFileSize() + SFC_REQUIRED_FREE_SPACE)* ONE_MEG;
|
|
|
|
//
|
|
// see
|
|
// a) how much space we have left
|
|
// b) compare against our free space buffer
|
|
// c) how much space the cache is using
|
|
// d) compare against our cache quota
|
|
//
|
|
// if these all succeed, then we're allowed to copy the file into
|
|
// the cache
|
|
//
|
|
if (GetDiskFreeSpaceEx(
|
|
SfcProtectedDllPath.Buffer,
|
|
&FreeBytesAvailableToCaller,
|
|
&TotalNumberOfBytes,
|
|
&TotalNumberOfFreeBytes)
|
|
&& TotalNumberOfFreeBytes.QuadPart > RequiredFreeSpace) {
|
|
if (TotalNumberOfBytes.QuadPart <= SFCQuota) {
|
|
ImageValData->RestoreFromReal = TRUE;
|
|
ImageValData->NotifyUser = FALSE;
|
|
ImageValData->EventLog = 0;
|
|
vrd->SyncOnly = TRUE;
|
|
}else {
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"quota is exceeded (%I64d), can't copy new files",
|
|
TotalNumberOfBytes);
|
|
Status = STATUS_QUOTA_EXCEEDED;
|
|
}
|
|
} else {
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"Not enough free space on disk (%I64d), can't copy new files",
|
|
TotalNumberOfBytes.QuadPart);
|
|
Status = STATUS_QUOTA_EXCEEDED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we were told to copy the file above, then do it.
|
|
//
|
|
if (ImageValData->RestoreFromReal) {
|
|
//
|
|
// put the real file back in the cache
|
|
//
|
|
|
|
FileName = FileNameOnMedia( RegVal );
|
|
RtlInitUnicodeString( &ActualFileName, FileName );
|
|
|
|
ASSERT(FileName != NULL);
|
|
ASSERT(RegVal->DirHandle != NULL);
|
|
ASSERT(SfcProtectedDllFileDirectory != NULL);
|
|
|
|
Status = SfcCopyFile(
|
|
RegVal->DirHandle,
|
|
RegVal->DirName.Buffer,
|
|
SfcProtectedDllFileDirectory,
|
|
SfcProtectedDllPath.Buffer,
|
|
&ActualFileName,
|
|
&RegVal->FileName
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
WCHAR FullPathToFile[MAX_PATH];
|
|
UNICODE_STRING FullPathToCacheFile;
|
|
|
|
wcscpy(FullPathToFile,SfcProtectedDllPath.Buffer);
|
|
pSetupConcatenatePaths(
|
|
FullPathToFile,
|
|
ActualFileName.Buffer,
|
|
UnicodeChars(FullPathToFile), NULL );
|
|
RtlInitUnicodeString( &FullPathToCacheFile, FullPathToFile );
|
|
|
|
SfcGetValidationData( &ActualFileName,
|
|
&FullPathToCacheFile,
|
|
SfcProtectedDllFileDirectory,
|
|
hCatAdmin,
|
|
&ImageValData->New );
|
|
|
|
//
|
|
// since we started with a valid file, we had better end up with
|
|
// a valid file installed
|
|
//
|
|
if(ImageValData->New.SignatureValid == TRUE) {
|
|
|
|
vrd->CopyCompleted = TRUE;
|
|
|
|
} else {
|
|
ImageValData->New.DllVersion = 0;
|
|
SfcReportEvent( MSG_CACHE_COPY_ERROR, RegVal->FullPathName.Buffer, ImageValData, GetLastError() );
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
} else {
|
|
SfcReportEvent( MSG_CACHE_COPY_ERROR, RegVal->FullPathName.Buffer, ImageValData, GetLastError() );
|
|
}
|
|
}
|
|
|
|
return (NT_SUCCESS(Status));
|
|
|
|
}
|
|
|
|
|
|
|
|
PSOURCE_INFO
|
|
pSfcGetSourceInfoFromSourceName(
|
|
const PSOURCE_INFO *SourceInfoList,
|
|
DWORD SourceInfoCount,
|
|
const PSOURCE_MEDIA SourceMediaInfo
|
|
)
|
|
{
|
|
DWORD i;
|
|
PSOURCE_INFO SourceInfo;
|
|
|
|
ASSERT( SourceInfoList != NULL );
|
|
ASSERT( SourceInfoCount > 0 );
|
|
ASSERT( SourceMediaInfo != NULL );
|
|
|
|
if (ShuttingDown) {
|
|
return NULL;
|
|
}
|
|
|
|
i = 0;
|
|
while (i < SourceInfoCount) {
|
|
|
|
SourceInfo = SourceInfoList[i];
|
|
|
|
ASSERT(SourceInfo != NULL);
|
|
|
|
if (_wcsicmp(
|
|
SourceInfo->SourceFileName,
|
|
SourceMediaInfo->SourceFile) == 0) {
|
|
return (SourceInfo);
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
PVALIDATION_REQUEST_DATA
|
|
pSfcGetValidationRequestFromFilePaths(
|
|
const PSOURCE_INFO *SourceInfoList,
|
|
DWORD SourceInfoCount,
|
|
const PFILEPATHS FilePaths
|
|
)
|
|
{
|
|
DWORD i;
|
|
PSOURCE_INFO SourceInfo;
|
|
PCWSTR p;
|
|
|
|
ASSERT( SourceInfoList != NULL );
|
|
ASSERT( SourceInfoCount > 0 );
|
|
ASSERT( FilePaths != NULL );
|
|
|
|
if (ShuttingDown) {
|
|
return NULL;
|
|
}
|
|
|
|
i = 0;
|
|
while (i < SourceInfoCount) {
|
|
|
|
SourceInfo = SourceInfoList[i];
|
|
|
|
ASSERT(SourceInfo != NULL);
|
|
|
|
if (SourceInfo->ValidationRequestData) {
|
|
p = SourceInfo->ValidationRequestData->RegVal->FullPathName.Buffer;
|
|
|
|
if (_wcsicmp(
|
|
p,
|
|
FilePaths->Target) == 0) {
|
|
return (SourceInfo->ValidationRequestData);
|
|
}
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcQueueAddFileToRestoreQueue(
|
|
IN BOOL RequiresUI,
|
|
IN PSFC_REGISTRY_VALUE RegVal,
|
|
IN PCWSTR InfFileName,
|
|
IN BOOL ExcepPackFile,
|
|
IN OUT PSOURCE_INFO SourceInfo,
|
|
IN PCWSTR ActualFileNameOnMedia
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to add a file to the appropriate global file queue.
|
|
|
|
If the queue is being commited, then this routine fails.
|
|
|
|
If the file queue does not yet exist, the queue is created.
|
|
|
|
Arguments:
|
|
|
|
RequiresUI - if TRUE, the file will require UI in order to be
|
|
installed
|
|
RegVal - pointer to SFC_REGISTRY_VALUE that describes file
|
|
to be restored
|
|
SourceInfo - pointer to SOURCE_INFO structure describing where
|
|
the source file is to be restored from
|
|
ActualFileNameOnMedia - the real filename of the file on the source media
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file was added to the queue, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
PRESTORE_QUEUE RestoreQueue;
|
|
BOOL RetVal = FALSE;
|
|
PVOID Ptr;
|
|
|
|
ASSERT( SourceInfo != NULL );
|
|
ASSERT( SourceInfo->SourceFileName[0] != (TCHAR)'\0' );
|
|
|
|
//
|
|
// point to the proper global queue
|
|
//
|
|
RestoreQueue = RequiresUI
|
|
? &UIRestoreQueue
|
|
: &SilentRestoreQueue;
|
|
|
|
//
|
|
// must protect all of this in a critical section
|
|
//
|
|
RtlEnterCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
//
|
|
// if the queue is in progress, we can't do anything
|
|
//
|
|
if (!RestoreQueue->RestoreInProgress) {
|
|
//
|
|
// create the queue if it doesn't already exist
|
|
//
|
|
if (RestoreQueue->FileQueue == INVALID_HANDLE_VALUE) {
|
|
RestoreQueue->FileQueue = SetupOpenFileQueue();
|
|
if (RestoreQueue->FileQueue == INVALID_HANDLE_VALUE) {
|
|
DebugPrint1(
|
|
LVL_MINIMAL,
|
|
L"SetupOpenFileQueue() failed, ec=%d",
|
|
GetLastError() );
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// also preallocate nothing to make re-allocating easy
|
|
//
|
|
ASSERT(RestoreQueue->FileCopyInfo.si == NULL);
|
|
RestoreQueue->FileCopyInfo.si = MemAlloc( 0 );
|
|
|
|
}
|
|
|
|
ASSERT(RestoreQueue->FileQueue != INVALID_HANDLE_VALUE);
|
|
|
|
//
|
|
// now make more room in our array of PSOURCE_INFO pointers
|
|
// for the new entry in our queue and assign that entry.
|
|
//
|
|
Ptr = MemReAlloc(
|
|
((RestoreQueue->QueueCount + 1) * sizeof(PSOURCE_INFO)),
|
|
RestoreQueue->FileCopyInfo.si );
|
|
|
|
if (Ptr) {
|
|
RestoreQueue->FileCopyInfo.si = (PSOURCE_INFO *)Ptr;
|
|
RestoreQueue->FileCopyInfo.si[RestoreQueue->QueueCount] = SourceInfo;
|
|
} else {
|
|
MemFree( (PVOID) RestoreQueue->FileCopyInfo.si );
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// add the file to the queue
|
|
//
|
|
RetVal = SfcAddFileToQueue(
|
|
RestoreQueue->FileQueue,
|
|
RegVal->FileName.Buffer,
|
|
RegVal->FileName.Buffer,
|
|
RegVal->DirName.Buffer,
|
|
ActualFileNameOnMedia,
|
|
NULL,
|
|
InfFileName,
|
|
ExcepPackFile,
|
|
SourceInfo
|
|
);
|
|
|
|
if (!RetVal) {
|
|
DebugPrint2(
|
|
LVL_MINIMAL,
|
|
L"SfcAddFileToQueue failed [%ws], ec = %d",
|
|
RegVal->FileName.Buffer,
|
|
GetLastError() );
|
|
} else {
|
|
RestoreQueue->QueueCount += 1;
|
|
//
|
|
// remember something about adding this entry so that
|
|
// when we commit the file we how to treat it
|
|
//
|
|
SourceInfo->Flags |= SI_FLAG_USERESTORE_QUEUE
|
|
| (RequiresUI ? 0 : SI_FLAG_SILENT_QUEUE) ;
|
|
|
|
DebugPrint2(
|
|
LVL_MINIMAL,
|
|
L"Added file [%ws] to %ws queue for restoration",
|
|
RegVal->FileName.Buffer,
|
|
RequiresUI ? L"UIRestoreQueue" : L"SilentRestoreQueue" );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
RtlLeaveCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
return RetVal;
|
|
|
|
}
|
|
|
|
BOOL
|
|
SfcQueueResetQueue(
|
|
IN BOOL RequiresUI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called after we've successfully committed the file queue.
|
|
|
|
The routine removes all of the associated validation requests from our
|
|
queue, logging entries for each of these requests. It also cleans up the
|
|
global file queue to be processed again.
|
|
|
|
If the queue has not been commited, then this routine fails.
|
|
|
|
Arguments:
|
|
|
|
RequiresUI - if TRUE, the file will require UI in order to be
|
|
installed
|
|
|
|
Return Value:
|
|
|
|
TRUE if the routine succeeded, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
PRESTORE_QUEUE RestoreQueue;
|
|
BOOL RetVal = FALSE;
|
|
DWORD Count;
|
|
PLIST_ENTRY Current;
|
|
PVALIDATION_REQUEST_DATA vrd;
|
|
DWORD Mask;
|
|
BOOL DoReset = FALSE;
|
|
DWORD Msg, ErrorCode;
|
|
|
|
//
|
|
// point to the proper global queue
|
|
//
|
|
RestoreQueue = RequiresUI
|
|
? &UIRestoreQueue
|
|
: &SilentRestoreQueue;
|
|
|
|
|
|
|
|
Mask = (VRD_FLAG_REQUEST_PROCESSED | VRD_FLAG_REQUEST_QUEUED)
|
|
| (RequiresUI ? VRD_FLAG_REQUIRE_UI : 0);
|
|
|
|
//
|
|
// must protect all of this in a critical section
|
|
//
|
|
RtlEnterCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
if ((RestoreQueue->RestoreInProgress == TRUE) &&
|
|
(RestoreQueue->RestoreComplete == TRUE)) {
|
|
DoReset = TRUE;
|
|
}
|
|
|
|
if (DoReset) {
|
|
|
|
RtlEnterCriticalSection( &ErrorCs );
|
|
|
|
Current = SfcErrorQueue.Flink;
|
|
Count = 0;
|
|
|
|
//
|
|
// cycle through our queue, logging and removing requests as we go.
|
|
//
|
|
while (Current != &SfcErrorQueue) {
|
|
vrd = CONTAINING_RECORD( Current, VALIDATION_REQUEST_DATA, Entry );
|
|
|
|
Current = vrd->Entry.Flink;
|
|
|
|
//
|
|
// check if we have a valid entry
|
|
//
|
|
|
|
if (vrd->Flags == Mask) {
|
|
Count += 1;
|
|
|
|
#if 0
|
|
//
|
|
// if the file was copied successfully, then we'd better make
|
|
// sure that the signature of the file is valid
|
|
//
|
|
// if the file failed to be copied, then we'd better have a
|
|
// reason why it failed to be copied
|
|
//
|
|
ASSERT(vrd->CopyCompleted
|
|
? (vrd->ImageValData.New.SignatureValid == TRUE)
|
|
&& (vrd->ImageValData.EventLog == MSG_DLL_CHANGE)
|
|
: (RestoreQueue->LastErrorCode == ERROR_SUCCESS)
|
|
? ((vrd->ImageValData.EventLog == MSG_RESTORE_FAILURE)
|
|
&& (vrd->Win32Error != ERROR_SUCCESS))
|
|
: TRUE );
|
|
#endif
|
|
|
|
if (vrd->CopyCompleted && vrd->ImageValData.New.SignatureValid == FALSE) {
|
|
vrd->ImageValData.New.DllVersion = 0;
|
|
}
|
|
|
|
DebugPrint2(
|
|
LVL_MINIMAL,
|
|
L"File [%ws] %ws restored successfully.",
|
|
vrd->RegVal->FullPathName.Buffer,
|
|
vrd->CopyCompleted ? L"was" : L"was NOT"
|
|
);
|
|
|
|
//
|
|
// log an event
|
|
//
|
|
// first determine if we need to tweak the error code if the
|
|
// user cancelled, then log the event
|
|
//
|
|
ErrorCode = vrd->Win32Error;
|
|
Msg = vrd->ImageValData.EventLog;
|
|
|
|
if (RestoreQueue->LastErrorCode != ERROR_SUCCESS) {
|
|
if (RestoreQueue->LastErrorCode == ERROR_CANCELLED) {
|
|
if ((vrd->Win32Error == ERROR_SUCCESS)
|
|
&& (vrd->CopyCompleted == FALSE) ) {
|
|
ErrorCode = ERROR_CANCELLED;
|
|
Msg = SFCNoPopUps ? MSG_COPY_CANCEL_NOUI : MSG_COPY_CANCEL;
|
|
}
|
|
} else {
|
|
if ((vrd->Win32Error == ERROR_SUCCESS)
|
|
&& (vrd->CopyCompleted == FALSE) ) {
|
|
ErrorCode = RestoreQueue->LastErrorCode;
|
|
Msg = MSG_RESTORE_FAILURE;
|
|
} else if (Msg == 0) {
|
|
Msg = MSG_RESTORE_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(Msg != 0);
|
|
if (Msg == 0) {
|
|
Msg = MSG_RESTORE_FAILURE;
|
|
}
|
|
|
|
//
|
|
// log the event
|
|
//
|
|
SfcReportEvent(
|
|
Msg,
|
|
vrd->RegVal->FileName.Buffer,
|
|
&vrd->ImageValData,
|
|
ErrorCode );
|
|
|
|
//
|
|
// remove the entry
|
|
//
|
|
|
|
RemoveEntryList( &vrd->Entry );
|
|
ErrorQueueCount -= 1;
|
|
MemFree( vrd );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &ErrorCs );
|
|
|
|
ASSERT( Count == RestoreQueue->QueueCount );
|
|
|
|
CloseHandle( RestoreQueue->WorkerThreadHandle );
|
|
RestoreQueue->WorkerThreadHandle = NULL;
|
|
RestoreQueue->RestoreComplete = FALSE;
|
|
RestoreQueue->QueueCount = 0;
|
|
SetupCloseFileQueue( RestoreQueue->FileQueue );
|
|
RestoreQueue->FileQueue = INVALID_HANDLE_VALUE;
|
|
RestoreQueue->RestoreInProgress = FALSE;
|
|
RestoreQueue->RestoreStatus = FALSE;
|
|
RestoreQueue->LastErrorCode = ERROR_SUCCESS;
|
|
SetupTermDefaultQueueCallback( RestoreQueue->FileCopyInfo.MsgHandlerContext );
|
|
MemFree((PVOID)RestoreQueue->FileCopyInfo.si);
|
|
ZeroMemory( &RestoreQueue->FileCopyInfo, sizeof(FILE_COPY_INFO) );
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
return ( RetVal );
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcQueueCommitRestoreQueue(
|
|
IN BOOL RequiresUI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to add a file to the appropriate global file queue.
|
|
|
|
If the queue is being commited, then this routine fails.
|
|
|
|
If the file queue does not yet exist, the queue is created.
|
|
|
|
Arguments:
|
|
|
|
RequiresUI - if TRUE, the file will require UI in order to be
|
|
installed
|
|
RegVal - pointer to SFC_REGISTRY_VALUE that describes file
|
|
to be restored
|
|
SourceInfo - pointer to SOURCE_INFO structure describing where
|
|
the source file is to be restored from
|
|
ActualFileNameOnMedia - the real filename of the file on the source media
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file was added to the queue, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
PRESTORE_QUEUE RestoreQueue;
|
|
BOOL RetVal = FALSE;
|
|
BOOL DoCommit = FALSE;
|
|
|
|
//
|
|
// point to the proper global queue
|
|
//
|
|
RestoreQueue = RequiresUI
|
|
? &UIRestoreQueue
|
|
: &SilentRestoreQueue;
|
|
|
|
//
|
|
// we must protect our restore queue access in a critical section
|
|
//
|
|
RtlEnterCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
//
|
|
// see if we should commit the queue
|
|
//
|
|
if ( (RestoreQueue->RestoreInProgress == FALSE)
|
|
&& (RestoreQueue->RestoreComplete == FALSE)
|
|
&& (RestoreQueue->QueueCount > 0)) {
|
|
ASSERT(RestoreQueue->FileQueue != INVALID_HANDLE_VALUE );
|
|
RestoreQueue->RestoreInProgress = TRUE;
|
|
DoCommit = TRUE;
|
|
}
|
|
|
|
if (DoCommit) {
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"Creating pSfcRestoreFromMediaWorkerThread for %ws queue",
|
|
RequiresUI ? L"UIRestoreQueue" : L"SilentRestoreQueue" );
|
|
|
|
RestoreQueue->WorkerThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)pSfcRestoreFromMediaWorkerThread,
|
|
RestoreQueue,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (!RestoreQueue->WorkerThreadHandle) {
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"Couldn't create pSfcRestoreFromMediaWorkerThread, ec = 0x%08x",
|
|
GetLastError() );
|
|
RestoreQueue->RestoreInProgress = FALSE;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
return ( RetVal );
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSfcRestoreFromMediaWorkerThread(
|
|
IN PRESTORE_QUEUE RestoreQueue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine takes a media queue that is ready for committal and commits
|
|
that queue to disk.
|
|
|
|
We require another thread to do the actual queue commital for 2
|
|
reasons:
|
|
|
|
a) we want to keep servicing change requests as they come in (for example,
|
|
if we have to prompt for UI for some requests and not for others, we can
|
|
commit the requests which do not require UI while we wait for media to
|
|
become present to commit the files which require UI
|
|
|
|
b) it makes terminating our valiation thread much easier if we know that we
|
|
will never have popups on the screen as a result of that thread. We can
|
|
simply signal an event for that thread to go away, which can then signal
|
|
these worker threads to go away.
|
|
|
|
Arguments:
|
|
|
|
RestoreQueue - pointer to a RESTORE_QUEUE structure which describes the
|
|
queue to be committed.
|
|
|
|
Return Value:
|
|
|
|
N/A.
|
|
|
|
--*/
|
|
{
|
|
BOOL RetVal;
|
|
PVOID MsgHandlerContext = NULL;
|
|
BOOL RequiresUI;
|
|
|
|
RequiresUI = (RestoreQueue == &UIRestoreQueue);
|
|
|
|
ASSERT(RestoreQueue != NULL);
|
|
|
|
#if 1
|
|
if (RequiresUI) {
|
|
SetThreadDesktop( hUserDesktop );
|
|
}
|
|
#endif
|
|
|
|
DebugPrint1( LVL_MINIMAL,
|
|
L"entering pSfcRestoreFromMediaWorkerThread for %ws queue",
|
|
RequiresUI ? L"UIRestoreQueue" : L"SilentRestoreQueue" );
|
|
|
|
//
|
|
// setup the default queue callback with the popups disabled
|
|
//
|
|
MsgHandlerContext = SetupInitDefaultQueueCallbackEx( NULL, INVALID_HANDLE_VALUE, 0, 0, 0 );
|
|
if (MsgHandlerContext == NULL) {
|
|
DebugPrint1( LVL_VERBOSE, L"SetupInitDefaultQueueCallbackEx failed, ec=%d", GetLastError() );
|
|
RetVal = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// build up a structure which we use in committing the queue.
|
|
//
|
|
|
|
RtlEnterCriticalSection( &RestoreQueue->CriticalSection );
|
|
RestoreQueue->FileCopyInfo.MsgHandlerContext = MsgHandlerContext;
|
|
ASSERT( RestoreQueue->FileCopyInfo.si != NULL);
|
|
RestoreQueue->FileCopyInfo.FileCount = RestoreQueue->QueueCount;
|
|
RestoreQueue->FileCopyInfo.AllowUI = RequiresUI;
|
|
|
|
//
|
|
// remember that this is a restoration queue.
|
|
// When we commit the queue we will use this fact to mark each file in
|
|
// our queue as processed so that we can log it and remember the
|
|
// file signature, etc.
|
|
//
|
|
RestoreQueue->FileCopyInfo.Flags |= FCI_FLAG_RESTORE_FILE;
|
|
RestoreQueue->FileCopyInfo.Flags |= FCI_FLAG_USERESTORE_QUEUE
|
|
| (RequiresUI ? 0 : FCI_FLAG_SILENT_QUEUE) ;
|
|
|
|
//
|
|
// force the file queue to require all files be signed
|
|
//
|
|
|
|
pSetupSetQueueFlags( RestoreQueue->FileQueue, pSetupGetQueueFlags( RestoreQueue->FileQueue ) | FQF_QUEUE_FORCE_BLOCK_POLICY );
|
|
|
|
RtlLeaveCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
//
|
|
// commit the file queue
|
|
//
|
|
|
|
RetVal = SetupCommitFileQueue(
|
|
NULL,
|
|
RestoreQueue->FileQueue,
|
|
SfcQueueCallback,
|
|
&RestoreQueue->FileCopyInfo
|
|
);
|
|
if (!RetVal) {
|
|
DebugPrint1( LVL_VERBOSE, L"SetupCommitFileQueue failed, ec=0x%08x", GetLastError() );
|
|
}
|
|
|
|
//
|
|
// if we succeeded commiting the queue, mark the queue
|
|
//
|
|
RtlEnterCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
ASSERT(RestoreQueue->RestoreInProgress == TRUE);
|
|
RestoreQueue->RestoreStatus = RetVal;
|
|
RestoreQueue->LastErrorCode = RetVal ? ERROR_SUCCESS : GetLastError();
|
|
RestoreQueue->RestoreComplete = TRUE;
|
|
|
|
RtlLeaveCriticalSection( &RestoreQueue->CriticalSection );
|
|
|
|
//
|
|
// set an event to wake up the validation thread so it can clean things up
|
|
//
|
|
SetEvent( ErrorQueueEvent );
|
|
|
|
exit:
|
|
|
|
DebugPrint2( LVL_MINIMAL,
|
|
L"Leaving pSfcRestoreFromMediaWorkerThread for %ws queue, retval = %d",
|
|
RequiresUI ? L"UIRestoreQueue" : L"SilentRestoreQueue",
|
|
RetVal
|
|
);
|
|
|
|
return (RetVal);
|
|
|
|
}
|
|
|
|
DWORD
|
|
SfcGetCabTagFile(
|
|
IN PSOURCE_INFO psi,
|
|
OUT PWSTR* ppFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets the tagfile of a cabfile. It is called when the tagfile for a protected file is a cabfile.
|
|
Allocates the output buffer.
|
|
|
|
Arguments:
|
|
|
|
psi - the protected file source info
|
|
ppFile - receives the tagfile
|
|
|
|
Return value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL bExcepFile = FALSE;
|
|
HINF hInf = INVALID_HANDLE_VALUE;
|
|
UINT uiInfo = SRCINFO_TAGFILE;
|
|
|
|
ASSERT(psi != NULL && ppFile != NULL);
|
|
ASSERT(psi->ValidationRequestData != NULL && psi->ValidationRequestData->RegVal != NULL);
|
|
|
|
*ppFile = (PWSTR) MemAlloc(MAX_PATH * sizeof(WCHAR));
|
|
|
|
if(NULL == *ppFile) {
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
//
|
|
// if there is a second tagfile, then we have to use the file's inf instead of layout.inf
|
|
//
|
|
if((psi->SetupAPIFlags & SRC_FLAGS_CABFILE) != 0) {
|
|
uiInfo = SRCINFO_TAGFILE2;
|
|
|
|
if(psi->ValidationRequestData != NULL && psi->ValidationRequestData->RegVal != NULL) {
|
|
bExcepFile = SfcGetInfName(psi->ValidationRequestData->RegVal, *ppFile);
|
|
}
|
|
}
|
|
|
|
hInf = SfcOpenInf(*ppFile, bExcepFile);
|
|
|
|
if(INVALID_HANDLE_VALUE == hInf) {
|
|
dwError = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
if(!SetupGetSourceInfo(hInf, psi->SourceId, uiInfo, *ppFile, MAX_PATH, NULL)) {
|
|
dwError = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if(dwError != ERROR_SUCCESS)
|
|
{
|
|
MemFree(*ppFile);
|
|
*ppFile = NULL;
|
|
}
|
|
|
|
if(hInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
|
|
return dwError;
|
|
}
|