/*++ Copyright (c) 2000 Microsoft Corporation Module Name: SyncSystemAndSystem32.cpp Abstract: This shim takes a semi-colon delimited command line of filenames. At process termination, the DLL will parse the extract each filename from the command line and make sure that the file exists in both the System directory and System32 (if it exists in either). Some older apps expect certain DLLs to be in System when under NT they belong in System32 (and vice versa). History: 03/15/2000 markder Created 10/18/2000 a-larrsh Add Wild Card support for command line. --*/ #include "precomp.h" #include "CharVector.h" IMPLEMENT_SHIM_BEGIN(SyncSystemAndSystem32) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(CreateFileA) APIHOOK_ENUM_ENTRY(CreateFileW) APIHOOK_ENUM_ENTRY(CloseHandle) APIHOOK_ENUM_ENTRY(CopyFileA) APIHOOK_ENUM_ENTRY(CopyFileW) APIHOOK_ENUM_ENTRY(CopyFileExA) APIHOOK_ENUM_ENTRY(CopyFileExW) APIHOOK_ENUM_ENTRY(GetFileVersionInfoSizeA) APIHOOK_ENUM_ENTRY(GetFileVersionInfoSizeW) APIHOOK_ENUM_END int g_nrgFilesToSync = 0; CString * g_rgFilesToSync = NULL; CString * g_csSystem = NULL; // c:\windows\system CString * g_csSystem32 = NULL; // c:\windows\system32 //--------------------------------------------------------------------------------------- /*+ A vector of handles objects. Access to this list must be inside a critical section. --*/ class CachedHandleList : public VectorT { private: // Prevent copy CachedHandleList(const CachedHandleList & ); CachedHandleList & operator = (const CachedHandleList & ); private: static CachedHandleList * TheCachedHandleList; CRITICAL_SECTION TheCachedHandleListLock; inline CachedHandleList() {} inline ~CachedHandleList(); static CachedHandleList * GetLocked(); inline void Lock(); inline void Unlock(); int FindHandleIndex(HANDLE handle) const; public: // All access to this class is through these static interfaces. // The app has no direct access to the list, therefore cannot accidentally // leave the list locked or unlocked. // All operations are Atomic. static BOOL Init(); static BOOL FindHandle(HANDLE handle); static BOOL AddHandle(HANDLE handle); static void RemoveHandle(HANDLE handle); }; /*+ A static pointer to the one-and-only handle list. --*/ CachedHandleList * CachedHandleList::TheCachedHandleList = NULL; /*+ Init the class --*/ inline BOOL CachedHandleList::Init() { TheCachedHandleList = new CachedHandleList; if( TheCachedHandleList ) { return InitializeCriticalSectionAndSpinCount(&TheCachedHandleList->TheCachedHandleListLock, 0x80000000); } return FALSE; } /*+ Clean up, releasing all resources. --*/ inline CachedHandleList::~CachedHandleList() { DeleteCriticalSection(&TheCachedHandleListLock); } /*+ Enter the critical section --*/ inline void CachedHandleList::Lock() { EnterCriticalSection(&TheCachedHandleListLock); } /*+ Unlock the list --*/ inline void CachedHandleList::Unlock() { LeaveCriticalSection(&TheCachedHandleListLock); } /*+ Return a locked pointer to the list --*/ CachedHandleList * CachedHandleList::GetLocked() { if (TheCachedHandleList) TheCachedHandleList->Lock(); return TheCachedHandleList; } /*+ Search for the member in the list, return index or -1 --*/ int CachedHandleList::FindHandleIndex(HANDLE handle) const { for (int i = 0; i < Size(); ++i) { if (Get(i) == handle) return i; } return -1; } BOOL CachedHandleList::FindHandle(HANDLE handle) { BOOL bRet = FALSE; CachedHandleList * CachedHandleList = NULL; CachedHandleList = CachedHandleList::GetLocked(); if (!CachedHandleList) goto Exit; bRet = CachedHandleList->FindHandleIndex(handle) != -1; Exit: if( CachedHandleList ) { CachedHandleList->Unlock(); } return bRet; } /*+ Add this handle to the global list. --*/ BOOL CachedHandleList::AddHandle(HANDLE handle) { BOOL bRet = FALSE; int index = -1; CachedHandleList * CachedHandleList = NULL; CachedHandleList = CachedHandleList::GetLocked(); if (!CachedHandleList) { goto Exit; } index = CachedHandleList->FindHandleIndex(handle); if ( -1 == index ) { bRet = CachedHandleList->Append(handle); } Exit: // unlock the list if(CachedHandleList) { CachedHandleList->Unlock(); } return bRet; } /*+ Remove the handle from the global list --*/ void CachedHandleList::RemoveHandle(HANDLE handle) { int index = -1; CachedHandleList * CachedHandleList = NULL; // Get a pointer to the locked list CachedHandleList = CachedHandleList::GetLocked(); if (!CachedHandleList) { goto Exit; } // Look for our handle remove it. index = CachedHandleList->FindHandleIndex(handle); if (index >= 0) { CachedHandleList->Remove(index); } Exit: // unlock the list if( CachedHandleList ) { CachedHandleList->Unlock(); } } void SyncDir(const CString & csFileToSync, const CString & csSrc, const CString & csDest) { // Don't need our own excpetion handler, // this routine is only called inside one already. CString csSrcFile(csSrc); csSrcFile.AppendPath(csFileToSync); WIN32_FIND_DATAW FindFileData; HANDLE hFind = FindFirstFileW(csSrcFile, &FindFileData); if (hFind != INVALID_HANDLE_VALUE) { // csFileToSync might be a wildcard do { CString csDestFile(csDest); csDestFile.AppendPath(FindFileData.cFileName); if (GetFileAttributesW(csDestFile) == INVALID_FILE_ATTRIBUTES) { // In System but not System32, copy it over CopyFileW(csSrcFile, csDestFile, FALSE); DPFN( eDbgLevelInfo, "File found in %S but not in %S: %S", csSrc.Get(), csDest.Get(), FindFileData.cFileName); DPFN( eDbgLevelInfo, "Copied over"); } } while (FindNextFileW(hFind, &FindFileData)); FindClose(hFind); } } void SyncSystemAndSystem32(const CString & csFileToSync) { SyncDir(csFileToSync, *g_csSystem, *g_csSystem32); SyncDir(csFileToSync, *g_csSystem32, *g_csSystem); } void SyncAllFiles() { CSTRING_TRY { for (int nFileCount = 0; nFileCount < g_nrgFilesToSync; ++nFileCount) { SyncSystemAndSystem32(g_rgFilesToSync[nFileCount]); } } CSTRING_CATCH { // Do nothing } } BOOL IsFileToSync(const CString & csFileName) { CSTRING_TRY { CString csFilePart; csFileName.GetLastPathComponent(csFilePart); for (int i = 0; i < g_nrgFilesToSync; ++i) { if (csFilePart == g_rgFilesToSync[i]) { LOGN( eDbgLevelWarning, "File to sync detected: %S", csFileName.Get()); return TRUE; } } } CSTRING_CATCH { // Do nothing } return FALSE; } BOOL IsFileToSync(LPCSTR szFileName) { CSTRING_TRY { CString csFileName(szFileName); return IsFileToSync(csFileName); } CSTRING_CATCH { // Do nothing } return FALSE; } BOOL IsFileToSync(LPCWSTR szFileName) { CSTRING_TRY { CString csFileName(szFileName); return IsFileToSync(csFileName); } CSTRING_CATCH { // Do nothing } return FALSE; } HANDLE APIHOOK(CreateFileA)( LPCSTR lpFileName, // file name DWORD dwDesiredAccess, // access mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD DWORD dwCreationDisposition, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle to template file ) { HANDLE hRet; hRet = ORIGINAL_API(CreateFileA)( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if (hRet != INVALID_HANDLE_VALUE) { if (IsFileToSync(lpFileName)) { CachedHandleList::AddHandle(hRet); } } return hRet; } HANDLE APIHOOK(CreateFileW)( LPCWSTR lpFileName, // file name DWORD dwDesiredAccess, // access mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD DWORD dwCreationDisposition, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle to template file ) { HANDLE hRet; hRet = ORIGINAL_API(CreateFileW)( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if (hRet != INVALID_HANDLE_VALUE) { if (IsFileToSync(lpFileName)) { CachedHandleList::AddHandle(hRet); } } return hRet; } BOOL APIHOOK(CloseHandle)(HANDLE hObject) { if (CachedHandleList::FindHandle(hObject)) { CachedHandleList::RemoveHandle(hObject); SyncAllFiles(); } return ORIGINAL_API(CloseHandle)(hObject); } BOOL APIHOOK(CopyFileA)( LPCSTR lpExistingFileName, // name of an existing file LPCSTR lpNewFileName, // name of new file BOOL bFailIfExists // operation if file exists ) { BOOL bRet; bRet = ORIGINAL_API(CopyFileA)( lpExistingFileName, lpNewFileName, bFailIfExists); if (bRet) { if (IsFileToSync(lpNewFileName)) { SyncAllFiles(); } } return bRet; } BOOL APIHOOK(CopyFileW)( LPCWSTR lpExistingFileName, // name of an existing file LPCWSTR lpNewFileName, // name of new file BOOL bFailIfExists // operation if file exists ) { BOOL bRet; bRet = ORIGINAL_API(CopyFileW)( lpExistingFileName, lpNewFileName, bFailIfExists); if (bRet) { if (IsFileToSync(lpNewFileName)) { SyncAllFiles(); } } return bRet; } BOOL APIHOOK(CopyFileExA)( LPCSTR lpExistingFileName, // name of existing file LPCSTR lpNewFileName, // name of new file LPPROGRESS_ROUTINE lpProgressRoutine, // callback function LPVOID lpData, // callback parameter LPBOOL pbCancel, // cancel status DWORD dwCopyFlags // copy options ) { BOOL bRet; bRet = ORIGINAL_API(CopyFileExA)( lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags); if (bRet) { if (IsFileToSync(lpNewFileName)) { SyncAllFiles(); } } return bRet; } BOOL APIHOOK(CopyFileExW)( LPCWSTR lpExistingFileName, // name of existing file LPCWSTR lpNewFileName, // name of new file LPPROGRESS_ROUTINE lpProgressRoutine, // callback function LPVOID lpData, // callback parameter LPBOOL pbCancel, // cancel status DWORD dwCopyFlags // copy options ) { BOOL bRet; bRet = ORIGINAL_API(CopyFileExW)( lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags); if (bRet) { if (IsFileToSync(lpNewFileName)) { SyncAllFiles(); } } return bRet; } // // GetFileVersionInfoSize was added for the Madeline series. // There was a specific point at which the sync had to occur. // DWORD APIHOOK(GetFileVersionInfoSizeA)( LPSTR lptstrFilename, // file name LPDWORD lpdwHandle // set to zero ) { if (IsFileToSync(lptstrFilename)) { SyncAllFiles(); } return ORIGINAL_API(GetFileVersionInfoSizeA)(lptstrFilename, lpdwHandle); } DWORD APIHOOK(GetFileVersionInfoSizeW)( LPWSTR lptstrFilename, // file name LPDWORD lpdwHandle // set to zero ) { if (IsFileToSync(lptstrFilename)) { SyncAllFiles(); } return ORIGINAL_API(GetFileVersionInfoSizeW)(lptstrFilename, lpdwHandle); } BOOL ParseCommandLine() { CSTRING_TRY { CString csCl(COMMAND_LINE); CStringParser csParser(csCl, L";"); g_nrgFilesToSync = csParser.GetCount(); g_rgFilesToSync = csParser.ReleaseArgv(); // Create strings to %windir%\system and %windir%\system32 g_csSystem = new CString; if( g_csSystem ) { if( g_csSystem->GetWindowsDirectoryW() ) { g_csSystem->AppendPath(L"System"); g_csSystem32 = new CString; if( g_csSystem32 ) { if( g_csSystem32->GetWindowsDirectoryW() ) { g_csSystem32->AppendPath(L"System32"); return TRUE; } delete g_csSystem32; } } delete g_csSystem; } } CSTRING_CATCH { } return FALSE; } /*++ Register hooked functions --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { if (fdwReason == DLL_PROCESS_ATTACH) { if( !CachedHandleList::Init() || !ParseCommandLine() ) { return FALSE; } } else if (fdwReason == SHIM_STATIC_DLLS_INITIALIZED) { SyncAllFiles(); } else if (fdwReason == DLL_PROCESS_DETACH) { SyncAllFiles(); } return TRUE; } HOOK_BEGIN CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(KERNEL32.DLL, CreateFileA); APIHOOK_ENTRY(KERNEL32.DLL, CreateFileW); APIHOOK_ENTRY(KERNEL32.DLL, CloseHandle); APIHOOK_ENTRY(KERNEL32.DLL, CopyFileA); APIHOOK_ENTRY(KERNEL32.DLL, CopyFileW); APIHOOK_ENTRY(KERNEL32.DLL, CopyFileExA); APIHOOK_ENTRY(KERNEL32.DLL, CopyFileExW); APIHOOK_ENTRY(VERSION.DLL, GetFileVersionInfoSizeA); APIHOOK_ENTRY(VERSION.DLL, GetFileVersionInfoSizeW); HOOK_END IMPLEMENT_SHIM_END