|
|
#include "stdinc.h" // actually from dll\whistler directory
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "fusionlastwin32error.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#pragma warning(push)
#pragma warning(disable: 4511)
#pragma warning(disable: 4512)
#pragma warning(disable: 4663)
#include <yvals.h>
#pragma warning(disable: 4663)
#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"
#undef NUMBER_OF
#include "..\getpdbname\lib.h"
#include "..\getpdbname\lib.c"
#include "setfilepointerex.c"
#include "getfilesizeex.c"
BOOL FusionpAreWeInOSSetupMode(BOOL* pfIsInSetup) { *pfIsInSetup = FALSE; return TRUE; }
void ReportFailure(const char szFormat[], ...); void TraceFailureContext(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;
#define MAX_RETRIES (5)
#define WAIT_NOTIFY_COUNTER (50)
// 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, bool &rfRequeue );
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; ASSERT_NTC(!rfReQueue); ::QueryPerformanceCounter(&liStart); BOOL fResult = this->DoYourThing(rfReQueue, pBuffer, cbBuffer); if (rfReQueue) { // Why would we retry if there wasn't a failure?
ASSERT_NTC(!fResult); m_cRetries++; if (m_cRetries <= MAX_RETRIES) fResult = TRUE; }
if (!fResult) TraceFailureContext("executing work item %p\n", this);
CSxsPreserveLastError ple; ::QueryPerformanceCounter(&liEnd); ple.Restore();
m_ullStart = static_cast<ULONGLONG>(liStart.QuadPart); m_ullEnd = static_cast<ULONGLONG>(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)) { TraceFailureContext("initializing CDir instance %p with source \"%ls\"\n", this, static_cast<PCWSTR>(rbuffSource)); goto Exit; }
if (!m_buffDestination.Win32Assign(rbuffDestination)) { TraceFailureContext("initializing CDir instance %p with destination \"%ls\"\n", this, static_cast<PCWSTR>(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<CDir, offsetof(CDir, m_linkage)> *g_pDirs = NULL; CDir **g_prgpDirs = NULL;
CDeque<CFileCopy, offsetof(CFileCopy, m_linkage)> *g_pFileCopies = NULL; CFileCopy **g_prgpFileCopies = NULL;
CDeque<CFileLink, offsetof(CFileLink, m_linkage)> *g_pFileLinks = NULL; CFileLink **g_prgpFileLinks = NULL;
class CFileIdHashHelper : public CHashTableHelper<ULONGLONG, ULONGLONG, PCWSTR, CStringBuffer> { 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<ULONG>(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<ULONGLONG, ULONGLONG, CFileCopy *, CFileCopy *, CFileIdHashHelper> { 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."); 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)) { TraceFailureContext("initializing the heap.\n"); 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);
for (i=0; i<g_nThreads; i++) g_rghThreads[i] = NULL;
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] <source> <dest>\n", argv[0], argv[0]); goto Exit; }
// Abuse these buffers for debugging purposes...
{ HANDLE h;
if (!::GetLogicalDriveStringsW(NUMBER_OF(rgwchSourceVolumePath), rgwchSourceVolumePath)) { ::ReportFailure("GetLogicalDriveStringsW failed."); goto Exit; }
h = ::FindFirstVolumeW(rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath)); if (h == INVALID_HANDLE_VALUE) { ::ReportFailure("FindFirstVolumeW failed."); goto Exit; }
for (;;) { DWORD cchReturnLength;
if (!::GetVolumePathNamesForVolumeNameW(rgwchSourceVolumePath, rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath), &cchReturnLength)) { ::ReportFailure("GetVolumePathNamesForVolumeNameW failed."); goto Exit; }
if (!::FindNextVolumeW(h, rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath))) { const DWORD dwLastError = ::GetLastError(); if (dwLastError != ERROR_NO_MORE_FILES) { ::ReportFailure("FindNextVolumeW failed."); goto Exit; }
break; } }
::FindVolumeClose(h); }
if (!::GetVolumePathNameW(argv[iSource], rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath))) { ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed.", argv[iSource]); goto Exit; }
uiSourceDriveType = ::GetDriveTypeW(rgwchSourceVolumePath);
if (!::GetVolumePathNameW(argv[iDestination], rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath))) { ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed.", 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.", rgwchSourceVolumePath); goto Exit; }
if (!::GetVolumeInformationW( rgwchDestinationVolumePath, rgwchDestinationVolumeName, NUMBER_OF(rgwchDestinationVolumeName), &dwDestinationVolumeSerialNumber, &dwDestinationMaximumComponentLength, &dwDestinationFileSystemFlags, rgwchDestinationFileSystemNameBuffer, NUMBER_OF(rgwchDestinationFileSystemNameBuffer))) { ::ReportFailure("GetVolumeInformation(L\"%ls\", ...) failed.", rgwchDestinationVolumePath); goto Exit; }
if (!::GetDiskFreeSpaceW( rgwchDestinationVolumePath, &g_dwDestinationSectorsPerCluster, &g_dwDestinationBytesPerSector, &g_dwDestinationNumberOfFreeClusters, &g_dwDestinationTotalNumberOfClusters)) { ::ReportFailure("GetDiskFreeSpaceW(L\"%ls\", ...) failed.", rgwchDestinationVolumePath); goto Exit; }
if (!::FusionpInitializeCriticalSection(&g_cs)) goto Exit;
g_pFiles = new CFileIdHashTable; g_pDirs = new CDeque<CDir, offsetof(CDir, m_linkage)>; g_pFileCopies = new CDeque<CFileCopy, offsetof(CFileCopy, m_linkage)>; g_pFileLinks = new CDeque<CFileLink, offsetof(CFileLink, m_linkage)>;
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.", g_nThreads); goto Exit; }
g_hWorkItemDoneEvent = ::CreateEventW(NULL, FALSE, FALSE, NULL); if (g_hWorkItemDoneEvent == NULL) { ::ReportFailure("Creating the file copied event failed."); goto Exit; } for (i=0; i<g_nThreads; i++) { g_rghThreads[i] = ::CreateThread(NULL, 0, &WorkerThreadProc, NULL, CREATE_SUSPENDED, NULL); if (g_rghThreads[i] == NULL) { ::ReportFailure("Creating worker thread number %lu failed.", i); goto Exit; } }
::GetSystemTime(&stStart);
if (!buffSource.Win32Assign(argv[iSource], wcslen(argv[iSource]))) goto Exit;
if (!buffDestination.Win32Assign(argv[iDestination], wcslen(argv[iDestination]))) goto Exit;
if (!::BuildDirList(buffSource, buffDestination)) goto Exit;
if (!g_fSilent) printf("%ls: Found %Iu directories.\n", g_pszImage, g_pDirs->GetEntryCount());
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; i<g_pFileCopies->GetEntryCount(); 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<PCWSTR>(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<ULONGLONG>(ullFilesPerMS / 1000ui64), static_cast<ULONG>(ullFilesPerMS % 1000ui64), static_cast<ULONGLONG>(ullBytesPerMS / 1000ui64), static_cast<ULONG>(ullBytesPerMS % 1000ui64)); }
ullTemp = (((stAfterLinksDelta.wHour * 60) + stAfterLinksDelta.wMinute) * 60) + stAfterLinksDelta.wSecond;
if (ullTemp != 0) { printf( " Files linked per second: %I64u\n", static_cast<ULONGLONG>(g_FilesLinked / ullTemp)); }
ullTemp = (((stEndDelta.wHour * 60) + stEndDelta.wMinute) * 60) + stEndDelta.wSecond;
if (ullTemp != 0) { printf( " Overall files per second: %I64u\n", static_cast<ULONGLONG>((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; i<g_nThreads; i++) { if ((g_rghThreads[i] != NULL) && (g_rghThreads[i] != INVALID_HANDLE_VALUE)) { if (::ResumeThread(g_rghThreads[i]) == -1) { ::ReportFailure("Failed to resume worker thread %lu.", i + 1); goto Exit; } } }
fSuccess = TRUE; Exit: return fSuccess; }
BOOL SuspendWorkerThreads() { BOOL fSuccess = FALSE; ULONG i;
for (i=0; i<g_nThreads; i++) { if (::SuspendThread(g_rghThreads[i]) == -1) { ::ReportFailure("Failed to suspend worker thread %lu.", i + 1); goto Exit; } }
fSuccess = TRUE; Exit: return fSuccess; }
BOOL WaitForWorkersToComplete( ULONGLONG &rullCounter, ULONGLONG ullLimit, PCSTR pszOperationName ) { BOOL fSuccess = FALSE; ULONGLONG i; ULONGLONG ullCounterNextNotify = rullCounter + WAIT_NOTIFY_COUNTER; ULONGLONG ullCounter;
if (!::ResumeWorkerThreads()) goto Exit;
i = 0; while (rullCounter < ullLimit) { // Don't wake more than every tenth of a second...
::Sleep(100);
DWORD dwWFSO = ::WaitForSingleObject(g_hWorkItemDoneEvent, INFINITE);
if (dwWFSO == WAIT_FAILED) { ::ReportFailure("Waiting for work item done event (%p) failed.", g_hWorkItemDoneEvent); goto Exit; }
i++;
ullCounter = rullCounter; if (ullCounter >= ullCounterNextNotify) { ullCounterNextNotify = ullCounter + WAIT_NOTIFY_COUNTER; if (!g_fSilent) printf("%ls: %s processed (%I64u total): %I64u\n", g_pszImage, pszOperationName, ullLimit, ullCounter); } }
if (!::SuspendWorkerThreads()) goto Exit;
fSuccess = TRUE; Exit: return fSuccess; }
void ComputeTimeDeltas( const SYSTEMTIME &rstStart, const SYSTEMTIME &rstEnd, SYSTEMTIME &rstDelta ) { FILETIME ftStart, ftEnd; ULARGE_INTEGER uliStart, uliEnd; ULONGLONG ullDiff, ullTemp; ULONG ulHours, ulMinutes, ulSeconds, ulMilliseconds;
::SystemTimeToFileTime(&rstStart, &ftStart); ::SystemTimeToFileTime(&rstEnd, &ftEnd);
uliStart.LowPart = ftStart.dwLowDateTime; uliStart.HighPart = ftStart.dwHighDateTime;
uliEnd.LowPart = ftEnd.dwLowDateTime; uliEnd.HighPart = ftEnd.dwHighDateTime;
ullDiff = (uliEnd.QuadPart - uliStart.QuadPart);
ulHours = (ULONG) (ullDiff / (10000000ui64 * 60 * 60));
ullTemp = ullDiff - ((ULONGLONG) ulHours) * (10000000ui64 * 60 * 60);
ulMinutes = (ULONG) (ullTemp / (10000000ui64 * 60));
ullTemp -= ((ULONGLONG) ulMinutes) * (10000000ui64 * 60);
ulSeconds = (ULONG) (ullTemp / 10000000ui64);
ullTemp -= ((ULONGLONG) ulSeconds) * 10000000ui64;
ulMilliseconds = (ULONG) (ullTemp / 10000ui64);
rstDelta.wYear = 0; rstDelta.wMonth = 0; rstDelta.wDayOfWeek = 0; rstDelta.wDay = 0; rstDelta.wHour = (WORD) ulHours; rstDelta.wMinute = (WORD) ulMinutes; rstDelta.wSecond = (WORD) ulSeconds; rstDelta.wMilliseconds = (WORD) ulMilliseconds; }
BOOL BuildDirList( const CBaseStringBuffer &rbuffSource, const CBaseStringBuffer &rbuffDestination ) { PCWSTR szSrc = rbuffSource; 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; CDir *pDir = NULL;
dwFileAttributes = ::GetFileAttributesW(rbuffSource); 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!"); goto Exit; }
if (!buffSrc.Win32Assign(rbuffSource)) goto Exit;
if (!buffSrc.Win32EnsureTrailingPathSeparator()) goto Exit;
if (!buffDst.Win32Assign(rbuffDestination)) goto Exit;
if (!buffDst.Win32EnsureTrailingPathSeparator()) goto Exit;
cchSrc = buffSrc.Cch(); cchDst = buffDst.Cch();
pDir = new CDir; if (pDir == NULL) { ::SetLastError(ERROR_OUTOFMEMORY); ::ReportFailure("Failed to allocate new CDir object."); goto Exit; }
if (!pDir->Initialize(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.", static_cast<PCWSTR>(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(%p, %p) failed.", hFind, &wfd); 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!", 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.", static_cast<PCWSTR>(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.", static_cast<PCWSTR>(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.", static_cast<PCWSTR>(buffSrc)); goto Exit; }
if (!::GetFileInformationByHandle(hFile, &bhfi)) { ::ReportFailure("Failed to GetFileInformationByHandle(%p, ...) on file \"%ls\".", hFile, static_cast<PCWSTR>(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.", 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."); 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."); 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<CDir, offsetof(CDir, m_linkage)> 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<PCWSTR>(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];
rgchBuffer[0] = 0; rgchWin32Error[0] = 0;
va_start(ap, szFormat); _vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), szFormat, ap); rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = 0; va_end(ap);
if (!::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, rgchWin32Error, NUMBER_OF(rgchWin32Error), &ap)) { const DWORD dwLastError2 = ::GetLastError(); _snwprintf(rgchWin32Error, NUMBER_OF(rgchWin32Error), L"Error formatting Win32 error %lu\nError from FormatMessage is %lu", dwLastError, dwLastError2); } rgchWin32Error[NUMBER_OF(rgchWin32Error) - 1] = 0;
fprintf(stderr, "%ls: %s\n%ls\n", g_pszImage, rgchBuffer, rgchWin32Error); }
void TraceFailureContext( const char szFormat[], ... ) { const DWORD dwLastError = ::GetLastError(); va_list ap; char rgchBuffer[4096]; WCHAR rgchWin32Error[4096];
rgchBuffer[0] = 0; rgchWin32Error[0] = 0;
va_start(ap, szFormat); _vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), szFormat, ap); rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = 0; va_end(ap);
if (!::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, rgchWin32Error, NUMBER_OF(rgchWin32Error), &ap)) { const DWORD dwLastError2 = ::GetLastError(); _snwprintf(rgchWin32Error, NUMBER_OF(rgchWin32Error), L"Error formatting Win32 error %lu\nError from FormatMessage is %lu", dwLastError, dwLastError2); }
rgchWin32Error[NUMBER_OF(rgchWin32Error) - 1] = 0; RemoveTrailingWhitespaceW(rgchWin32Error);
fprintf(stderr, "%ls (0x%lx) while %s\n", rgchWin32Error, dwLastError, rgchBuffer); }
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.", 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(%p, ...).", nThread, g_hIoCompletionPort); 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.", 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<CDir, offsetof(CDir, m_linkage)> 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."); goto Exit; } }
fSuccess = TRUE; Exit: return fSuccess; }
BOOL QueueFileCopies() { BOOL fSuccess = FALSE; SIZE_T i; FN_TRACE_WIN32(fSuccess);
CDequeIterator<CFileCopy, offsetof(CFileCopy, m_linkage)> fileIter;
g_prgpFileCopies = new (CFileCopy *[g_pFileCopies->GetEntryCount()]); if (g_prgpFileCopies == NULL) { ReportFailure("Failed to allocate file copy list."); goto Exit; }
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."); goto Exit; } g_prgpFileCopies[i++] = fileIter; }
fSuccess = TRUE; Exit: return fSuccess; }
BOOL QueueFileLinks() { BOOL fSuccess = FALSE; SIZE_T i;
CDequeIterator<CFileLink, offsetof(CFileLink, m_linkage)> fileIter;
g_prgpFileLinks = new (CFileLink *[g_pFileLinks->GetEntryCount()]); if (g_prgpFileLinks == NULL) { ReportFailure("Failed to allocate file link list."); goto Exit; }
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."); 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\".", static_cast<PCWSTR>(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.", static_cast<PCWSTR>(buffDestination), dwFileAttributes); goto Exit; } } }
if (g_fAnnounceCopies) printf("Copying \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffSource), static_cast<PCWSTR>(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, rfReQueue)) { ::ReportFailure("Failed to copy \"%ls\" to \"%ls\". %srequeuing.", static_cast<PCWSTR>(buffSource), static_cast<PCWSTR>(buffDestination), rfReQueue ? "" : "not "); goto Exit; }
if (fSetTimestamp) { if (!::SetFileTime(hFile, &pFile->m_ftSourceCreationTime, &pFile->m_ftSourceLastAccessTime, &pFile->m_ftSourceLastWriteTime)) { ::ReportFailure("Failed call to SetFileTime on file \"%ls\".", static_cast<PCWSTR>(buffDestination)); goto Exit; }
::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }
if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section."); 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<PCWSTR>(buffSource), static_cast<PCWSTR>(buffDestination));
if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section."); 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\".", static_cast<PCWSTR>(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<PCWSTR>(buffDestination));
if (!::DeleteFileW(buffDestination)) { ::ReportFailure("Error deleting destination \"%ls\" in preparation for hard link creation.", static_cast<PCWSTR>(buffDestination)); goto Exit; } } }
if (fDoLink) { if (g_fAnnounceLinks) ::printf("Creating hard link from \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource));
// Hey, it's already there. Let's link it.
if (!::CreateHardLinkW(buffDestination, buffSource, NULL)) { ::ReportFailure("Error creating hard link from \"%ls\" to \"%ls\".", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource)); goto Exit; }
if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section."); goto Exit; }
g_FilesLinked++; g_FileLinksProcessed++;
l.Unlock(); } else { if (g_fAnnounceSkips) ::printf("Skipping hard link creation from \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource));
if (!l.Lock()) { ::ReportFailure("Failed to lock global critical section."); 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 &rfRequeue ) { 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\".", lpExistingFileName); // we'll assume that we should requeue failures that have to do with the input file.
rfRequeue = true; 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\".", 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\".", lpExistingFileName); // we'll assume that we should requeue failures that have to do with the input file.
rfRequeue = true; 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\".", lpNewFileName); // we'll assume that we should requeue failures that have to do with the input file.
rfRequeue = true; 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\".", lpNewFileName); goto Exit; }
// Truncate the file appropriately
if (!::FusionpSetFilePointerEx(hOut, liFileSize, NULL, FILE_BEGIN)) { ::ReportFailure("Error setting file pointer on file \"%ls\".", lpNewFileName); goto Exit; }
if (!::SetEndOfFile(hOut)) { ::ReportFailure("Error setting end of file on file \"%ls\".", 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; }
|