mirror of https://github.com/lianthony/NT4.0
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.
712 lines
14 KiB
712 lines
14 KiB
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// HINST/HMODULE for this app.
|
|
//
|
|
HINSTANCE hInst;
|
|
|
|
//
|
|
// Global variable indicating that execution has been cancelled.
|
|
// This gets set if certain errors occur, or the user cancels, etc.
|
|
// Worker threads are expected to respect this variable but
|
|
// we don't bother synchronizing it, because the worst case would be
|
|
// that an extra file/dir, registry key, etc, gets scanned or diffed
|
|
// (given that the worker threads check this value at the top of their
|
|
// main loops).
|
|
//
|
|
// We also have a cancel event that some threads may wait on.
|
|
//
|
|
BOOL Cancel;
|
|
HANDLE CancelEvent;
|
|
|
|
//
|
|
// Name of application. Filled in at init time.
|
|
//
|
|
PCWSTR AppName;
|
|
|
|
//
|
|
// Mode we are being run in.
|
|
//
|
|
SysdiffMode Mode;
|
|
|
|
//
|
|
// Flag indicating whether we are supposed to generate unicode text files.
|
|
//
|
|
BOOL UnicodeTextFiles;
|
|
|
|
//
|
|
// This flag tells us whether we are supposed to map changes to the
|
|
// user profile directory structure to the default user.
|
|
//
|
|
BOOL RemapProfileChanges;
|
|
|
|
//
|
|
// This flag tells us to ignore all file/dir diffs except those
|
|
// in %userprofile%. Useful in DSP OEM case.
|
|
//
|
|
BOOL UserProfileFilesOnly;
|
|
|
|
//
|
|
// Special DSP inf mode where we don't generate an OEM tree
|
|
// but instead move files in the %USERPROFILE% directory
|
|
// into the backup profile directory used by the rollback.restartable
|
|
// setup stuff.
|
|
//
|
|
BOOL DspMode;
|
|
|
|
//
|
|
// Title for diff.
|
|
//
|
|
PCWSTR PackageTitle;
|
|
|
|
//
|
|
// Args from command line.
|
|
//
|
|
PCWSTR CmdLineSnapshotFile;
|
|
PCWSTR CmdLineDiffFile;
|
|
PCWSTR CmdLineLogFile;
|
|
PCWSTR CmdLineDumpFile;
|
|
|
|
|
|
DWORD
|
|
ThreadMain(
|
|
IN PVOID ThreadParameter
|
|
);
|
|
|
|
BOOL
|
|
InitApp(
|
|
IN BOOL Init
|
|
);
|
|
|
|
BOOL
|
|
ParseArgs(
|
|
IN int argc,
|
|
IN PCWSTR *argv
|
|
);
|
|
|
|
VOID
|
|
Usage(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
FileValidationError(
|
|
IN DWORD MessageId,
|
|
IN BOOL Friendly
|
|
);
|
|
|
|
int
|
|
_CRTAPI1
|
|
main(
|
|
VOID
|
|
)
|
|
{
|
|
int argc;
|
|
PWSTR *argv;
|
|
DWORD d;
|
|
MSG msg;
|
|
HANDLE ThreadHandle;
|
|
|
|
//
|
|
// Get unicode args using special shell API
|
|
//
|
|
argv = CommandLineToArgvW(GetCommandLine(),&argc);
|
|
if(!argv) {
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// Fire up the thread that will do the real work.
|
|
// This allows the main thread to run the UI.
|
|
//
|
|
ThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
ThreadMain,
|
|
NULL,
|
|
CREATE_SUSPENDED,
|
|
&d
|
|
);
|
|
|
|
if(!ThreadHandle) {
|
|
//
|
|
// Bail now.
|
|
//
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// Set up the module handle global.
|
|
//
|
|
hInst = GetModuleHandle(NULL);
|
|
|
|
//
|
|
// Parse arguments.
|
|
//
|
|
if(!ParseArgs(argc,argv)) {
|
|
Usage();
|
|
return(1);
|
|
}
|
|
|
|
if(!InitApp(TRUE)) {
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// Kick the main worker thread.
|
|
//
|
|
ResumeThread(ThreadHandle);
|
|
CloseHandle(ThreadHandle);
|
|
|
|
//
|
|
// Pump the message queue until done.
|
|
//
|
|
while(GetMessage(&msg,NULL,0,0) == TRUE) {
|
|
if(MdiClientWindow == NULL || !TranslateMDISysAccel(MdiClientWindow,&msg)) {
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
InitApp(FALSE);
|
|
|
|
return((int)msg.wParam);
|
|
}
|
|
|
|
|
|
DWORD
|
|
ThreadMain(
|
|
IN PVOID ThreadParameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main worker routine for this program. The main window creates
|
|
a thread with this routine as the entry point.
|
|
|
|
Arguments:
|
|
|
|
Unused.
|
|
|
|
Return Value:
|
|
|
|
Unused.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
WCHAR Path[MAX_PATH];
|
|
PWCHAR p;
|
|
|
|
UNREFERENCED_PARAMETER(ThreadParameter);
|
|
|
|
//
|
|
// Build a global list of valid hard drives.
|
|
//
|
|
BuildValidHardDriveList();
|
|
|
|
// In snapshot or diff modes, build the excludes lists
|
|
// and add output files to it.
|
|
//
|
|
if((Mode == SysdiffModeSnap) || (Mode == SysdiffModeDiff)) {
|
|
|
|
GetModuleFileName(NULL,Path,MAX_PATH);
|
|
*wcsrchr(Path,L'\\') = 0;
|
|
ConcatenatePaths(Path,L"sysdiff.inf",MAX_PATH,NULL);
|
|
|
|
if(!BuildExcludes(Path)) {
|
|
MessageOut(NULL,MSG_CANT_INIT_EXCLUDES,MB_ICONERROR | MB_OK | MB_TASKMODAL,Path);
|
|
PostMessage(MdiFrameWindow,WM_CLOSE,0,0);
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if(CmdLineLogFile) {
|
|
if(GetFullPathName(CmdLineLogFile,MAX_PATH,Path,&p)) {
|
|
AddFileToExclude(Path);
|
|
}
|
|
}
|
|
|
|
if(CmdLineSnapshotFile) {
|
|
if(GetFullPathName(CmdLineSnapshotFile,MAX_PATH,Path,&p)) {
|
|
AddFileToExclude(Path);
|
|
}
|
|
}
|
|
|
|
if(CmdLineDiffFile) {
|
|
if(GetFullPathName(CmdLineDiffFile,MAX_PATH,Path,&p)) {
|
|
AddFileToExclude(Path);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go do the real work.
|
|
//
|
|
switch(Mode) {
|
|
|
|
case SysdiffModeSnap:
|
|
|
|
d = SnapshotSystem(CmdLineSnapshotFile);
|
|
break;
|
|
|
|
case SysdiffModeDiff:
|
|
|
|
d = DiffSystem(CmdLineSnapshotFile,CmdLineDiffFile);
|
|
break;
|
|
|
|
case SysdiffModeApply:
|
|
|
|
d = ApplyDiff(CmdLineDiffFile);
|
|
break;
|
|
|
|
case SysdiffModeDump:
|
|
case SysdiffModeInf:
|
|
|
|
d = DumpDiff(CmdLineDiffFile,CmdLineDumpFile);
|
|
break;
|
|
}
|
|
if((Mode == SysdiffModeApply) || (Mode == SysdiffModeDump) || (Mode == SysdiffModeInf)) {
|
|
PostMessage(MdiFrameWindow,WM_CLOSE,0,0);
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
BOOL
|
|
ParseArgs(
|
|
IN int argc,
|
|
IN PCWSTR *argv
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse command line arguments. The command line is in the form
|
|
|
|
sysdiff /<mode> [/log:<log_file>] [options] [<snapshot_file>] [<sysdiff_file>]
|
|
|
|
Mode must be specified and is snap, diff, or apply.
|
|
|
|
<log_file> is optional and supplies a filename in which we will put
|
|
logging information.
|
|
|
|
[options] are various switches.
|
|
|
|
<snapshot_file> is required if mode is snap or diff.
|
|
|
|
<sysdiff_file> is required if mode is diff or apply.
|
|
|
|
Arguments:
|
|
|
|
argc - supplies argument count as given to main() by the CRT startup.
|
|
|
|
argv - supplies arguments as given to main() by the CRT startup.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCWSTR File1,File2;
|
|
|
|
//
|
|
// Skip program name
|
|
//
|
|
if(argc) {
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
//
|
|
// First arg must be /snap, /diff, or /apply
|
|
//
|
|
if(!argc || ((**argv != L'/') && (**argv != L'-'))) {
|
|
return(FALSE);
|
|
}
|
|
if(!lstrcmpi(&argv[0][1],L"snap")) {
|
|
|
|
Mode = SysdiffModeSnap;
|
|
|
|
} else {
|
|
|
|
if(!lstrcmpi(&argv[0][1],L"diff")) {
|
|
|
|
Mode = SysdiffModeDiff;
|
|
|
|
} else {
|
|
|
|
if(!lstrcmpi(&argv[0][1],L"apply")) {
|
|
|
|
Mode = SysdiffModeApply;
|
|
|
|
} else {
|
|
|
|
if(!lstrcmpi(&argv[0][1],L"dump")) {
|
|
|
|
Mode = SysdiffModeDump;
|
|
|
|
} else {
|
|
|
|
if(!lstrcmpi(&argv[0][1],L"inf")) {
|
|
|
|
Mode = SysdiffModeInf;
|
|
|
|
} else {
|
|
//
|
|
// Unknown mode
|
|
//
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip mode arg.
|
|
//
|
|
argc--;
|
|
argv++;
|
|
|
|
//
|
|
// See if we have a logfile argument.
|
|
//
|
|
if(argc && ((**argv == L'-') || (**argv == L'/')) && !_wcsnicmp(&argv[0][1],L"log:",4)) {
|
|
|
|
CmdLineLogFile = &argv[0][5];
|
|
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
//
|
|
// Handle switch args
|
|
//
|
|
while(argc && ((**argv == L'-') || (**argv == L'/'))) {
|
|
|
|
switch(UPPER(argv[0][1])) {
|
|
|
|
case L'C':
|
|
//
|
|
// Title for sysdiff package.
|
|
//
|
|
if((Mode == SysdiffModeDiff) && (argv[0][2] == L':')) {
|
|
PackageTitle = &argv[0][3];
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case L'D':
|
|
if((UPPER(argv[0][2]) == L'S')
|
|
&& (UPPER(argv[0][3]) == L'P')
|
|
&& !argv[0][4]
|
|
&& (Mode == SysdiffModeInf)) {
|
|
|
|
//
|
|
// Special DSP inf mode where we don't generate an OEM tree
|
|
// but instead move files in the %USERPROFILE% directory
|
|
// into the backup profile directory used by the rollback.restartable
|
|
// setup stuff.
|
|
//
|
|
DspMode = TRUE;
|
|
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
|
|
case L'M':
|
|
//
|
|
// Remap userprofile changes to Default User
|
|
//
|
|
if((Mode == SysdiffModeApply) || (Mode == SysdiffModeInf)) {
|
|
RemapProfileChanges = TRUE;
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case L'P':
|
|
//
|
|
// Ignore everything except %USERPROFILE% when diffing files/dirs
|
|
//
|
|
if((Mode == SysdiffModeSnap) || (Mode == SysdiffModeDiff)) {
|
|
UserProfileFilesOnly = TRUE;
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case L'U':
|
|
//
|
|
// Generate unicode text files.
|
|
//
|
|
UnicodeTextFiles = TRUE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Unknown switch.
|
|
//
|
|
return(FALSE);
|
|
}
|
|
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
//
|
|
// Get file args
|
|
//
|
|
File1 = File2 = NULL;
|
|
if(argc) {
|
|
File1 = *argv++;
|
|
argc--;
|
|
|
|
if(argc) {
|
|
File2 = *argv++;
|
|
argc--;
|
|
}
|
|
|
|
//
|
|
// Make sure there's no left-over.
|
|
//
|
|
if(argc) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure we have the relevent file args depending on the mode.
|
|
//
|
|
switch(Mode) {
|
|
|
|
case SysdiffModeSnap:
|
|
|
|
if(!File1 || File2) {
|
|
return(FALSE);
|
|
}
|
|
|
|
CmdLineSnapshotFile = File1;
|
|
break;
|
|
|
|
case SysdiffModeDiff:
|
|
|
|
if(!File1 || !File2) {
|
|
return(FALSE);
|
|
}
|
|
|
|
CmdLineSnapshotFile = File1;
|
|
CmdLineDiffFile = File2;
|
|
break;
|
|
|
|
case SysdiffModeApply:
|
|
|
|
if(!File1 || File2) {
|
|
return(FALSE);
|
|
}
|
|
|
|
CmdLineDiffFile = File1;
|
|
break;
|
|
|
|
case SysdiffModeDump:
|
|
case SysdiffModeInf:
|
|
|
|
if(!File1 || !File2) {
|
|
return(FALSE);
|
|
}
|
|
|
|
CmdLineDiffFile = File1;
|
|
CmdLineDumpFile = File2;
|
|
break;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
Usage(
|
|
VOID
|
|
)
|
|
{
|
|
MessageOut(NULL,MSG_USAGE,MB_ICONERROR | MB_OK | MB_TASKMODAL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitApp(
|
|
IN BOOL Init
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform miscellaneous app initialization or cleanup.
|
|
|
|
At init, this includes preloading certain strings, initializing window
|
|
classes, and creating the main app window.
|
|
|
|
At cleanup, those things are freed/torn down.
|
|
|
|
Arguments:
|
|
|
|
Init - boolean value indicating whether we are to initialize the app
|
|
or clean up resources it was using.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating outcome of initialization.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
|
|
if(Init) {
|
|
|
|
if(CancelEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) {
|
|
|
|
if(AppName = LoadAndDuplicateString(IDS_APPNAME)) {
|
|
|
|
if(InitUi(TRUE)) {
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
_MyFree(AppName);
|
|
}
|
|
|
|
CloseHandle(CancelEvent);
|
|
}
|
|
b = FALSE;
|
|
} else {
|
|
b = InitUi(FALSE);
|
|
_MyFree(AppName);
|
|
CloseHandle(CancelEvent);
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
DWORD
|
|
ValidateSnapshotOrDiffFile(
|
|
IN PSYSDIFF_FILE FileHeader,
|
|
IN DWORD FileSize,
|
|
IN SysdiffMode ExpectedFileType,
|
|
IN BOOL EndUserMessage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inspect a sysdiff snapshot or diff file to make sure it appears to
|
|
be what it says it is and is not obviously corrupt.
|
|
|
|
Arguments:
|
|
|
|
FileHeader - supplies pointer to sysdiff file header.
|
|
|
|
FileSize - supplies actual size of file on-disk.
|
|
|
|
ExpectedFileType - supplies type of file we expect this to be
|
|
(snapshot or diff).
|
|
|
|
EndUserMessage - if TRUE, use ultra-friendly messages. Otherwise
|
|
assume this is being run by an oem in their engineering dept
|
|
and use less friendly messages.
|
|
|
|
Return Value:
|
|
|
|
Win32 error indicating outcome. ERROR_INVALID_DATA means the file
|
|
is corrupt. User will have been informed why.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR Path[MAX_PATH];
|
|
|
|
//
|
|
// Check signature and file type.
|
|
//
|
|
if((FileHeader->Signature != SYSDIFF_SIGNATURE) || (FileHeader->Type != ExpectedFileType)) {
|
|
|
|
FileValidationError(MSG_NOT_SYSDIFF_FILE,EndUserMessage);
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Check version.
|
|
//
|
|
if(FileHeader->Version != SYSDIFF_VERSION) {
|
|
|
|
FileValidationError(MSG_WRONG_VERSION,EndUserMessage);
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Quick sanity check of certain key values. We take advantage of the fact that
|
|
// the structures within the union are identical.
|
|
//
|
|
if((FileHeader->TotalSize != FileSize)
|
|
|| (FileHeader->u.Snapshot.RegistrySnapOffset >= FileSize)
|
|
|| (FileHeader->u.Snapshot.DirAndFileSnapOffset >= FileSize)
|
|
|| (FileHeader->u.Snapshot.IniFileSnapOffset >= FileSize)) {
|
|
|
|
FileValidationError(MSG_FILE_CORRUPT,EndUserMessage);
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Make sure the current sysroot matches what is stored in the file header.
|
|
// Ditto for user profile root, unless we're remapping profile changes.
|
|
//
|
|
if(!RemapProfileChanges) {
|
|
ExpandEnvironmentStrings(L"%USERPROFILE%",Path,MAX_PATH);
|
|
if(FileHeader->UserProfileRoot[0] && lstrcmpi(FileHeader->UserProfileRoot,Path)) {
|
|
|
|
FileValidationError(MSG_PROFILE_MISMATCH,EndUserMessage);
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
}
|
|
|
|
GetWindowsDirectory(Path,MAX_PATH);
|
|
if(FileHeader->Sysroot[0] && lstrcmpi(FileHeader->Sysroot,Path)) {
|
|
|
|
FileValidationError(MSG_SYSROOT_MISMATCH,EndUserMessage);
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
VOID
|
|
FileValidationError(
|
|
IN DWORD MessageId,
|
|
IN BOOL Friendly
|
|
)
|
|
{
|
|
WCHAR Problem[2048];
|
|
|
|
if(Friendly) {
|
|
|
|
RetreiveMessageIntoBuffer(MessageId,Problem,2048);
|
|
MessageOut(MdiFrameWindow,MSG_FILE_VALIDATION_ERROR,MB_ICONSTOP | MB_OK | MB_TASKMODAL,Problem);
|
|
|
|
} else {
|
|
|
|
MessageOut(MdiFrameWindow,MessageId,MB_ICONSTOP | MB_OK | MB_TASKMODAL);
|
|
}
|
|
}
|