#include "stdinc.h" // actually from dll\whistler directory /*----------------------------------------------------------------------------- Side X ("by") Side Test -----------------------------------------------------------------------------*/ #include "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "windows.h" #include "fusionlastwin32error.h" #include #include #include #include #pragma warning(push) #pragma warning(disable: 4511) #pragma warning(disable: 4512) #pragma warning(disable: 4663) #include #pragma warning(disable: 4663) #include #include #include #pragma warning(pop) #include "fusionbuffer.h" #include "fusion.h" #include "sxsasmname.h" #include "util.h" #include "filestream.cpp" #include "sxsapi.h" #include "fusiontrace.h" #include "cresourcestream.cpp" #include "cmemorystream.cpp" #include "wintrust.h" #include "softpub.h" #include "perfclocking.h" #include "strongname.h" #include "fusionversion.h" #include "fusionhash.h" #include "fusiondeque.h" BOOL MySetFilePointerEx( HANDLE File, // handle to file LARGE_INTEGER DistanceToMove, // bytes to move pointer PLARGE_INTEGER NewFilePointer, // new file pointer DWORD MoveMethod // starting point ) { LONG DistanceToMoveLow = static_cast(DistanceToMove.LowPart); LONG DistanceToMoveHigh = DistanceToMove.HighPart; DWORD NewPositionLow = SetFilePointer(File, DistanceToMoveLow, &DistanceToMoveHigh, MoveMethod); if (NewPositionLow == INVALID_SET_FILE_POINTER) { if (GetLastError() != NO_ERROR) return FALSE; } if (NewFilePointer != NULL) { NewFilePointer->LowPart = NewPositionLow; NewFilePointer->HighPart = DistanceToMoveHigh; } return TRUE; } BOOL FusionpAreWeInOSSetupMode(BOOL* pfIsInSetup) { *pfIsInSetup = FALSE; return TRUE; } void ReportFailure(const char szFormat[], ...); PCWSTR g_pszImage = L"mjgcopy"; CRITICAL_SECTION g_cs; ULONGLONG g_FilesCopied = 0; ULONGLONG g_BytesCopied = 0; ULONGLONG g_FilesLinked = 0; ULONGLONG g_DirectoriesCopied = 0; ULONGLONG g_CopiesSkipped = 0; ULONGLONG g_LinksSkipped = 0; ULONGLONG g_FileCopiesProcessed = 0; ULONGLONG g_FileLinksProcessed = 0; ULONGLONG g_DirScansProcessed = 0; ULONGLONG g_BytesToCopy = 0; bool g_fAnnounceDirectories = true; bool g_fAnnounceCopies = false; bool g_fAnnounceDeletes = true; bool g_fAnnounceLinks = true; bool g_fAnnounceSkips = false; bool g_fSilent = false; bool g_fShowProgress = true; HANDLE g_hIoCompletionPort = NULL; HANDLE g_hWorkItemDoneEvent = INVALID_HANDLE_VALUE; DWORD g_dwDestinationSectorsPerCluster = 0; DWORD g_dwDestinationBytesPerSector = 0; DWORD g_dwDestinationNumberOfFreeClusters = 0; DWORD g_dwDestinationTotalNumberOfClusters = 0; ULONG g_nThreads = 3; HANDLE g_rghThreads[32]; class CFileCopy; class CFileLink; class CDir; // BOOL ScanAndCopyDir(PCWSTR szSource, PCWSTR szDest); BOOL BuildDirList(const CBaseStringBuffer &rbuffSource, const CBaseStringBuffer &rbuffDestination); BOOL MakeDirectoryStructure(); BOOL QueueDirScans(); BOOL QueueFileCopies(); BOOL QueueFileLinks(); DWORD WINAPI WorkerThreadProc(LPVOID pvParameter); void ComputeTimeDeltas(const SYSTEMTIME &rstStart, const SYSTEMTIME &rstEnd, SYSTEMTIME &rstDelta); BOOL ProcessFileCopy(CFileCopy *pFileCopy, bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer); BOOL ProcessFileLink(CFileLink *pFileLink, bool &rfReQueue); BOOL ProcessDirScan(CDir *pDir, bool &rfReQueue); BOOL ResumeWorkerThreads(); BOOL SuspendWorkerThreads(); BOOL WaitForWorkersToComplete( ULONGLONG &rullCounter, ULONGLONG ullLimit, PCSTR pszOperationName ); BOOL MyCopyFile( BYTE *pBuffer, DWORD cbBuffer, PCWSTR lpExistingFileName, PCWSTR lpNewFileName, BOOL fFailIfExists, HANDLE &rhNewFileHandle ); class CEntry { private: CEntry(const CEntry&); // deliberately not implemented void operator=(const CEntry&); // deliberately not implemented public: CEntry() : m_cRetries(0) { } ~CEntry() { } virtual BOOL BaseDoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer) { LARGE_INTEGER liStart, liEnd; ::QueryPerformanceCounter(&liStart); BOOL fResult = this->DoYourThing(rfReQueue, pBuffer, cbBuffer); if (fResult && rfReQueue) { m_cRetries++; if (m_cRetries > 100) fResult = FALSE; } CSxsPreserveLastError ple; ::QueryPerformanceCounter(&liEnd); ple.Restore(); m_ullStart = static_cast(liStart.QuadPart); m_ullEnd = static_cast(liEnd.QuadPart); return fResult; } virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pbBuffer, DWORD cbBuffer) = 0; ULONGLONG m_ullStart, m_ullEnd; ULONG m_cRetries; }; class CDir : public CEntry { private: CDir(const CDir&); // deliberately not implemented void operator=(const CDir&); // deliberately not implemented public: CDir() { }; ~CDir() { }; BOOL Initialize(const CBaseStringBuffer &rbuffSource, const CBaseStringBuffer &rbuffDestination) { BOOL fSuccess = FALSE; if (!m_buffSource.Win32Assign(rbuffSource)) goto Exit; if (!m_buffDestination.Win32Assign(rbuffDestination)) goto Exit; fSuccess = TRUE; Exit: return fSuccess; } virtual BOOL DoYourThing(bool &rfReQueue, BYTE *, DWORD) { return ::ProcessDirScan(this, rfReQueue); } CStringBuffer m_buffSource; CStringBuffer m_buffDestination; CDequeLinkage m_linkage; }; class CFileBase : public CEntry { private: CFileBase(const CFileBase&); // deliberately not implemented void operator=(const CFileBase&); // deliberately not implemented public: CFileBase() : m_fDone(false) { } ~CFileBase() { } BOOL Initialize( CDir *pDir, PCWSTR pszFilename, FILETIME ftSourceCreationTime, FILETIME ftSourceLastAccessTime, FILETIME ftSourceLastWriteTime, ULONGLONG ullFileIndex, ULONGLONG cbSize ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); IFW32FALSE_EXIT(m_buffFilename.Win32Assign(pszFilename, wcslen(pszFilename))); m_ftSourceCreationTime = ftSourceCreationTime; m_ftSourceLastAccessTime = ftSourceLastAccessTime; m_ftSourceLastWriteTime = ftSourceLastWriteTime; m_ullFileIndex = ullFileIndex; m_cbSize = cbSize; m_pDir = pDir; fSuccess = TRUE; Exit: return fSuccess; } virtual BOOL GetSource(CBaseStringBuffer &rbuff) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); IFW32FALSE_EXIT(rbuff.Win32Assign(m_pDir->m_buffSource)); IFW32FALSE_EXIT(rbuff.Win32Append(m_buffFilename)); fSuccess = TRUE; Exit: return fSuccess; } virtual BOOL GetDestination(CBaseStringBuffer &rbuff) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); IFW32FALSE_EXIT(rbuff.Win32Assign(m_pDir->m_buffDestination)); IFW32FALSE_EXIT(rbuff.Win32Append(m_buffFilename)); fSuccess = TRUE; Exit: return fSuccess; } CStringBuffer m_buffFilename; FILETIME m_ftSourceCreationTime; FILETIME m_ftSourceLastAccessTime; FILETIME m_ftSourceLastWriteTime; ULONGLONG m_ullFileIndex; ULONGLONG m_cbSize; CDir *m_pDir; bool m_fSkipped; // used to avoid skewed statistics about number of bytes copied/sec etc. bool m_fDone; CDequeLinkage m_linkage; }; class CFileCopy : public CFileBase { public: CFileCopy() { } ~CFileCopy() { } virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer) { return ::ProcessFileCopy(this, rfReQueue, pBuffer, cbBuffer); } static int __cdecl QSortBySize(const void *param1, const void *param2) { CFileCopy **pp1 = (CFileCopy **) param1; CFileCopy **pp2 = (CFileCopy **) param2; CFileCopy *p1 = *pp1; CFileCopy *p2 = *pp2; int iRet = 0; if (p1->m_fSkipped) { if (p2->m_fSkipped) { if (p1->m_cbSize < p2->m_cbSize) iRet = 1; else if (p1->m_cbSize > p2->m_cbSize) iRet = -1; else { if (p1->m_ullFileIndex < p2->m_ullFileIndex) iRet = -1; else iRet = 1; } } else iRet = -1; } else { if (p2->m_fSkipped) iRet = 1; else { if (p1->m_cbSize < p2->m_cbSize) iRet = 1; else if (p1->m_cbSize > p2->m_cbSize) iRet = -1; else { if (p1->m_ullFileIndex < p2->m_ullFileIndex) iRet = -1; else iRet = 1; } } } return iRet; } private: CFileCopy(const CFileCopy &r); void operator =(const CFileCopy &r); }; class CFileLink : public CFileBase { public: CFileLink() { } ~CFileLink() { } virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer) { return ::ProcessFileLink(this, rfReQueue); } BOOL GetSource(CBaseStringBuffer &rbuff); private: CFileLink(const CFileLink &r); void operator =(const CFileLink &r); }; CDeque *g_pDirs = NULL; CDir **g_prgpDirs = NULL; CDeque *g_pFileCopies = NULL; CFileCopy **g_prgpFileCopies = NULL; CDeque *g_pFileLinks = NULL; CFileLink **g_prgpFileLinks = NULL; class CFileIdHashHelper : public CHashTableHelper { private: CFileIdHashHelper(const CFileIdHashHelper&); // deliberately not implemented void operator=(const CFileIdHashHelper&); // deliberately not implemented public: static BOOL HashKey(ULONGLONG keyin, ULONG &rulPseudoKey) { rulPseudoKey = static_cast(keyin); return TRUE; } static BOOL CompareKey(ULONGLONG keyin, const ULONGLONG &rtkeystored, bool &rfMatch) { rfMatch = keyin == rtkeystored; return TRUE; } static VOID PreInitializeKey(ULONGLONG &rtkeystored) { rtkeystored = 0; } static VOID PreInitializeValue(CFileCopy *&rtvaluestored) { rtvaluestored = NULL; } static BOOL InitializeKey(ULONGLONG keyin, ULONGLONG &rtkeystored) { rtkeystored = keyin; return TRUE; } static BOOL InitializeValue(CFileCopy *vin, CFileCopy *&rvstored) { rvstored = vin; return TRUE; } static BOOL UpdateValue(CFileCopy *vin, CFileCopy *&rvstored) { rvstored = vin; return TRUE; } static VOID FinalizeKey(ULONGLONG &rtkeystored) { } static VOID FinalizeValue(CFileCopy *&rvstored) { rvstored = NULL; } }; class CFileIdHashTable : public CHashTable { private: CFileIdHashTable(const CFileIdHashTable&); // deliberately not implemented void operator=(const CFileIdHashTable&); // deliberately not implemented public: CFileIdHashTable() { } ~CFileIdHashTable() { } }; CFileIdHashTable *g_pFiles = NULL; FILE *g_pLogFile = NULL; BOOL CFileLink::GetSource(CBaseStringBuffer &rbuff) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); CStringBuffer *pbuff = NULL; CFileCopy **ppFileCopy = NULL; if (!g_pFiles->Find(m_ullFileIndex, ppFileCopy)) { ::ReportFailure("Finding file index %I64u in the file table failed.\n"); goto Exit; } IFW32FALSE_EXIT((*ppFileCopy)->GetDestination(rbuff)); FN_EPILOG } extern "C" int __cdecl wmain(int argc, wchar_t** argv) { int iReturnStatus = EXIT_FAILURE; SYSTEMTIME stStart, stAfterScan, stAfterDirCreation, stAfterCopies, stAfterLinks, stEnd; ULONG i; CStringBuffer buffSource; CStringBuffer buffDestination; int iSource = 0; int iDestination = 0; SYSTEMTIME stAfterScanDelta, stAfterDirCreationDelta, stAfterCopiesDelta, stAfterLinksDelta, stEndDelta; ULONGLONG ullTemp; int iArg; // DWORD dwRetVal; WCHAR rgwchSourceVolumePath[MAX_PATH]; WCHAR rgwchSourceVolumeName[MAX_PATH]; DWORD dwSourceVolumeSerialNumber; DWORD dwSourceMaximumComponentLength; DWORD dwSourceFileSystemFlags; WCHAR rgwchSourceFileSystemNameBuffer[MAX_PATH]; WCHAR rgwchSourceRemoteName[MAX_PATH]; DWORD dwSourceRemoteNameLength = NUMBER_OF(rgwchSourceRemoteName); UINT uiSourceDriveType; WCHAR rgwchDestinationVolumePath[MAX_PATH]; WCHAR rgwchDestinationVolumeName[MAX_PATH]; DWORD dwDestinationVolumeSerialNumber; DWORD dwDestinationMaximumComponentLength; DWORD dwDestinationFileSystemFlags; WCHAR rgwchDestinationFileSystemNameBuffer[MAX_PATH]; WCHAR rgwchDestinationRemoteName[MAX_PATH]; DWORD dwDestinationRemoteNameLength = NUMBER_OF(rgwchDestinationRemoteName); UINT uiDestinationDriveType; if (!::FusionpInitializeHeap(NULL)) goto Exit; iArg = 1; while (iArg < argc) { PCWSTR arg = argv[iArg]; // Let's see if we see some switches... if ((arg[0] == L'-') || (arg[0] == L'/')) { arg++; if ((_wcsicmp(arg, L"threads") == 0) || (_wcsicmp(arg, L"t") == 0)) { PWSTR pszDummy; iArg++; if (iArg >= argc) break; g_nThreads = wcstol(argv[iArg], &pszDummy, 10); if (g_nThreads < 1) g_nThreads = 1; if (g_nThreads > RTL_NUMBER_OF(g_rghThreads)) g_nThreads = RTL_NUMBER_OF(g_rghThreads); iArg++; continue; } else if ((_wcsicmp(arg, L"quiet") == 0) || (_wcsicmp(arg, L"q") == 0)) { g_fSilent = true; iArg++; continue; } else if (_wcsicmp(arg, L"logfile") == 0) { iArg++; if (iArg >= argc) break; g_pLogFile = ::_wfopen(argv[iArg], L"w+"); if (g_pLogFile == NULL) { ::perror("Error opening logfile"); goto Exit; } iArg++; } } // This must be it! We hope; there should be two things left if ((iArg + 2) != argc) break; iSource = iArg; iDestination = iArg + 1; break; } if (iSource == 0) { fprintf(stderr, "%ls: usage:\n" " %ls [-threads n] [-quiet] \n", argv[0], argv[0]); goto Exit; } // Abuse these buffers for debugging purposes... { HANDLE h; if (!::GetLogicalDriveStringsW(NUMBER_OF(rgwchSourceVolumePath), rgwchSourceVolumePath)) { ::ReportFailure("GetLogicalDriveStringsW failed\n"); goto Exit; } h = ::FindFirstVolumeW(rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath)); if (h == INVALID_HANDLE_VALUE) { ::ReportFailure("FindFirstVolumeW failed\n"); goto Exit; } for (;;) { DWORD cchReturnLength; if (!::GetVolumePathNamesForVolumeNameW(rgwchSourceVolumePath, rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath), &cchReturnLength)) { ::ReportFailure("GetVolumePathNamesForVolumeNameW failed\n"); goto Exit; } if (!::FindNextVolumeW(h, rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath))) { const DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_NO_MORE_FILES) { ::ReportFailure("FindNextVolumeW failed\n"); goto Exit; } break; } } ::FindVolumeClose(h); } if (!::GetVolumePathNameW(argv[iSource], rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath))) { ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed\n", argv[iSource]); goto Exit; } uiSourceDriveType = ::GetDriveTypeW(rgwchSourceVolumePath); if (!::GetVolumePathNameW(argv[iDestination], rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath))) { ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed\n", argv[iDestination]); goto Exit; } uiDestinationDriveType = ::GetDriveTypeW(rgwchDestinationVolumePath); if (!::GetVolumeInformationW( rgwchSourceVolumePath, rgwchSourceVolumeName, NUMBER_OF(rgwchSourceVolumeName), &dwSourceVolumeSerialNumber, &dwSourceMaximumComponentLength, &dwSourceFileSystemFlags, rgwchSourceFileSystemNameBuffer, NUMBER_OF(rgwchSourceFileSystemNameBuffer))) { ::ReportFailure("GetVolumeInformation(L\"%ls\", ...) failed\n", rgwchSourceVolumePath); goto Exit; } if (!::GetVolumeInformationW( rgwchDestinationVolumePath, rgwchDestinationVolumeName, NUMBER_OF(rgwchDestinationVolumeName), &dwDestinationVolumeSerialNumber, &dwDestinationMaximumComponentLength, &dwDestinationFileSystemFlags, rgwchDestinationFileSystemNameBuffer, NUMBER_OF(rgwchDestinationFileSystemNameBuffer))) { ::ReportFailure("GetVolumeInformation(L\"%ls\", ...) failed\n", rgwchDestinationVolumePath); goto Exit; } if (!::GetDiskFreeSpaceW( rgwchDestinationVolumePath, &g_dwDestinationSectorsPerCluster, &g_dwDestinationBytesPerSector, &g_dwDestinationNumberOfFreeClusters, &g_dwDestinationTotalNumberOfClusters)) { ::ReportFailure("GetDiskFreeSpaceW(L\"%ls\", ...) failed\n", rgwchDestinationVolumePath); goto Exit; } #if 0 if ((dwRetVal = ::WNetGetConnectionW(rgwchSourceVolumePath, rgwchSourceRemoteName, &dwSourceRemoteNameLength)) != NO_ERROR) { ::SetLastError(dwRetVal); ::ReportFailure("WNetGetConnection(L\"%ls\", ...) failed\n", rgwchSourceVolumePath); goto Exit; } #endif // 0 InitializeCriticalSection(&g_cs); g_pFiles = new CFileIdHashTable; g_pDirs = new CDeque; g_pFileCopies = new CDeque; g_pFileLinks = new CDeque; g_pszImage = wcsrchr(argv[0], L'\\'); if (g_pszImage == NULL) g_pszImage = argv[0]; else g_pszImage++; g_hIoCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, g_nThreads); if (g_hIoCompletionPort == NULL) { ::ReportFailure("Creating I/O Completion Port with %lu concurrent threads failed.\n", g_nThreads); goto Exit; } g_hWorkItemDoneEvent = ::CreateEventW(NULL, FALSE, FALSE, NULL); if (g_hWorkItemDoneEvent == NULL) { ::ReportFailure("Creating the file copied event failed.\n"); goto Exit; } for (i=0; iGetEntryCount()); if (!::QueueDirScans()) goto Exit; if (!::WaitForWorkersToComplete(g_DirScansProcessed, g_pDirs->GetEntryCount(), "Directory scans")) goto Exit; ::GetSystemTime(&stAfterScan); if (!g_fSilent) { printf("%ls: Copying %Iu files (%I64u bytes)\n", g_pszImage, g_pFileCopies->GetEntryCount(), g_BytesToCopy); printf("%ls: Linking %Iu files\n", g_pszImage, g_pFileLinks->GetEntryCount()); } if (!::MakeDirectoryStructure()) goto Exit; if (!g_fSilent) printf("%ls: Created %Iu directories.\n", g_pszImage, g_pDirs->GetEntryCount()); ::GetSystemTime(&stAfterDirCreation); if (!::QueueFileCopies()) goto Exit; if (!::WaitForWorkersToComplete(g_FileCopiesProcessed, g_pFileCopies->GetEntryCount(), "File copies")) goto Exit; ::GetSystemTime(&stAfterCopies); ::qsort(g_prgpFileCopies, g_pFileCopies->GetEntryCount(), sizeof(CFileCopy *), &CFileCopy::QSortBySize); if (g_pLogFile != NULL) { LARGE_INTEGER liFreq; ULONGLONG ullFreqDiv100; CStringBuffer buffDestination; ::QueryPerformanceFrequency(&liFreq); ullFreqDiv100 = (liFreq.QuadPart / 100); for (i=0; iGetEntryCount(); i++) { ULONGLONG diff = g_prgpFileCopies[i]->m_ullEnd - g_prgpFileCopies[i]->m_ullStart; diff = diff / ullFreqDiv100; if (!g_prgpFileCopies[i]->GetDestination(buffDestination)) goto Exit; fprintf(g_pLogFile, "%ls,%Iu,%Iu\n", static_cast(buffDestination), diff, g_prgpFileCopies[i]->m_cbSize); } } if (!::QueueFileLinks()) goto Exit; if (!::WaitForWorkersToComplete(g_FileLinksProcessed, g_pFileLinks->GetEntryCount(), "File links")) goto Exit; ::GetSystemTime(&stAfterLinks); ::GetSystemTime(&stEnd); ::ComputeTimeDeltas(stStart, stAfterScan, stAfterScanDelta); ::ComputeTimeDeltas(stAfterScan, stAfterDirCreation, stAfterDirCreationDelta); ::ComputeTimeDeltas(stAfterDirCreation, stAfterCopies, stAfterCopiesDelta); ::ComputeTimeDeltas(stAfterCopies, stAfterLinks, stAfterLinksDelta); ::ComputeTimeDeltas(stStart, stEnd, stEndDelta); printf( "%ls: Statistics:\n" " Directories Copied: %I64u\n" " Files Copied: %I64u\n" " Bytes Copied: %I64u\n" " Files Linked: %I64u\n" " Copies Skipped: %I64u\n" " Links Skipped: %I64u\n", g_pszImage, g_DirectoriesCopied, g_FilesCopied, g_BytesCopied, g_FilesLinked, g_CopiesSkipped, g_LinksSkipped); printf( " Times:\n" " Scan: %u:%02u:%02u.%03u\n" " Directory Creation: %u:%02u:%02u.%03u\n" " Copying Files: %u:%02u:%02u.%03u\n" " Linking Files: %u:%02u:%02u.%03u\n" " Total: %u:%02u:%02u.%03u\n", stAfterScanDelta.wHour, stAfterScanDelta.wMinute, stAfterScanDelta.wSecond, stAfterScanDelta.wMilliseconds, stAfterDirCreationDelta.wHour, stAfterDirCreationDelta.wMinute, stAfterDirCreationDelta.wSecond, stAfterDirCreationDelta.wMilliseconds, stAfterCopiesDelta.wHour, stAfterCopiesDelta.wMinute, stAfterCopiesDelta.wSecond, stAfterCopiesDelta.wMilliseconds, stAfterLinksDelta.wHour, stAfterLinksDelta.wMinute, stAfterLinksDelta.wSecond, stAfterLinksDelta.wMilliseconds, stEndDelta.wHour, stEndDelta.wMinute, stEndDelta.wSecond, stEndDelta.wMilliseconds ); ullTemp = (((((stAfterCopiesDelta.wHour * 60) + stAfterCopiesDelta.wMinute) * 60) + stAfterCopiesDelta.wSecond) * 1000) + stAfterCopiesDelta.wMilliseconds; if (ullTemp != 0) { ULONGLONG ullFilesPerMS = ((g_FilesCopied * 1000000ui64) / ullTemp); ULONGLONG ullBytesPerMS = ((g_BytesCopied * 1000000ui64) / ullTemp); printf( " Files copied per second: %I64u.%03u\n" " Bytes copied per second: %I64u.%03u\n", static_cast(ullFilesPerMS / 1000ui64), static_cast(ullFilesPerMS % 1000ui64), static_cast(ullBytesPerMS / 1000ui64), static_cast(ullBytesPerMS % 1000ui64)); #if 0 static_cast((g_FilesCopied / ullTemp) * 1000ui64), static_cast((g_FilesCopied / ullTemp) % 1000), static_cast((g_BytesCopied / ullTemp) * 1000ui64), static_cast((g_BytesCopied / ullTemp) % 1000)); #endif } ullTemp = (((stAfterLinksDelta.wHour * 60) + stAfterLinksDelta.wMinute) * 60) + stAfterLinksDelta.wSecond; if (ullTemp != 0) { printf( " Files linked per second: %I64u\n", static_cast(g_FilesLinked / ullTemp)); } ullTemp = (((stEndDelta.wHour * 60) + stEndDelta.wMinute) * 60) + stEndDelta.wSecond; if (ullTemp != 0) { printf( " Overall files per second: %I64u\n", static_cast((g_FilesCopied + g_CopiesSkipped + g_FilesLinked + g_LinksSkipped + g_pDirs->GetEntryCount()) / ullTemp)); } iReturnStatus = EXIT_SUCCESS; Exit: // Wake the children; process termination doesn't seem to work if we don't. ::ResumeWorkerThreads(); if (g_pLogFile != NULL) { fflush(g_pLogFile); fclose(g_pLogFile); } return iReturnStatus; } BOOL ResumeWorkerThreads() { BOOL fSuccess = FALSE; ULONG i; for (i=0; iInitialize(buffSrc, buffDst)) goto Exit; g_pDirs->AddToTail(pDir); pDir = NULL; if ((g_pDirs->GetEntryCount() % 50) == 0) { if (!g_fSilent) printf("%ls: Found %Iu directories...\n", g_pszImage, g_pDirs->GetEntryCount()); } if (!buffSrcWildcard.Win32Assign(buffSrc)) goto Exit; if (!buffSrcWildcard.Win32Append(L"*", 1)) goto Exit; if ((hFind = ::FindFirstFileExW( buffSrcWildcard, FindExInfoStandard, &wfd, FindExSearchLimitToDirectories, NULL, 0)) == INVALID_HANDLE_VALUE) { const DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_NO_MORE_FILES) { ::ReportFailure("FindFirstFileW(L\"%ls\", ...) failed.\n", static_cast(buffSrcWildcard)); goto Exit; } } for (;;) { if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (wcscmp(wfd.cFileName, L".") != 0) && (wcscmp(wfd.cFileName, L"..") != 0)) { buffSrc.Left(cchSrc); buffDst.Left(cchDst); if (!buffSrc.Win32Append(wfd.cFileName, wcslen(wfd.cFileName))) goto Exit; if (!buffDst.Win32Append(wfd.cFileName, wcslen(wfd.cFileName))) goto Exit; if (!::BuildDirList(buffSrc, buffDst)) goto Exit; } if (!::FindNextFileW(hFind, &wfd)) { if (::GetLastError() == ERROR_NO_MORE_FILES) break; ::ReportFailure("FindNextFileW() failed\n"); goto Exit; } } fSuccess = TRUE; Exit: if (hFind != INVALID_HANDLE_VALUE) { CSxsPreserveLastError ple; ::FindClose(hFind); ple.Restore(); } if (pDir != NULL) delete pDir; return fSuccess; } BOOL BuildDirFileList( CSxsLockCriticalSection &rlcs, CDir *pDir ) { PCWSTR szSrc = pDir->m_buffSource; BOOL fSuccess = FALSE; DWORD dwFileAttributes; CStringBuffer buffSrc; CStringBuffer buffSrcWildcard; CStringBuffer buffDst; WIN32_FIND_DATAW wfd; HANDLE hFind = INVALID_HANDLE_VALUE; HANDLE hFile = INVALID_HANDLE_VALUE; SIZE_T cchSrc, cchDst; CFileLink *pFileLink = NULL; CFileCopy *pFileCopy = NULL; dwFileAttributes = ::GetFileAttributesW(pDir->m_buffSource); if (dwFileAttributes == ((DWORD) -1)) { ::ReportFailure("GetFileAttributesW() on the source \"%ls\" failed", szSrc); goto Exit; } if (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { ::SetLastError(ERROR_INVALID_PARAMETER); ::ReportFailure("Source directory \"%ls\" is not a directory!\n", szSrc); goto Exit; } if (!buffSrc.Win32Assign(pDir->m_buffSource)) goto Exit; if (!buffSrc.Win32EnsureTrailingPathSeparator()) goto Exit; if (!buffDst.Win32Assign(pDir->m_buffDestination)) goto Exit; if (!buffDst.Win32EnsureTrailingPathSeparator()) goto Exit; cchSrc = buffSrc.Cch(); cchDst = buffDst.Cch(); if (!buffSrcWildcard.Win32Assign(buffSrc)) goto Exit; if (!buffSrcWildcard.Win32Append(L"*", 1)) goto Exit; if ((hFind = ::FindFirstFileW(buffSrcWildcard, &wfd)) == INVALID_HANDLE_VALUE) { const DWORD dwLastError = ::GetLastError(); if (::GetLastError() != ERROR_NO_MORE_FILES) { ::ReportFailure("FindFirstFileW(L\"%ls\", ...) failed\n", static_cast(buffSrcWildcard)); goto Exit; } } for (;;) { if ((wcscmp(wfd.cFileName, L".") != 0) && (wcscmp(wfd.cFileName, L"..") != 0)) { buffSrc.Left(cchSrc); buffDst.Left(cchDst); if (!buffSrc.Win32Append(wfd.cFileName, wcslen(wfd.cFileName))) goto Exit; if (!buffDst.Win32Append(wfd.cFileName, wcslen(wfd.cFileName))) goto Exit; dwFileAttributes = ::GetFileAttributesW(buffSrc); if (dwFileAttributes == ((DWORD) -1)) { ::ReportFailure("GetFileAttribuesW(L\"%ls\") failed\n", static_cast(buffSrc)); goto Exit; } if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { BY_HANDLE_FILE_INFORMATION bhfi; ULONGLONG ullFileIndex; ULONGLONG ullFileSize; CFileCopy **ppFileCopy = NULL; hFile = ::CreateFileW(buffSrc, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { ::ReportFailure("CreateFileW(L\"%ls\", ...) failed\n", static_cast(buffSrc)); goto Exit; } if (!::GetFileInformationByHandle(hFile, &bhfi)) { ::ReportFailure("Failed to GetFileInformationByHandle(%p, ...) on file \"%ls\"\n", hFile, static_cast(buffSrc)); goto Exit; } ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; ullFileIndex = (((ULONGLONG) bhfi.nFileIndexHigh) << 32) | ((ULONGLONG) bhfi.nFileIndexLow); ullFileSize = (((ULONGLONG) bhfi.nFileSizeHigh) << 32) | ((ULONGLONG) bhfi.nFileSizeLow); g_BytesToCopy += ullFileSize; if (!rlcs.Lock()) { ::ReportFailure("Failed to lock global critical section"); goto Exit; } if (!g_pFiles->Find(ullFileIndex, ppFileCopy)) { ::ReportFailure("Finding file index %I64u in file table failed.\n", ullFileIndex); goto Exit; } if (ppFileCopy != NULL) { pFileLink = new CFileLink; if (pFileLink == NULL) { ::SetLastError(ERROR_OUTOFMEMORY); goto Exit; } if (!pFileLink->Initialize( pDir, wfd.cFileName, bhfi.ftCreationTime, bhfi.ftLastAccessTime, bhfi.ftLastWriteTime, ullFileIndex, ullFileSize)) goto Exit; g_pFileLinks->AddToTail(pFileLink); pFileLink = NULL; } else { pFileCopy = new CFileCopy; if (pFileCopy == NULL) { ::SetLastError(ERROR_OUTOFMEMORY); ::ReportFailure("Allocating new CFileCopy object failed.\n"); goto Exit; } if (!pFileCopy->Initialize( pDir, wfd.cFileName, bhfi.ftCreationTime, bhfi.ftLastAccessTime, bhfi.ftLastWriteTime, ullFileIndex, ullFileSize)) goto Exit; if (!g_pFiles->Insert(ullFileIndex, pFileCopy)) goto Exit; g_pFileCopies->AddToTail(pFileCopy); pFileCopy = NULL; } rlcs.Unlock(); } } if (!::FindNextFileW(hFind, &wfd)) { const DWORD dwLastError = ::GetLastError(); if (dwLastError == ERROR_NO_MORE_FILES) break; ::ReportFailure("FindNextFileW() call failed\n"); goto Exit; } } fSuccess = TRUE; Exit: if (hFind != INVALID_HANDLE_VALUE) { CSxsPreserveLastError ple; ::FindClose(hFind); ple.Restore(); } if (pFileLink != NULL) delete pFileLink; if (pFileCopy != NULL) delete pFileCopy; return fSuccess; } BOOL MakeDirectoryStructure() { BOOL fSuccess = FALSE; CDequeIterator dirIter; dirIter.Rebind(g_pDirs); for (dirIter.Reset(); dirIter.More(); dirIter.Next()) { if (!::CreateDirectoryW(dirIter->m_buffDestination, NULL)) { const DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_ALREADY_EXISTS) { ::ReportFailure("Unable to create directory \"%ls\"", static_cast(dirIter->m_buffDestination)); goto Exit; } } } fSuccess = TRUE; Exit: return fSuccess; } void ReportFailure( const char szFormat[], ... ) { const DWORD dwLastError = ::GetLastError(); va_list ap; char rgchBuffer[4096]; WCHAR rgchWin32Error[4096]; va_start(ap, szFormat); _vsnprintf(rgchBuffer, sizeof(rgchBuffer) / sizeof(rgchBuffer[0]), szFormat, ap); va_end(ap); if (!::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, rgchWin32Error, NUMBER_OF(rgchWin32Error), &ap)) { const DWORD dwLastError2 = ::GetLastError(); _snwprintf(rgchWin32Error, sizeof(rgchWin32Error) / sizeof(rgchWin32Error[0]), L"Error formatting Win32 error %lu\nError from FormatMessage is %lu", dwLastError, dwLastError2); } fprintf(stderr, "%ls: %s\n%ls\n", g_pszImage, rgchBuffer, rgchWin32Error); } DWORD WINAPI WorkerThreadProc( LPVOID pvParameter ) { ULONG nThread = (ULONG)(ULONG_PTR)pvParameter; DWORD dwReturnValue = 0; BYTE *pBuffer = NULL; DWORD cbBuffer; cbBuffer = 8192 * 32; pBuffer = (BYTE *) ::VirtualAlloc(NULL, cbBuffer, MEM_COMMIT, PAGE_READWRITE); if (pBuffer == NULL) { ReportFailure("VirtualAlloc() for thread %lu failed.\n", nThread); goto Exit; } for (;;) { DWORD nBytes = 0; ULONG_PTR ulpCompletionKey = 0; LPOVERLAPPED lpo = NULL; CEntry *pEntry = NULL; bool fReQueue = false; if (!::GetQueuedCompletionStatus(g_hIoCompletionPort, &nBytes, &ulpCompletionKey, &lpo, INFINITE)) { ReportFailure("Thread %lu failed call to GetQueuedCompletionStatus()\n", nThread); dwReturnValue = ::GetLastError(); goto Exit; } pEntry = (CEntry *) lpo; DoItAgain: if (!pEntry->BaseDoYourThing(fReQueue, pBuffer, cbBuffer)) goto Exit; if (fReQueue) { if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, NULL, lpo)) { ReportFailure("Thread %lu failed to requeue item; retrying directly.\n", nThread); goto DoItAgain; } } ::SetEvent(g_hWorkItemDoneEvent); } Exit: // Heavy handed but what else can we do? ::ExitProcess(dwReturnValue); return dwReturnValue; } BOOL QueueDirScans() { BOOL fSuccess = FALSE; SIZE_T i; g_prgpDirs = new (CDir *[g_pDirs->GetEntryCount()]); CDequeIterator dirIter; dirIter.Rebind(g_pDirs); for (dirIter.Reset(), i=0; dirIter.More(); dirIter.Next(), i++) { g_prgpDirs[i] = dirIter; if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) dirIter.Current())) { ReportFailure("Failed to queue dir scan\n"); goto Exit; } } fSuccess = TRUE; Exit: return fSuccess; } BOOL QueueFileCopies() { BOOL fSuccess = FALSE; SIZE_T i; FN_TRACE_WIN32(fSuccess); CDequeIterator fileIter; g_prgpFileCopies = new (CFileCopy *[g_pFileCopies->GetEntryCount()]); fileIter.Rebind(g_pFileCopies); i=0; for (fileIter.Reset(); fileIter.More(); fileIter.Next()) { if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) fileIter.Current())) { ReportFailure("Failed to queue file copy\n"); goto Exit; } g_prgpFileCopies[i++] = fileIter; } fSuccess = TRUE; Exit: return fSuccess; } BOOL QueueFileLinks() { BOOL fSuccess = FALSE; SIZE_T i; CDequeIterator fileIter; g_prgpFileLinks = new (CFileLink *[g_pFileLinks->GetEntryCount()]); fileIter.Rebind(g_pFileLinks); i=0; for (fileIter.Reset(); fileIter.More(); fileIter.Next()) { if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) fileIter.Current())) { ReportFailure("Failed to queue file link\n"); goto Exit; } g_prgpFileLinks[i++] = fileIter; } fSuccess = TRUE; Exit: return fSuccess; } BOOL ProcessDirScan( CDir *pDir, bool &rfReQueue ) { BOOL fSuccess = FALSE; CSxsLockCriticalSection l(g_cs); rfReQueue = false; if (!::BuildDirFileList(l, pDir)) goto Exit; if (!l.Lock()) { ReportFailure("Failed to lock global critical section"); goto Exit; } g_DirScansProcessed++; l.Unlock(); fSuccess = TRUE; Exit: return fSuccess; } BOOL ProcessFileCopy( CFileCopy *pFile, bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); CSxsLockCriticalSection l(g_cs); bool fDoCopy = true; DWORD dwFileAttributes; HANDLE hFile = INVALID_HANDLE_VALUE; CStringBuffer buffSource, buffDestination; rfReQueue = false; // If we're about to copy it, it's worth taking a look at the target to see if it's more-or-less // out of date. WIN32_FILE_ATTRIBUTE_DATA wfad; IFW32FALSE_EXIT(pFile->GetSource(buffSource)); IFW32FALSE_EXIT(pFile->GetDestination(buffDestination)); if (!::GetFileAttributesExW(buffDestination, GetFileExInfoStandard, &wfad)) { const DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_FILE_NOT_FOUND) { ::ReportFailure("Error opening target file \"%ls\"\n", static_cast(buffDestination)); goto Exit; } wfad.dwFileAttributes = ((DWORD) -1); } else { ULONGLONG cbFileSize = (wfad.nFileSizeHigh << 32) | wfad.nFileSizeLow; ASSERT_NTC(cbFileSize >= wfad.nFileSizeLow); if ((cbFileSize == pFile->m_cbSize) && (wfad.ftCreationTime == pFile->m_ftSourceCreationTime)) { pFile->m_fSkipped = true; fDoCopy = false; } } if (fDoCopy) { bool fSetTimestamp = true; bool fClearedReadOnly = false; // Destructively clear the read-only bit if th destination file exists and is read-only. dwFileAttributes = wfad.dwFileAttributes; // We already filtered out all other reasons for failure other than FILE_NOT_FOUND. if (dwFileAttributes != ((DWORD) -1)) { if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) { // If it's readonly, clear that bit as well as all the others that // are illegal to set via SetFileAttributes() dwFileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_SPARSE_FILE); if (!::SetFileAttributesW(buffDestination, dwFileAttributes)) { ::ReportFailure("Error setting file attributes for target file \"%ls\" to 0x%08lx to allow overwrite\n", static_cast(buffDestination), dwFileAttributes); goto Exit; } } } if (g_fAnnounceCopies) printf("Copying \"%ls\" to \"%ls\"\n", static_cast(buffSource), static_cast(buffDestination)); // Hey, it's not there. Let's copy it and put the entry in the table. if (!::MyCopyFile(pBuffer, cbBuffer, buffSource, buffDestination, FALSE, hFile)) { ::ReportFailure("Failed to copy \"%ls\" to \"%ls\".\n", static_cast(buffSource), static_cast(buffDestination)); goto Exit; } if (fSetTimestamp) { if (!::SetFileTime(hFile, &pFile->m_ftSourceCreationTime, &pFile->m_ftSourceLastAccessTime, &pFile->m_ftSourceLastWriteTime)) { ::ReportFailure("Failed call to SetFileTime on file \"%ls\"\n", static_cast(buffDestination)); goto Exit; } ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section\n"); goto Exit; } g_FilesCopied++; g_FileCopiesProcessed++; g_BytesCopied += pFile->m_cbSize; l.Unlock(); } else { if (g_fAnnounceSkips) ::printf("Skipping copy from \"%ls\" to \"%ls\"\n", static_cast(buffSource), static_cast(buffDestination)); if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section\n"); goto Exit; } g_CopiesSkipped++; g_FileCopiesProcessed++; l.Unlock(); } fSuccess = TRUE; Exit: if (hFile != INVALID_HANDLE_VALUE) { CSxsPreserveLastError ple; ::CloseHandle(hFile); ple.Restore(); } if (::GetLastError() == ERROR_TOO_MANY_OPEN_FILES) { rfReQueue = true; fSuccess = TRUE; } return fSuccess; } BOOL ProcessFileLink( CFileLink *pFile, bool &rfReQueue ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); CSxsLockCriticalSection l(g_cs); bool fDoLink = true; CStringBuffer buffSource, buffDestination; WIN32_FILE_ATTRIBUTE_DATA wfad; rfReQueue = false; IFW32FALSE_EXIT(pFile->GetSource(buffSource)); IFW32FALSE_EXIT(pFile->GetDestination(buffDestination)); // CreateHardLinkW() doesn't deal with replace-existing like copyfile, so we'll see if a // file by that name is already present and if so, delete it. if (!::GetFileAttributesExW(buffDestination, GetFileExInfoStandard, &wfad)) { // It failed. But did it fail for the right reason? DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_FILE_NOT_FOUND) { ::ReportFailure("Error probing destination for \"%ls\"\n", static_cast(buffDestination)); goto Exit; } } else { ULONGLONG cbFileSize = (wfad.nFileSizeHigh << 32) | wfad.nFileSizeLow; ASSERT_NTC(cbFileSize >= wfad.nFileSizeLow); if ((pFile->m_cbSize == cbFileSize) && (pFile->m_ftSourceCreationTime == wfad.ftCreationTime)) { fDoLink = false; pFile->m_fSkipped = true; } else { if (g_fAnnounceDeletes) ::printf("Deleting file \"%ls\" in preparation for hard link creation\n", static_cast(buffDestination)); if (!::DeleteFileW(buffDestination)) { ::ReportFailure("Error deleting destination \"%ls\" in preparation of hard link creation\n", static_cast(buffDestination)); goto Exit; } } } if (fDoLink) { if (g_fAnnounceLinks) ::printf("Creating hard link from \"%ls\" to \"%ls\"\n", static_cast(buffDestination), static_cast(buffSource)); // Hey, it's already there. Let's link it. if (!::CreateHardLinkW(buffDestination, buffSource, NULL)) { ::ReportFailure("Error creating hard link from \"%ls\" to \"%ls\"\n", static_cast(buffDestination), static_cast(buffSource)); goto Exit; } if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section\n"); goto Exit; } g_FilesLinked++; g_FileLinksProcessed++; l.Unlock(); } else { if (g_fAnnounceSkips) ::printf("Skipping hard link creation from \"%ls\" to \"%ls\"\n", static_cast(buffDestination), static_cast(buffSource)); if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section\n"); goto Exit; } g_LinksSkipped++; g_FileLinksProcessed++; l.Unlock(); } fSuccess = TRUE; Exit: if (::GetLastError() == ERROR_TOO_MANY_OPEN_FILES) { rfReQueue = true; fSuccess = TRUE; } return fSuccess; } BOOL MyCopyFile( BYTE *pBuffer, DWORD cbBuffer, PCWSTR lpExistingFileName, PCWSTR lpNewFileName, BOOL fFailIfExists, HANDLE &rhFile ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); LARGE_INTEGER liFileSize; rhFile = INVALID_HANDLE_VALUE; HANDLE hIn = INVALID_HANDLE_VALUE, hOut = INVALID_HANDLE_VALUE; hIn = ::CreateFileW(lpExistingFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hIn == INVALID_HANDLE_VALUE) { ::ReportFailure("Failed to open input file \"%ls\"\n", lpExistingFileName); goto Exit; } hOut = ::CreateFileW(lpNewFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL); if (hOut == INVALID_HANDLE_VALUE) { ::ReportFailure("Failed to open output file \"%ls\"\n", lpNewFileName); goto Exit; } liFileSize.QuadPart = 0; // let the games begin... for (;;) { DWORD dwBytesRead, dwBytesWritten; DWORD dwBytesToWrite = 0; if (!::ReadFile(hIn, pBuffer, cbBuffer, &dwBytesRead, NULL)) { ::ReportFailure("Error reading from file \"%ls\"\n", lpExistingFileName); goto Exit; } if (dwBytesRead == 0) break; liFileSize.QuadPart += dwBytesRead; if (dwBytesRead != cbBuffer) { // We have to round to the cluster size for the write... dwBytesToWrite = dwBytesRead + (g_dwDestinationBytesPerSector - 1); dwBytesToWrite -= (dwBytesToWrite % g_dwDestinationBytesPerSector); } else dwBytesToWrite = dwBytesRead; if (!::WriteFile(hOut, pBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) { ::ReportFailure("Error writing to file \"%ls\"\n", lpNewFileName); goto Exit; } } // If the file size wasn't a multiple of the sector size, we need to open the // file without the unbuffered flag so that we can set its size to the exact // correct byte count. if ((liFileSize.QuadPart % g_dwDestinationBytesPerSector) != 0) { ::CloseHandle(hOut); hOut = INVALID_HANDLE_VALUE; hOut = ::CreateFileW(lpNewFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hOut == INVALID_HANDLE_VALUE) { ::ReportFailure("Unable to reopen output file \"%ls\"\n", lpNewFileName); goto Exit; } // Truncate the file appropriately if (!::MySetFilePointerEx(hOut, liFileSize, NULL, FILE_BEGIN)) { ::ReportFailure("Error setting file pointer on file \"%ls\"\n", lpNewFileName); goto Exit; } if (!::SetEndOfFile(hOut)) { ::ReportFailure("Error setting end of file on file \"%ls\"\n", lpNewFileName); goto Exit; } } ::CloseHandle(hIn); hIn = INVALID_HANDLE_VALUE; // Pass the handle back out and set it to INVALID_HANDLE_VALUE so that we don't // try to close it in the exit path. rhFile = hOut; hOut = INVALID_HANDLE_VALUE; fSuccess = TRUE; Exit: if (hIn != INVALID_HANDLE_VALUE) { CSxsPreserveLastError ple; ::CloseHandle(hIn); ple.Restore(); } if (hOut != INVALID_HANDLE_VALUE) { CSxsPreserveLastError ple; ::CloseHandle(hOut); ple.Restore(); } return fSuccess; }