Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1980 lines
57 KiB

  1. #include "stdinc.h" // actually from dll\whistler directory
  2. #include "nt.h"
  3. #include "ntrtl.h"
  4. #include "nturtl.h"
  5. #include "windows.h"
  6. #include "fusionlastwin32error.h"
  7. #include <stddef.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <stdarg.h>
  11. #pragma warning(push)
  12. #pragma warning(disable: 4511)
  13. #pragma warning(disable: 4512)
  14. #pragma warning(disable: 4663)
  15. #include <yvals.h>
  16. #pragma warning(disable: 4663)
  17. #pragma warning(pop)
  18. #include "fusionbuffer.h"
  19. #include "fusion.h"
  20. #include "sxsasmname.h"
  21. #include "util.h"
  22. #include "filestream.cpp"
  23. #include "sxsapi.h"
  24. #include "fusiontrace.h"
  25. #include "cresourcestream.cpp"
  26. #include "cmemorystream.cpp"
  27. #include "wintrust.h"
  28. #include "softpub.h"
  29. #include "perfclocking.h"
  30. #include "strongname.h"
  31. #include "fusionversion.h"
  32. #include "fusionhash.h"
  33. #include "fusiondeque.h"
  34. #undef NUMBER_OF
  35. #include "..\getpdbname\lib.h"
  36. #include "..\getpdbname\lib.c"
  37. #include "setfilepointerex.c"
  38. #include "getfilesizeex.c"
  39. BOOL FusionpAreWeInOSSetupMode(BOOL* pfIsInSetup) { *pfIsInSetup = FALSE; return TRUE; }
  40. void ReportFailure(const char szFormat[], ...);
  41. void TraceFailureContext(const char szFormat[], ...);
  42. PCWSTR g_pszImage = L"mjgcopy";
  43. CRITICAL_SECTION g_cs;
  44. ULONGLONG g_FilesCopied = 0;
  45. ULONGLONG g_BytesCopied = 0;
  46. ULONGLONG g_FilesLinked = 0;
  47. ULONGLONG g_DirectoriesCopied = 0;
  48. ULONGLONG g_CopiesSkipped = 0;
  49. ULONGLONG g_LinksSkipped = 0;
  50. ULONGLONG g_FileCopiesProcessed = 0;
  51. ULONGLONG g_FileLinksProcessed = 0;
  52. ULONGLONG g_DirScansProcessed = 0;
  53. ULONGLONG g_BytesToCopy = 0;
  54. bool g_fAnnounceDirectories = true;
  55. bool g_fAnnounceCopies = false;
  56. bool g_fAnnounceDeletes = true;
  57. bool g_fAnnounceLinks = true;
  58. bool g_fAnnounceSkips = false;
  59. bool g_fSilent = false;
  60. bool g_fShowProgress = true;
  61. HANDLE g_hIoCompletionPort = NULL;
  62. HANDLE g_hWorkItemDoneEvent = INVALID_HANDLE_VALUE;
  63. DWORD g_dwDestinationSectorsPerCluster = 0;
  64. DWORD g_dwDestinationBytesPerSector = 0;
  65. DWORD g_dwDestinationNumberOfFreeClusters = 0;
  66. DWORD g_dwDestinationTotalNumberOfClusters = 0;
  67. ULONG g_nThreads = 3;
  68. HANDLE g_rghThreads[32];
  69. class CFileCopy;
  70. class CFileLink;
  71. class CDir;
  72. #define MAX_RETRIES (5)
  73. #define WAIT_NOTIFY_COUNTER (50)
  74. // BOOL ScanAndCopyDir(PCWSTR szSource, PCWSTR szDest);
  75. BOOL BuildDirList(const CBaseStringBuffer &rbuffSource, const CBaseStringBuffer &rbuffDestination);
  76. BOOL MakeDirectoryStructure();
  77. BOOL QueueDirScans();
  78. BOOL QueueFileCopies();
  79. BOOL QueueFileLinks();
  80. DWORD WINAPI WorkerThreadProc(LPVOID pvParameter);
  81. void ComputeTimeDeltas(const SYSTEMTIME &rstStart, const SYSTEMTIME &rstEnd, SYSTEMTIME &rstDelta);
  82. BOOL ProcessFileCopy(CFileCopy *pFileCopy, bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer);
  83. BOOL ProcessFileLink(CFileLink *pFileLink, bool &rfReQueue);
  84. BOOL ProcessDirScan(CDir *pDir, bool &rfReQueue);
  85. BOOL ResumeWorkerThreads();
  86. BOOL SuspendWorkerThreads();
  87. BOOL
  88. WaitForWorkersToComplete(
  89. ULONGLONG &rullCounter,
  90. ULONGLONG ullLimit,
  91. PCSTR pszOperationName
  92. );
  93. BOOL
  94. MyCopyFile(
  95. BYTE *pBuffer,
  96. DWORD cbBuffer,
  97. PCWSTR lpExistingFileName,
  98. PCWSTR lpNewFileName,
  99. BOOL fFailIfExists,
  100. HANDLE &rhNewFileHandle,
  101. bool &rfRequeue
  102. );
  103. class CEntry
  104. {
  105. private:
  106. CEntry(const CEntry&); // deliberately not implemented
  107. void operator=(const CEntry&); // deliberately not implemented
  108. public:
  109. CEntry() : m_cRetries(0) { }
  110. ~CEntry() { }
  111. virtual BOOL BaseDoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer)
  112. {
  113. LARGE_INTEGER liStart, liEnd;
  114. ASSERT_NTC(!rfReQueue);
  115. ::QueryPerformanceCounter(&liStart);
  116. BOOL fResult = this->DoYourThing(rfReQueue, pBuffer, cbBuffer);
  117. if (rfReQueue)
  118. {
  119. // Why would we retry if there wasn't a failure?
  120. ASSERT_NTC(!fResult);
  121. m_cRetries++;
  122. if (m_cRetries <= MAX_RETRIES)
  123. fResult = TRUE;
  124. }
  125. if (!fResult)
  126. TraceFailureContext("executing work item %p\n", this);
  127. CSxsPreserveLastError ple;
  128. ::QueryPerformanceCounter(&liEnd);
  129. ple.Restore();
  130. m_ullStart = static_cast<ULONGLONG>(liStart.QuadPart);
  131. m_ullEnd = static_cast<ULONGLONG>(liEnd.QuadPart);
  132. return fResult;
  133. }
  134. virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pbBuffer, DWORD cbBuffer) = 0;
  135. ULONGLONG m_ullStart, m_ullEnd;
  136. ULONG m_cRetries;
  137. };
  138. class CDir : public CEntry
  139. {
  140. private:
  141. CDir(const CDir&); // deliberately not implemented
  142. void operator=(const CDir&); // deliberately not implemented
  143. public:
  144. CDir() { };
  145. ~CDir() { };
  146. BOOL Initialize(const CBaseStringBuffer &rbuffSource, const CBaseStringBuffer &rbuffDestination)
  147. {
  148. BOOL fSuccess = FALSE;
  149. if (!m_buffSource.Win32Assign(rbuffSource))
  150. {
  151. TraceFailureContext("initializing CDir instance %p with source \"%ls\"\n", this, static_cast<PCWSTR>(rbuffSource));
  152. goto Exit;
  153. }
  154. if (!m_buffDestination.Win32Assign(rbuffDestination))
  155. {
  156. TraceFailureContext("initializing CDir instance %p with destination \"%ls\"\n", this, static_cast<PCWSTR>(rbuffDestination));
  157. goto Exit;
  158. }
  159. fSuccess = TRUE;
  160. Exit:
  161. return fSuccess;
  162. }
  163. virtual BOOL DoYourThing(bool &rfReQueue, BYTE *, DWORD) { return ::ProcessDirScan(this, rfReQueue); }
  164. CStringBuffer m_buffSource;
  165. CStringBuffer m_buffDestination;
  166. CDequeLinkage m_linkage;
  167. };
  168. class CFileBase : public CEntry
  169. {
  170. private:
  171. CFileBase(const CFileBase&); // deliberately not implemented
  172. void operator=(const CFileBase&); // deliberately not implemented
  173. public:
  174. CFileBase() : m_fDone(false) { }
  175. ~CFileBase() { }
  176. BOOL Initialize(
  177. CDir *pDir,
  178. PCWSTR pszFilename,
  179. FILETIME ftSourceCreationTime,
  180. FILETIME ftSourceLastAccessTime,
  181. FILETIME ftSourceLastWriteTime,
  182. ULONGLONG ullFileIndex,
  183. ULONGLONG cbSize
  184. )
  185. {
  186. BOOL fSuccess = FALSE;
  187. FN_TRACE_WIN32(fSuccess);
  188. IFW32FALSE_EXIT(m_buffFilename.Win32Assign(pszFilename, wcslen(pszFilename)));
  189. m_ftSourceCreationTime = ftSourceCreationTime;
  190. m_ftSourceLastAccessTime = ftSourceLastAccessTime;
  191. m_ftSourceLastWriteTime = ftSourceLastWriteTime;
  192. m_ullFileIndex = ullFileIndex;
  193. m_cbSize = cbSize;
  194. m_pDir = pDir;
  195. fSuccess = TRUE;
  196. Exit:
  197. return fSuccess;
  198. }
  199. virtual BOOL GetSource(CBaseStringBuffer &rbuff)
  200. {
  201. BOOL fSuccess = FALSE;
  202. FN_TRACE_WIN32(fSuccess);
  203. IFW32FALSE_EXIT(rbuff.Win32Assign(m_pDir->m_buffSource));
  204. IFW32FALSE_EXIT(rbuff.Win32Append(m_buffFilename));
  205. fSuccess = TRUE;
  206. Exit:
  207. return fSuccess;
  208. }
  209. virtual BOOL GetDestination(CBaseStringBuffer &rbuff)
  210. {
  211. BOOL fSuccess = FALSE;
  212. FN_TRACE_WIN32(fSuccess);
  213. IFW32FALSE_EXIT(rbuff.Win32Assign(m_pDir->m_buffDestination));
  214. IFW32FALSE_EXIT(rbuff.Win32Append(m_buffFilename));
  215. fSuccess = TRUE;
  216. Exit:
  217. return fSuccess;
  218. }
  219. CStringBuffer m_buffFilename;
  220. FILETIME m_ftSourceCreationTime;
  221. FILETIME m_ftSourceLastAccessTime;
  222. FILETIME m_ftSourceLastWriteTime;
  223. ULONGLONG m_ullFileIndex;
  224. ULONGLONG m_cbSize;
  225. CDir *m_pDir;
  226. bool m_fSkipped; // used to avoid skewed statistics about number of bytes copied/sec etc.
  227. bool m_fDone;
  228. CDequeLinkage m_linkage;
  229. };
  230. class CFileCopy : public CFileBase
  231. {
  232. public:
  233. CFileCopy() { }
  234. ~CFileCopy() { }
  235. virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer) { return ::ProcessFileCopy(this, rfReQueue, pBuffer, cbBuffer); }
  236. static int __cdecl QSortBySize(const void *param1, const void *param2)
  237. {
  238. CFileCopy **pp1 = (CFileCopy **) param1;
  239. CFileCopy **pp2 = (CFileCopy **) param2;
  240. CFileCopy *p1 = *pp1;
  241. CFileCopy *p2 = *pp2;
  242. int iRet = 0;
  243. if (p1->m_fSkipped)
  244. {
  245. if (p2->m_fSkipped)
  246. {
  247. if (p1->m_cbSize < p2->m_cbSize)
  248. iRet = 1;
  249. else if (p1->m_cbSize > p2->m_cbSize)
  250. iRet = -1;
  251. else
  252. {
  253. if (p1->m_ullFileIndex < p2->m_ullFileIndex)
  254. iRet = -1;
  255. else
  256. iRet = 1;
  257. }
  258. }
  259. else
  260. iRet = -1;
  261. }
  262. else
  263. {
  264. if (p2->m_fSkipped)
  265. iRet = 1;
  266. else
  267. {
  268. if (p1->m_cbSize < p2->m_cbSize)
  269. iRet = 1;
  270. else if (p1->m_cbSize > p2->m_cbSize)
  271. iRet = -1;
  272. else
  273. {
  274. if (p1->m_ullFileIndex < p2->m_ullFileIndex)
  275. iRet = -1;
  276. else
  277. iRet = 1;
  278. }
  279. }
  280. }
  281. return iRet;
  282. }
  283. private:
  284. CFileCopy(const CFileCopy &r);
  285. void operator =(const CFileCopy &r);
  286. };
  287. class CFileLink : public CFileBase
  288. {
  289. public:
  290. CFileLink() { }
  291. ~CFileLink() { }
  292. virtual BOOL DoYourThing(bool &rfReQueue, BYTE *pBuffer, DWORD cbBuffer) { return ::ProcessFileLink(this, rfReQueue); }
  293. BOOL GetSource(CBaseStringBuffer &rbuff);
  294. private:
  295. CFileLink(const CFileLink &r);
  296. void operator =(const CFileLink &r);
  297. };
  298. CDeque<CDir, offsetof(CDir, m_linkage)> *g_pDirs = NULL;
  299. CDir **g_prgpDirs = NULL;
  300. CDeque<CFileCopy, offsetof(CFileCopy, m_linkage)> *g_pFileCopies = NULL;
  301. CFileCopy **g_prgpFileCopies = NULL;
  302. CDeque<CFileLink, offsetof(CFileLink, m_linkage)> *g_pFileLinks = NULL;
  303. CFileLink **g_prgpFileLinks = NULL;
  304. class CFileIdHashHelper : public CHashTableHelper<ULONGLONG, ULONGLONG, PCWSTR, CStringBuffer>
  305. {
  306. private:
  307. CFileIdHashHelper(const CFileIdHashHelper&); // deliberately not implemented
  308. void operator=(const CFileIdHashHelper&); // deliberately not implemented
  309. public:
  310. static BOOL HashKey(ULONGLONG keyin, ULONG &rulPseudoKey) { rulPseudoKey = static_cast<ULONG>(keyin); return TRUE; }
  311. static BOOL CompareKey(ULONGLONG keyin, const ULONGLONG &rtkeystored, bool &rfMatch) { rfMatch = keyin == rtkeystored; return TRUE; }
  312. static VOID PreInitializeKey(ULONGLONG &rtkeystored) { rtkeystored = 0; }
  313. static VOID PreInitializeValue(CFileCopy *&rtvaluestored) { rtvaluestored = NULL; }
  314. static BOOL InitializeKey(ULONGLONG keyin, ULONGLONG &rtkeystored) { rtkeystored = keyin; return TRUE; }
  315. static BOOL InitializeValue(CFileCopy *vin, CFileCopy *&rvstored) { rvstored = vin; return TRUE; }
  316. static BOOL UpdateValue(CFileCopy *vin, CFileCopy *&rvstored) { rvstored = vin; return TRUE; }
  317. static VOID FinalizeKey(ULONGLONG &rtkeystored) { }
  318. static VOID FinalizeValue(CFileCopy *&rvstored) { rvstored = NULL; }
  319. };
  320. class CFileIdHashTable : public CHashTable<ULONGLONG, ULONGLONG, CFileCopy *, CFileCopy *, CFileIdHashHelper>
  321. {
  322. private:
  323. CFileIdHashTable(const CFileIdHashTable&); // deliberately not implemented
  324. void operator=(const CFileIdHashTable&); // deliberately not implemented
  325. public:
  326. CFileIdHashTable() { }
  327. ~CFileIdHashTable() { }
  328. };
  329. CFileIdHashTable *g_pFiles = NULL;
  330. FILE *g_pLogFile = NULL;
  331. BOOL
  332. CFileLink::GetSource(CBaseStringBuffer &rbuff)
  333. {
  334. BOOL fSuccess = FALSE;
  335. FN_TRACE_WIN32(fSuccess);
  336. CStringBuffer *pbuff = NULL;
  337. CFileCopy **ppFileCopy = NULL;
  338. if (!g_pFiles->Find(m_ullFileIndex, ppFileCopy))
  339. {
  340. ::ReportFailure("Finding file index %I64u in the file table failed.");
  341. goto Exit;
  342. }
  343. IFW32FALSE_EXIT((*ppFileCopy)->GetDestination(rbuff));
  344. FN_EPILOG
  345. }
  346. extern "C" int __cdecl wmain(int argc, wchar_t** argv)
  347. {
  348. int iReturnStatus = EXIT_FAILURE;
  349. SYSTEMTIME stStart, stAfterScan, stAfterDirCreation, stAfterCopies, stAfterLinks, stEnd;
  350. ULONG i;
  351. CStringBuffer buffSource;
  352. CStringBuffer buffDestination;
  353. int iSource = 0;
  354. int iDestination = 0;
  355. SYSTEMTIME stAfterScanDelta, stAfterDirCreationDelta, stAfterCopiesDelta, stAfterLinksDelta, stEndDelta;
  356. ULONGLONG ullTemp;
  357. int iArg;
  358. // DWORD dwRetVal;
  359. WCHAR rgwchSourceVolumePath[MAX_PATH];
  360. WCHAR rgwchSourceVolumeName[MAX_PATH];
  361. DWORD dwSourceVolumeSerialNumber;
  362. DWORD dwSourceMaximumComponentLength;
  363. DWORD dwSourceFileSystemFlags;
  364. WCHAR rgwchSourceFileSystemNameBuffer[MAX_PATH];
  365. WCHAR rgwchSourceRemoteName[MAX_PATH];
  366. DWORD dwSourceRemoteNameLength = NUMBER_OF(rgwchSourceRemoteName);
  367. UINT uiSourceDriveType;
  368. WCHAR rgwchDestinationVolumePath[MAX_PATH];
  369. WCHAR rgwchDestinationVolumeName[MAX_PATH];
  370. DWORD dwDestinationVolumeSerialNumber;
  371. DWORD dwDestinationMaximumComponentLength;
  372. DWORD dwDestinationFileSystemFlags;
  373. WCHAR rgwchDestinationFileSystemNameBuffer[MAX_PATH];
  374. WCHAR rgwchDestinationRemoteName[MAX_PATH];
  375. DWORD dwDestinationRemoteNameLength = NUMBER_OF(rgwchDestinationRemoteName);
  376. UINT uiDestinationDriveType;
  377. if (!::FusionpInitializeHeap(NULL))
  378. {
  379. TraceFailureContext("initializing the heap.\n");
  380. goto Exit;
  381. }
  382. iArg = 1;
  383. while (iArg < argc)
  384. {
  385. PCWSTR arg = argv[iArg];
  386. // Let's see if we see some switches...
  387. if ((arg[0] == L'-') || (arg[0] == L'/'))
  388. {
  389. arg++;
  390. if ((_wcsicmp(arg, L"threads") == 0) ||
  391. (_wcsicmp(arg, L"t") == 0))
  392. {
  393. PWSTR pszDummy;
  394. iArg++;
  395. if (iArg >= argc)
  396. break;
  397. g_nThreads = wcstol(argv[iArg], &pszDummy, 10);
  398. if (g_nThreads < 1)
  399. g_nThreads = 1;
  400. if (g_nThreads > RTL_NUMBER_OF(g_rghThreads))
  401. g_nThreads = RTL_NUMBER_OF(g_rghThreads);
  402. for (i=0; i<g_nThreads; i++)
  403. g_rghThreads[i] = NULL;
  404. iArg++;
  405. continue;
  406. }
  407. else if ((_wcsicmp(arg, L"quiet") == 0) ||
  408. (_wcsicmp(arg, L"q") == 0))
  409. {
  410. g_fSilent = true;
  411. iArg++;
  412. continue;
  413. }
  414. else if (_wcsicmp(arg, L"logfile") == 0)
  415. {
  416. iArg++;
  417. if (iArg >= argc)
  418. break;
  419. g_pLogFile = ::_wfopen(argv[iArg], L"w+");
  420. if (g_pLogFile == NULL)
  421. {
  422. ::perror("Error opening logfile");
  423. goto Exit;
  424. }
  425. iArg++;
  426. }
  427. }
  428. // This must be it! We hope; there should be two things left
  429. if ((iArg + 2) != argc)
  430. break;
  431. iSource = iArg;
  432. iDestination = iArg + 1;
  433. break;
  434. }
  435. if (iSource == 0)
  436. {
  437. fprintf(stderr,
  438. "%ls: usage:\n"
  439. " %ls [-threads n] [-quiet] <source> <dest>\n",
  440. argv[0], argv[0]);
  441. goto Exit;
  442. }
  443. // Abuse these buffers for debugging purposes...
  444. {
  445. HANDLE h;
  446. if (!::GetLogicalDriveStringsW(NUMBER_OF(rgwchSourceVolumePath), rgwchSourceVolumePath))
  447. {
  448. ::ReportFailure("GetLogicalDriveStringsW failed.");
  449. goto Exit;
  450. }
  451. h = ::FindFirstVolumeW(rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath));
  452. if (h == INVALID_HANDLE_VALUE)
  453. {
  454. ::ReportFailure("FindFirstVolumeW failed.");
  455. goto Exit;
  456. }
  457. for (;;)
  458. {
  459. DWORD cchReturnLength;
  460. if (!::GetVolumePathNamesForVolumeNameW(rgwchSourceVolumePath, rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath), &cchReturnLength))
  461. {
  462. ::ReportFailure("GetVolumePathNamesForVolumeNameW failed.");
  463. goto Exit;
  464. }
  465. if (!::FindNextVolumeW(h, rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath)))
  466. {
  467. const DWORD dwLastError = ::GetLastError();
  468. if (dwLastError != ERROR_NO_MORE_FILES)
  469. {
  470. ::ReportFailure("FindNextVolumeW failed.");
  471. goto Exit;
  472. }
  473. break;
  474. }
  475. }
  476. ::FindVolumeClose(h);
  477. }
  478. if (!::GetVolumePathNameW(argv[iSource], rgwchSourceVolumePath, NUMBER_OF(rgwchSourceVolumePath)))
  479. {
  480. ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed.", argv[iSource]);
  481. goto Exit;
  482. }
  483. uiSourceDriveType = ::GetDriveTypeW(rgwchSourceVolumePath);
  484. if (!::GetVolumePathNameW(argv[iDestination], rgwchDestinationVolumePath, NUMBER_OF(rgwchDestinationVolumePath)))
  485. {
  486. ::ReportFailure("GetVolumePathName(L\"%ls\", ...) failed.", argv[iDestination]);
  487. goto Exit;
  488. }
  489. uiDestinationDriveType = ::GetDriveTypeW(rgwchDestinationVolumePath);
  490. if (!::GetVolumeInformationW(
  491. rgwchSourceVolumePath,
  492. rgwchSourceVolumeName,
  493. NUMBER_OF(rgwchSourceVolumeName),
  494. &dwSourceVolumeSerialNumber,
  495. &dwSourceMaximumComponentLength,
  496. &dwSourceFileSystemFlags,
  497. rgwchSourceFileSystemNameBuffer,
  498. NUMBER_OF(rgwchSourceFileSystemNameBuffer)))
  499. {
  500. ::ReportFailure("GetVolumeInformation(L\"%ls\", ...) failed.", rgwchSourceVolumePath);
  501. goto Exit;
  502. }
  503. if (!::GetVolumeInformationW(
  504. rgwchDestinationVolumePath,
  505. rgwchDestinationVolumeName,
  506. NUMBER_OF(rgwchDestinationVolumeName),
  507. &dwDestinationVolumeSerialNumber,
  508. &dwDestinationMaximumComponentLength,
  509. &dwDestinationFileSystemFlags,
  510. rgwchDestinationFileSystemNameBuffer,
  511. NUMBER_OF(rgwchDestinationFileSystemNameBuffer)))
  512. {
  513. ::ReportFailure("GetVolumeInformation(L\"%ls\", ...) failed.", rgwchDestinationVolumePath);
  514. goto Exit;
  515. }
  516. if (!::GetDiskFreeSpaceW(
  517. rgwchDestinationVolumePath,
  518. &g_dwDestinationSectorsPerCluster,
  519. &g_dwDestinationBytesPerSector,
  520. &g_dwDestinationNumberOfFreeClusters,
  521. &g_dwDestinationTotalNumberOfClusters))
  522. {
  523. ::ReportFailure("GetDiskFreeSpaceW(L\"%ls\", ...) failed.", rgwchDestinationVolumePath);
  524. goto Exit;
  525. }
  526. if (!::FusionpInitializeCriticalSection(&g_cs))
  527. goto Exit;
  528. g_pFiles = new CFileIdHashTable;
  529. g_pDirs = new CDeque<CDir, offsetof(CDir, m_linkage)>;
  530. g_pFileCopies = new CDeque<CFileCopy, offsetof(CFileCopy, m_linkage)>;
  531. g_pFileLinks = new CDeque<CFileLink, offsetof(CFileLink, m_linkage)>;
  532. g_pszImage = wcsrchr(argv[0], L'\\');
  533. if (g_pszImage == NULL)
  534. g_pszImage = argv[0];
  535. else
  536. g_pszImage++;
  537. g_hIoCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, g_nThreads);
  538. if (g_hIoCompletionPort == NULL)
  539. {
  540. ::ReportFailure("Creating I/O Completion Port with %lu concurrent threads failed.", g_nThreads);
  541. goto Exit;
  542. }
  543. g_hWorkItemDoneEvent = ::CreateEventW(NULL, FALSE, FALSE, NULL);
  544. if (g_hWorkItemDoneEvent == NULL)
  545. {
  546. ::ReportFailure("Creating the file copied event failed.");
  547. goto Exit;
  548. }
  549. for (i=0; i<g_nThreads; i++)
  550. {
  551. g_rghThreads[i] = ::CreateThread(NULL, 0, &WorkerThreadProc, NULL, CREATE_SUSPENDED, NULL);
  552. if (g_rghThreads[i] == NULL)
  553. {
  554. ::ReportFailure("Creating worker thread number %lu failed.", i);
  555. goto Exit;
  556. }
  557. }
  558. ::GetSystemTime(&stStart);
  559. if (!buffSource.Win32Assign(argv[iSource], wcslen(argv[iSource])))
  560. goto Exit;
  561. if (!buffDestination.Win32Assign(argv[iDestination], wcslen(argv[iDestination])))
  562. goto Exit;
  563. if (!::BuildDirList(buffSource, buffDestination))
  564. goto Exit;
  565. if (!g_fSilent)
  566. printf("%ls: Found %Iu directories.\n", g_pszImage, g_pDirs->GetEntryCount());
  567. if (!::QueueDirScans())
  568. goto Exit;
  569. if (!::WaitForWorkersToComplete(g_DirScansProcessed, g_pDirs->GetEntryCount(), "Directory scans"))
  570. goto Exit;
  571. ::GetSystemTime(&stAfterScan);
  572. if (!g_fSilent)
  573. {
  574. printf("%ls: Copying %Iu files (%I64u bytes)\n", g_pszImage, g_pFileCopies->GetEntryCount(), g_BytesToCopy);
  575. printf("%ls: Linking %Iu files\n", g_pszImage, g_pFileLinks->GetEntryCount());
  576. }
  577. if (!::MakeDirectoryStructure())
  578. goto Exit;
  579. if (!g_fSilent)
  580. printf("%ls: Created %Iu directories.\n", g_pszImage, g_pDirs->GetEntryCount());
  581. ::GetSystemTime(&stAfterDirCreation);
  582. if (!::QueueFileCopies())
  583. goto Exit;
  584. if (!::WaitForWorkersToComplete(g_FileCopiesProcessed, g_pFileCopies->GetEntryCount(), "File copies"))
  585. goto Exit;
  586. ::GetSystemTime(&stAfterCopies);
  587. ::qsort(g_prgpFileCopies, g_pFileCopies->GetEntryCount(), sizeof(CFileCopy *), &CFileCopy::QSortBySize);
  588. if (g_pLogFile != NULL)
  589. {
  590. LARGE_INTEGER liFreq;
  591. ULONGLONG ullFreqDiv100;
  592. CStringBuffer buffDestination;
  593. ::QueryPerformanceFrequency(&liFreq);
  594. ullFreqDiv100 = (liFreq.QuadPart / 100);
  595. for (i=0; i<g_pFileCopies->GetEntryCount(); i++)
  596. {
  597. ULONGLONG diff = g_prgpFileCopies[i]->m_ullEnd - g_prgpFileCopies[i]->m_ullStart;
  598. diff = diff / ullFreqDiv100;
  599. if (!g_prgpFileCopies[i]->GetDestination(buffDestination))
  600. goto Exit;
  601. fprintf(g_pLogFile, "%ls,%Iu,%Iu\n", static_cast<PCWSTR>(buffDestination), diff, g_prgpFileCopies[i]->m_cbSize);
  602. }
  603. }
  604. if (!::QueueFileLinks())
  605. goto Exit;
  606. if (!::WaitForWorkersToComplete(g_FileLinksProcessed, g_pFileLinks->GetEntryCount(), "File links"))
  607. goto Exit;
  608. ::GetSystemTime(&stAfterLinks);
  609. ::GetSystemTime(&stEnd);
  610. ::ComputeTimeDeltas(stStart, stAfterScan, stAfterScanDelta);
  611. ::ComputeTimeDeltas(stAfterScan, stAfterDirCreation, stAfterDirCreationDelta);
  612. ::ComputeTimeDeltas(stAfterDirCreation, stAfterCopies, stAfterCopiesDelta);
  613. ::ComputeTimeDeltas(stAfterCopies, stAfterLinks, stAfterLinksDelta);
  614. ::ComputeTimeDeltas(stStart, stEnd, stEndDelta);
  615. printf(
  616. "%ls: Statistics:\n"
  617. " Directories Copied: %I64u\n"
  618. " Files Copied: %I64u\n"
  619. " Bytes Copied: %I64u\n"
  620. " Files Linked: %I64u\n"
  621. " Copies Skipped: %I64u\n"
  622. " Links Skipped: %I64u\n",
  623. g_pszImage,
  624. g_DirectoriesCopied,
  625. g_FilesCopied,
  626. g_BytesCopied,
  627. g_FilesLinked,
  628. g_CopiesSkipped,
  629. g_LinksSkipped);
  630. printf(
  631. " Times:\n"
  632. " Scan: %u:%02u:%02u.%03u\n"
  633. " Directory Creation: %u:%02u:%02u.%03u\n"
  634. " Copying Files: %u:%02u:%02u.%03u\n"
  635. " Linking Files: %u:%02u:%02u.%03u\n"
  636. " Total: %u:%02u:%02u.%03u\n",
  637. stAfterScanDelta.wHour, stAfterScanDelta.wMinute, stAfterScanDelta.wSecond, stAfterScanDelta.wMilliseconds,
  638. stAfterDirCreationDelta.wHour, stAfterDirCreationDelta.wMinute, stAfterDirCreationDelta.wSecond, stAfterDirCreationDelta.wMilliseconds,
  639. stAfterCopiesDelta.wHour, stAfterCopiesDelta.wMinute, stAfterCopiesDelta.wSecond, stAfterCopiesDelta.wMilliseconds,
  640. stAfterLinksDelta.wHour, stAfterLinksDelta.wMinute, stAfterLinksDelta.wSecond, stAfterLinksDelta.wMilliseconds,
  641. stEndDelta.wHour, stEndDelta.wMinute, stEndDelta.wSecond, stEndDelta.wMilliseconds
  642. );
  643. ullTemp = (((((stAfterCopiesDelta.wHour * 60) + stAfterCopiesDelta.wMinute) * 60) + stAfterCopiesDelta.wSecond) * 1000) + stAfterCopiesDelta.wMilliseconds;
  644. if (ullTemp != 0)
  645. {
  646. ULONGLONG ullFilesPerMS = ((g_FilesCopied * 1000000ui64) / ullTemp);
  647. ULONGLONG ullBytesPerMS = ((g_BytesCopied * 1000000ui64) / ullTemp);
  648. printf(
  649. " Files copied per second: %I64u.%03u\n"
  650. " Bytes copied per second: %I64u.%03u\n",
  651. static_cast<ULONGLONG>(ullFilesPerMS / 1000ui64), static_cast<ULONG>(ullFilesPerMS % 1000ui64),
  652. static_cast<ULONGLONG>(ullBytesPerMS / 1000ui64), static_cast<ULONG>(ullBytesPerMS % 1000ui64));
  653. }
  654. ullTemp = (((stAfterLinksDelta.wHour * 60) + stAfterLinksDelta.wMinute) * 60) + stAfterLinksDelta.wSecond;
  655. if (ullTemp != 0)
  656. {
  657. printf(
  658. " Files linked per second: %I64u\n",
  659. static_cast<ULONGLONG>(g_FilesLinked / ullTemp));
  660. }
  661. ullTemp = (((stEndDelta.wHour * 60) + stEndDelta.wMinute) * 60) + stEndDelta.wSecond;
  662. if (ullTemp != 0)
  663. {
  664. printf(
  665. " Overall files per second: %I64u\n",
  666. static_cast<ULONGLONG>((g_FilesCopied + g_CopiesSkipped + g_FilesLinked + g_LinksSkipped + g_pDirs->GetEntryCount()) / ullTemp));
  667. }
  668. iReturnStatus = EXIT_SUCCESS;
  669. Exit:
  670. // Wake the children; process termination doesn't seem to work if we don't.
  671. ::ResumeWorkerThreads();
  672. if (g_pLogFile != NULL)
  673. {
  674. fflush(g_pLogFile);
  675. fclose(g_pLogFile);
  676. }
  677. return iReturnStatus;
  678. }
  679. BOOL
  680. ResumeWorkerThreads()
  681. {
  682. BOOL fSuccess = FALSE;
  683. ULONG i;
  684. for (i=0; i<g_nThreads; i++)
  685. {
  686. if ((g_rghThreads[i] != NULL) && (g_rghThreads[i] != INVALID_HANDLE_VALUE))
  687. {
  688. if (::ResumeThread(g_rghThreads[i]) == -1)
  689. {
  690. ::ReportFailure("Failed to resume worker thread %lu.", i + 1);
  691. goto Exit;
  692. }
  693. }
  694. }
  695. fSuccess = TRUE;
  696. Exit:
  697. return fSuccess;
  698. }
  699. BOOL
  700. SuspendWorkerThreads()
  701. {
  702. BOOL fSuccess = FALSE;
  703. ULONG i;
  704. for (i=0; i<g_nThreads; i++)
  705. {
  706. if (::SuspendThread(g_rghThreads[i]) == -1)
  707. {
  708. ::ReportFailure("Failed to suspend worker thread %lu.", i + 1);
  709. goto Exit;
  710. }
  711. }
  712. fSuccess = TRUE;
  713. Exit:
  714. return fSuccess;
  715. }
  716. BOOL
  717. WaitForWorkersToComplete(
  718. ULONGLONG &rullCounter,
  719. ULONGLONG ullLimit,
  720. PCSTR pszOperationName
  721. )
  722. {
  723. BOOL fSuccess = FALSE;
  724. ULONGLONG i;
  725. ULONGLONG ullCounterNextNotify = rullCounter + WAIT_NOTIFY_COUNTER;
  726. ULONGLONG ullCounter;
  727. if (!::ResumeWorkerThreads())
  728. goto Exit;
  729. i = 0;
  730. while (rullCounter < ullLimit)
  731. {
  732. // Don't wake more than every tenth of a second...
  733. ::Sleep(100);
  734. DWORD dwWFSO = ::WaitForSingleObject(g_hWorkItemDoneEvent, INFINITE);
  735. if (dwWFSO == WAIT_FAILED)
  736. {
  737. ::ReportFailure("Waiting for work item done event (%p) failed.", g_hWorkItemDoneEvent);
  738. goto Exit;
  739. }
  740. i++;
  741. ullCounter = rullCounter;
  742. if (ullCounter >= ullCounterNextNotify)
  743. {
  744. ullCounterNextNotify = ullCounter + WAIT_NOTIFY_COUNTER;
  745. if (!g_fSilent)
  746. printf("%ls: %s processed (%I64u total): %I64u\n", g_pszImage, pszOperationName, ullLimit, ullCounter);
  747. }
  748. }
  749. if (!::SuspendWorkerThreads())
  750. goto Exit;
  751. fSuccess = TRUE;
  752. Exit:
  753. return fSuccess;
  754. }
  755. void
  756. ComputeTimeDeltas(
  757. const SYSTEMTIME &rstStart,
  758. const SYSTEMTIME &rstEnd,
  759. SYSTEMTIME &rstDelta
  760. )
  761. {
  762. FILETIME ftStart, ftEnd;
  763. ULARGE_INTEGER uliStart, uliEnd;
  764. ULONGLONG ullDiff, ullTemp;
  765. ULONG ulHours, ulMinutes, ulSeconds, ulMilliseconds;
  766. ::SystemTimeToFileTime(&rstStart, &ftStart);
  767. ::SystemTimeToFileTime(&rstEnd, &ftEnd);
  768. uliStart.LowPart = ftStart.dwLowDateTime;
  769. uliStart.HighPart = ftStart.dwHighDateTime;
  770. uliEnd.LowPart = ftEnd.dwLowDateTime;
  771. uliEnd.HighPart = ftEnd.dwHighDateTime;
  772. ullDiff = (uliEnd.QuadPart - uliStart.QuadPart);
  773. ulHours = (ULONG) (ullDiff / (10000000ui64 * 60 * 60));
  774. ullTemp = ullDiff - ((ULONGLONG) ulHours) * (10000000ui64 * 60 * 60);
  775. ulMinutes = (ULONG) (ullTemp / (10000000ui64 * 60));
  776. ullTemp -= ((ULONGLONG) ulMinutes) * (10000000ui64 * 60);
  777. ulSeconds = (ULONG) (ullTemp / 10000000ui64);
  778. ullTemp -= ((ULONGLONG) ulSeconds) * 10000000ui64;
  779. ulMilliseconds = (ULONG) (ullTemp / 10000ui64);
  780. rstDelta.wYear = 0;
  781. rstDelta.wMonth = 0;
  782. rstDelta.wDayOfWeek = 0;
  783. rstDelta.wDay = 0;
  784. rstDelta.wHour = (WORD) ulHours;
  785. rstDelta.wMinute = (WORD) ulMinutes;
  786. rstDelta.wSecond = (WORD) ulSeconds;
  787. rstDelta.wMilliseconds = (WORD) ulMilliseconds;
  788. }
  789. BOOL
  790. BuildDirList(
  791. const CBaseStringBuffer &rbuffSource,
  792. const CBaseStringBuffer &rbuffDestination
  793. )
  794. {
  795. PCWSTR szSrc = rbuffSource;
  796. BOOL fSuccess = FALSE;
  797. DWORD dwFileAttributes;
  798. CStringBuffer buffSrc;
  799. CStringBuffer buffSrcWildcard;
  800. CStringBuffer buffDst;
  801. WIN32_FIND_DATAW wfd;
  802. HANDLE hFind = INVALID_HANDLE_VALUE;
  803. HANDLE hFile = INVALID_HANDLE_VALUE;
  804. SIZE_T cchSrc, cchDst;
  805. CDir *pDir = NULL;
  806. dwFileAttributes = ::GetFileAttributesW(rbuffSource);
  807. if (dwFileAttributes == ((DWORD) -1))
  808. {
  809. ::ReportFailure("GetFileAttributesW() on the source \"%ls\" failed.", szSrc);
  810. goto Exit;
  811. }
  812. if (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  813. {
  814. ::SetLastError(ERROR_INVALID_PARAMETER);
  815. ::ReportFailure("Source directory \"%ls\" is not a directory!");
  816. goto Exit;
  817. }
  818. if (!buffSrc.Win32Assign(rbuffSource))
  819. goto Exit;
  820. if (!buffSrc.Win32EnsureTrailingPathSeparator())
  821. goto Exit;
  822. if (!buffDst.Win32Assign(rbuffDestination))
  823. goto Exit;
  824. if (!buffDst.Win32EnsureTrailingPathSeparator())
  825. goto Exit;
  826. cchSrc = buffSrc.Cch();
  827. cchDst = buffDst.Cch();
  828. pDir = new CDir;
  829. if (pDir == NULL)
  830. {
  831. ::SetLastError(ERROR_OUTOFMEMORY);
  832. ::ReportFailure("Failed to allocate new CDir object.");
  833. goto Exit;
  834. }
  835. if (!pDir->Initialize(buffSrc, buffDst))
  836. goto Exit;
  837. g_pDirs->AddToTail(pDir);
  838. pDir = NULL;
  839. if ((g_pDirs->GetEntryCount() % 50) == 0)
  840. {
  841. if (!g_fSilent)
  842. printf("%ls: Found %Iu directories...\n", g_pszImage, g_pDirs->GetEntryCount());
  843. }
  844. if (!buffSrcWildcard.Win32Assign(buffSrc))
  845. goto Exit;
  846. if (!buffSrcWildcard.Win32Append(L"*", 1))
  847. goto Exit;
  848. if ((hFind = ::FindFirstFileExW(
  849. buffSrcWildcard,
  850. FindExInfoStandard,
  851. &wfd,
  852. FindExSearchLimitToDirectories,
  853. NULL,
  854. 0)) == INVALID_HANDLE_VALUE)
  855. {
  856. const DWORD dwLastError = ::GetLastError();
  857. if (dwLastError != ERROR_NO_MORE_FILES)
  858. {
  859. ::ReportFailure("FindFirstFileW(L\"%ls\", ...) failed.", static_cast<PCWSTR>(buffSrcWildcard));
  860. goto Exit;
  861. }
  862. }
  863. for (;;)
  864. {
  865. if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  866. (wcscmp(wfd.cFileName, L".") != 0) &&
  867. (wcscmp(wfd.cFileName, L"..") != 0))
  868. {
  869. buffSrc.Left(cchSrc);
  870. buffDst.Left(cchDst);
  871. if (!buffSrc.Win32Append(wfd.cFileName, wcslen(wfd.cFileName)))
  872. goto Exit;
  873. if (!buffDst.Win32Append(wfd.cFileName, wcslen(wfd.cFileName)))
  874. goto Exit;
  875. if (!::BuildDirList(buffSrc, buffDst))
  876. goto Exit;
  877. }
  878. if (!::FindNextFileW(hFind, &wfd))
  879. {
  880. if (::GetLastError() == ERROR_NO_MORE_FILES)
  881. break;
  882. ::ReportFailure("FindNextFileW(%p, %p) failed.", hFind, &wfd);
  883. goto Exit;
  884. }
  885. }
  886. fSuccess = TRUE;
  887. Exit:
  888. if (hFind != INVALID_HANDLE_VALUE)
  889. {
  890. CSxsPreserveLastError ple;
  891. ::FindClose(hFind);
  892. ple.Restore();
  893. }
  894. if (pDir != NULL)
  895. delete pDir;
  896. return fSuccess;
  897. }
  898. BOOL
  899. BuildDirFileList(
  900. CSxsLockCriticalSection &rlcs,
  901. CDir *pDir
  902. )
  903. {
  904. PCWSTR szSrc = pDir->m_buffSource;
  905. BOOL fSuccess = FALSE;
  906. DWORD dwFileAttributes;
  907. CStringBuffer buffSrc;
  908. CStringBuffer buffSrcWildcard;
  909. CStringBuffer buffDst;
  910. WIN32_FIND_DATAW wfd;
  911. HANDLE hFind = INVALID_HANDLE_VALUE;
  912. HANDLE hFile = INVALID_HANDLE_VALUE;
  913. SIZE_T cchSrc, cchDst;
  914. CFileLink *pFileLink = NULL;
  915. CFileCopy *pFileCopy = NULL;
  916. dwFileAttributes = ::GetFileAttributesW(pDir->m_buffSource);
  917. if (dwFileAttributes == ((DWORD) -1))
  918. {
  919. ::ReportFailure("GetFileAttributesW() on the source \"%ls\" failed.", szSrc);
  920. goto Exit;
  921. }
  922. if (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  923. {
  924. ::SetLastError(ERROR_INVALID_PARAMETER);
  925. ::ReportFailure("Source directory \"%ls\" is not a directory!", szSrc);
  926. goto Exit;
  927. }
  928. if (!buffSrc.Win32Assign(pDir->m_buffSource))
  929. goto Exit;
  930. if (!buffSrc.Win32EnsureTrailingPathSeparator())
  931. goto Exit;
  932. if (!buffDst.Win32Assign(pDir->m_buffDestination))
  933. goto Exit;
  934. if (!buffDst.Win32EnsureTrailingPathSeparator())
  935. goto Exit;
  936. cchSrc = buffSrc.Cch();
  937. cchDst = buffDst.Cch();
  938. if (!buffSrcWildcard.Win32Assign(buffSrc))
  939. goto Exit;
  940. if (!buffSrcWildcard.Win32Append(L"*", 1))
  941. goto Exit;
  942. if ((hFind = ::FindFirstFileW(buffSrcWildcard, &wfd)) == INVALID_HANDLE_VALUE)
  943. {
  944. const DWORD dwLastError = ::GetLastError();
  945. if (::GetLastError() != ERROR_NO_MORE_FILES)
  946. {
  947. ::ReportFailure("FindFirstFileW(L\"%ls\", ...) failed.", static_cast<PCWSTR>(buffSrcWildcard));
  948. goto Exit;
  949. }
  950. }
  951. for (;;)
  952. {
  953. if ((wcscmp(wfd.cFileName, L".") != 0) && (wcscmp(wfd.cFileName, L"..") != 0))
  954. {
  955. buffSrc.Left(cchSrc);
  956. buffDst.Left(cchDst);
  957. if (!buffSrc.Win32Append(wfd.cFileName, wcslen(wfd.cFileName)))
  958. goto Exit;
  959. if (!buffDst.Win32Append(wfd.cFileName, wcslen(wfd.cFileName)))
  960. goto Exit;
  961. dwFileAttributes = ::GetFileAttributesW(buffSrc);
  962. if (dwFileAttributes == ((DWORD) -1))
  963. {
  964. ::ReportFailure("GetFileAttribuesW(L\"%ls\") failed.", static_cast<PCWSTR>(buffSrc));
  965. goto Exit;
  966. }
  967. if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  968. {
  969. BY_HANDLE_FILE_INFORMATION bhfi;
  970. ULONGLONG ullFileIndex;
  971. ULONGLONG ullFileSize;
  972. CFileCopy **ppFileCopy = NULL;
  973. hFile = ::CreateFileW(buffSrc, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  974. if (hFile == INVALID_HANDLE_VALUE)
  975. {
  976. ::ReportFailure("CreateFileW(L\"%ls\", ...) failed.", static_cast<PCWSTR>(buffSrc));
  977. goto Exit;
  978. }
  979. if (!::GetFileInformationByHandle(hFile, &bhfi))
  980. {
  981. ::ReportFailure("Failed to GetFileInformationByHandle(%p, ...) on file \"%ls\".", hFile, static_cast<PCWSTR>(buffSrc));
  982. goto Exit;
  983. }
  984. ::CloseHandle(hFile);
  985. hFile = INVALID_HANDLE_VALUE;
  986. ullFileIndex = (((ULONGLONG) bhfi.nFileIndexHigh) << 32) | ((ULONGLONG) bhfi.nFileIndexLow);
  987. ullFileSize = (((ULONGLONG) bhfi.nFileSizeHigh) << 32) | ((ULONGLONG) bhfi.nFileSizeLow);
  988. g_BytesToCopy += ullFileSize;
  989. if (!rlcs.Lock())
  990. {
  991. ::ReportFailure("Failed to lock global critical section.");
  992. goto Exit;
  993. }
  994. if (!g_pFiles->Find(ullFileIndex, ppFileCopy))
  995. {
  996. ::ReportFailure("Finding file index %I64u in file table failed.", ullFileIndex);
  997. goto Exit;
  998. }
  999. if (ppFileCopy != NULL)
  1000. {
  1001. pFileLink = new CFileLink;
  1002. if (pFileLink == NULL)
  1003. {
  1004. ::SetLastError(ERROR_OUTOFMEMORY);
  1005. goto Exit;
  1006. }
  1007. if (!pFileLink->Initialize(
  1008. pDir,
  1009. wfd.cFileName,
  1010. bhfi.ftCreationTime,
  1011. bhfi.ftLastAccessTime,
  1012. bhfi.ftLastWriteTime,
  1013. ullFileIndex,
  1014. ullFileSize))
  1015. goto Exit;
  1016. g_pFileLinks->AddToTail(pFileLink);
  1017. pFileLink = NULL;
  1018. }
  1019. else
  1020. {
  1021. pFileCopy = new CFileCopy;
  1022. if (pFileCopy == NULL)
  1023. {
  1024. ::SetLastError(ERROR_OUTOFMEMORY);
  1025. ::ReportFailure("Allocating new CFileCopy object failed.");
  1026. goto Exit;
  1027. }
  1028. if (!pFileCopy->Initialize(
  1029. pDir,
  1030. wfd.cFileName,
  1031. bhfi.ftCreationTime,
  1032. bhfi.ftLastAccessTime,
  1033. bhfi.ftLastWriteTime,
  1034. ullFileIndex,
  1035. ullFileSize))
  1036. goto Exit;
  1037. if (!g_pFiles->Insert(ullFileIndex, pFileCopy))
  1038. goto Exit;
  1039. g_pFileCopies->AddToTail(pFileCopy);
  1040. pFileCopy = NULL;
  1041. }
  1042. rlcs.Unlock();
  1043. }
  1044. }
  1045. if (!::FindNextFileW(hFind, &wfd))
  1046. {
  1047. const DWORD dwLastError = ::GetLastError();
  1048. if (dwLastError == ERROR_NO_MORE_FILES)
  1049. break;
  1050. ::ReportFailure("FindNextFileW() call failed.");
  1051. goto Exit;
  1052. }
  1053. }
  1054. fSuccess = TRUE;
  1055. Exit:
  1056. if (hFind != INVALID_HANDLE_VALUE)
  1057. {
  1058. CSxsPreserveLastError ple;
  1059. ::FindClose(hFind);
  1060. ple.Restore();
  1061. }
  1062. if (pFileLink != NULL)
  1063. delete pFileLink;
  1064. if (pFileCopy != NULL)
  1065. delete pFileCopy;
  1066. return fSuccess;
  1067. }
  1068. BOOL
  1069. MakeDirectoryStructure()
  1070. {
  1071. BOOL fSuccess = FALSE;
  1072. CDequeIterator<CDir, offsetof(CDir, m_linkage)> dirIter;
  1073. dirIter.Rebind(g_pDirs);
  1074. for (dirIter.Reset(); dirIter.More(); dirIter.Next())
  1075. {
  1076. if (!::CreateDirectoryW(dirIter->m_buffDestination, NULL))
  1077. {
  1078. const DWORD dwLastError = ::GetLastError();
  1079. if (dwLastError != ERROR_ALREADY_EXISTS)
  1080. {
  1081. ::ReportFailure("Unable to create directory \"%ls\".", static_cast<PCWSTR>(dirIter->m_buffDestination));
  1082. goto Exit;
  1083. }
  1084. }
  1085. }
  1086. fSuccess = TRUE;
  1087. Exit:
  1088. return fSuccess;
  1089. }
  1090. void
  1091. ReportFailure(
  1092. const char szFormat[],
  1093. ...
  1094. )
  1095. {
  1096. const DWORD dwLastError = ::GetLastError();
  1097. va_list ap;
  1098. char rgchBuffer[4096];
  1099. WCHAR rgchWin32Error[4096];
  1100. rgchBuffer[0] = 0;
  1101. rgchWin32Error[0] = 0;
  1102. va_start(ap, szFormat);
  1103. _vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), szFormat, ap);
  1104. rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = 0;
  1105. va_end(ap);
  1106. if (!::FormatMessageW(
  1107. FORMAT_MESSAGE_FROM_SYSTEM,
  1108. NULL,
  1109. dwLastError,
  1110. 0,
  1111. rgchWin32Error,
  1112. NUMBER_OF(rgchWin32Error),
  1113. &ap))
  1114. {
  1115. const DWORD dwLastError2 = ::GetLastError();
  1116. _snwprintf(rgchWin32Error, NUMBER_OF(rgchWin32Error), L"Error formatting Win32 error %lu\nError from FormatMessage is %lu", dwLastError, dwLastError2);
  1117. }
  1118. rgchWin32Error[NUMBER_OF(rgchWin32Error) - 1] = 0;
  1119. fprintf(stderr, "%ls: %s\n%ls\n", g_pszImage, rgchBuffer, rgchWin32Error);
  1120. }
  1121. void
  1122. TraceFailureContext(
  1123. const char szFormat[],
  1124. ...
  1125. )
  1126. {
  1127. const DWORD dwLastError = ::GetLastError();
  1128. va_list ap;
  1129. char rgchBuffer[4096];
  1130. WCHAR rgchWin32Error[4096];
  1131. rgchBuffer[0] = 0;
  1132. rgchWin32Error[0] = 0;
  1133. va_start(ap, szFormat);
  1134. _vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), szFormat, ap);
  1135. rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = 0;
  1136. va_end(ap);
  1137. if (!::FormatMessageW(
  1138. FORMAT_MESSAGE_FROM_SYSTEM,
  1139. NULL,
  1140. dwLastError,
  1141. 0,
  1142. rgchWin32Error,
  1143. NUMBER_OF(rgchWin32Error),
  1144. &ap))
  1145. {
  1146. const DWORD dwLastError2 = ::GetLastError();
  1147. _snwprintf(rgchWin32Error, NUMBER_OF(rgchWin32Error), L"Error formatting Win32 error %lu\nError from FormatMessage is %lu", dwLastError, dwLastError2);
  1148. }
  1149. rgchWin32Error[NUMBER_OF(rgchWin32Error) - 1] = 0;
  1150. RemoveTrailingWhitespaceW(rgchWin32Error);
  1151. fprintf(stderr, "%ls (0x%lx) while %s\n", rgchWin32Error, dwLastError, rgchBuffer);
  1152. }
  1153. DWORD
  1154. WINAPI
  1155. WorkerThreadProc(
  1156. LPVOID pvParameter
  1157. )
  1158. {
  1159. ULONG nThread = (ULONG)(ULONG_PTR)pvParameter;
  1160. DWORD dwReturnValue = 0;
  1161. BYTE *pBuffer = NULL;
  1162. DWORD cbBuffer;
  1163. cbBuffer = 8192 * 32;
  1164. pBuffer = (BYTE *) ::VirtualAlloc(NULL, cbBuffer, MEM_COMMIT, PAGE_READWRITE);
  1165. if (pBuffer == NULL)
  1166. {
  1167. ReportFailure("VirtualAlloc() for thread %lu failed.", nThread);
  1168. goto Exit;
  1169. }
  1170. for (;;)
  1171. {
  1172. DWORD nBytes = 0;
  1173. ULONG_PTR ulpCompletionKey = 0;
  1174. LPOVERLAPPED lpo = NULL;
  1175. CEntry *pEntry = NULL;
  1176. bool fReQueue = false;
  1177. if (!::GetQueuedCompletionStatus(g_hIoCompletionPort, &nBytes, &ulpCompletionKey, &lpo, INFINITE))
  1178. {
  1179. ReportFailure("Thread %lu failed call to GetQueuedCompletionStatus(%p, ...).", nThread, g_hIoCompletionPort);
  1180. dwReturnValue = ::GetLastError();
  1181. goto Exit;
  1182. }
  1183. pEntry = (CEntry *) lpo;
  1184. DoItAgain:
  1185. if (!pEntry->BaseDoYourThing(fReQueue, pBuffer, cbBuffer))
  1186. goto Exit;
  1187. if (fReQueue)
  1188. {
  1189. if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, NULL, lpo))
  1190. {
  1191. ReportFailure("Thread %lu failed to requeue item; retrying directly.", nThread);
  1192. goto DoItAgain;
  1193. }
  1194. }
  1195. ::SetEvent(g_hWorkItemDoneEvent);
  1196. }
  1197. Exit:
  1198. // Heavy handed but what else can we do?
  1199. ::ExitProcess(dwReturnValue);
  1200. return dwReturnValue;
  1201. }
  1202. BOOL
  1203. QueueDirScans()
  1204. {
  1205. BOOL fSuccess = FALSE;
  1206. SIZE_T i;
  1207. g_prgpDirs = new (CDir *[g_pDirs->GetEntryCount()]);
  1208. CDequeIterator<CDir, offsetof(CDir, m_linkage)> dirIter;
  1209. dirIter.Rebind(g_pDirs);
  1210. for (dirIter.Reset(), i=0; dirIter.More(); dirIter.Next(), i++)
  1211. {
  1212. g_prgpDirs[i] = dirIter;
  1213. if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) dirIter.Current()))
  1214. {
  1215. ReportFailure("Failed to queue dir scan.");
  1216. goto Exit;
  1217. }
  1218. }
  1219. fSuccess = TRUE;
  1220. Exit:
  1221. return fSuccess;
  1222. }
  1223. BOOL
  1224. QueueFileCopies()
  1225. {
  1226. BOOL fSuccess = FALSE;
  1227. SIZE_T i;
  1228. FN_TRACE_WIN32(fSuccess);
  1229. CDequeIterator<CFileCopy, offsetof(CFileCopy, m_linkage)> fileIter;
  1230. g_prgpFileCopies = new (CFileCopy *[g_pFileCopies->GetEntryCount()]);
  1231. if (g_prgpFileCopies == NULL)
  1232. {
  1233. ReportFailure("Failed to allocate file copy list.");
  1234. goto Exit;
  1235. }
  1236. fileIter.Rebind(g_pFileCopies);
  1237. i=0;
  1238. for (fileIter.Reset(); fileIter.More(); fileIter.Next())
  1239. {
  1240. if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) fileIter.Current()))
  1241. {
  1242. ReportFailure("Failed to queue file copy.");
  1243. goto Exit;
  1244. }
  1245. g_prgpFileCopies[i++] = fileIter;
  1246. }
  1247. fSuccess = TRUE;
  1248. Exit:
  1249. return fSuccess;
  1250. }
  1251. BOOL
  1252. QueueFileLinks()
  1253. {
  1254. BOOL fSuccess = FALSE;
  1255. SIZE_T i;
  1256. CDequeIterator<CFileLink, offsetof(CFileLink, m_linkage)> fileIter;
  1257. g_prgpFileLinks = new (CFileLink *[g_pFileLinks->GetEntryCount()]);
  1258. if (g_prgpFileLinks == NULL)
  1259. {
  1260. ReportFailure("Failed to allocate file link list.");
  1261. goto Exit;
  1262. }
  1263. fileIter.Rebind(g_pFileLinks);
  1264. i=0;
  1265. for (fileIter.Reset(); fileIter.More(); fileIter.Next())
  1266. {
  1267. if (!::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, 0, (LPOVERLAPPED) fileIter.Current()))
  1268. {
  1269. ReportFailure("Failed to queue file link.");
  1270. goto Exit;
  1271. }
  1272. g_prgpFileLinks[i++] = fileIter;
  1273. }
  1274. fSuccess = TRUE;
  1275. Exit:
  1276. return fSuccess;
  1277. }
  1278. BOOL
  1279. ProcessDirScan(
  1280. CDir *pDir,
  1281. bool &rfReQueue
  1282. )
  1283. {
  1284. BOOL fSuccess = FALSE;
  1285. CSxsLockCriticalSection l(g_cs);
  1286. rfReQueue = false;
  1287. if (!::BuildDirFileList(l, pDir))
  1288. goto Exit;
  1289. if (!l.Lock())
  1290. {
  1291. ReportFailure("Failed to lock global critical section.");
  1292. goto Exit;
  1293. }
  1294. g_DirScansProcessed++;
  1295. l.Unlock();
  1296. fSuccess = TRUE;
  1297. Exit:
  1298. return fSuccess;
  1299. }
  1300. BOOL
  1301. ProcessFileCopy(
  1302. CFileCopy *pFile,
  1303. bool &rfReQueue,
  1304. BYTE *pBuffer,
  1305. DWORD cbBuffer
  1306. )
  1307. {
  1308. BOOL fSuccess = FALSE;
  1309. FN_TRACE_WIN32(fSuccess);
  1310. CSxsLockCriticalSection l(g_cs);
  1311. bool fDoCopy = true;
  1312. DWORD dwFileAttributes;
  1313. HANDLE hFile = INVALID_HANDLE_VALUE;
  1314. CStringBuffer buffSource, buffDestination;
  1315. rfReQueue = false;
  1316. // If we're about to copy it, it's worth taking a look at the target to see if it's more-or-less
  1317. // out of date.
  1318. WIN32_FILE_ATTRIBUTE_DATA wfad;
  1319. IFW32FALSE_EXIT(pFile->GetSource(buffSource));
  1320. IFW32FALSE_EXIT(pFile->GetDestination(buffDestination));
  1321. if (!::GetFileAttributesExW(buffDestination, GetFileExInfoStandard, &wfad))
  1322. {
  1323. const DWORD dwLastError = ::GetLastError();
  1324. if (dwLastError != ERROR_FILE_NOT_FOUND)
  1325. {
  1326. ::ReportFailure("Error opening target file \"%ls\".", static_cast<PCWSTR>(buffDestination));
  1327. goto Exit;
  1328. }
  1329. wfad.dwFileAttributes = ((DWORD) -1);
  1330. }
  1331. else
  1332. {
  1333. ULONGLONG cbFileSize = (wfad.nFileSizeHigh << 32) | wfad.nFileSizeLow;
  1334. ASSERT_NTC(cbFileSize >= wfad.nFileSizeLow);
  1335. if ((cbFileSize == pFile->m_cbSize) &&
  1336. (wfad.ftCreationTime == pFile->m_ftSourceCreationTime))
  1337. {
  1338. pFile->m_fSkipped = true;
  1339. fDoCopy = false;
  1340. }
  1341. }
  1342. if (fDoCopy)
  1343. {
  1344. bool fSetTimestamp = true;
  1345. bool fClearedReadOnly = false;
  1346. // Destructively clear the read-only bit if th destination file exists and is read-only.
  1347. dwFileAttributes = wfad.dwFileAttributes;
  1348. // We already filtered out all other reasons for failure other than FILE_NOT_FOUND.
  1349. if (dwFileAttributes != ((DWORD) -1))
  1350. {
  1351. if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  1352. {
  1353. // If it's readonly, clear that bit as well as all the others that
  1354. // are illegal to set via SetFileAttributes()
  1355. dwFileAttributes &=
  1356. ~(FILE_ATTRIBUTE_READONLY |
  1357. FILE_ATTRIBUTE_COMPRESSED |
  1358. FILE_ATTRIBUTE_DEVICE |
  1359. FILE_ATTRIBUTE_DIRECTORY |
  1360. FILE_ATTRIBUTE_ENCRYPTED |
  1361. FILE_ATTRIBUTE_REPARSE_POINT |
  1362. FILE_ATTRIBUTE_SPARSE_FILE);
  1363. if (!::SetFileAttributesW(buffDestination, dwFileAttributes))
  1364. {
  1365. ::ReportFailure("Error setting file attributes for target file \"%ls\" to 0x%08lx to allow overwrite.", static_cast<PCWSTR>(buffDestination), dwFileAttributes);
  1366. goto Exit;
  1367. }
  1368. }
  1369. }
  1370. if (g_fAnnounceCopies)
  1371. printf("Copying \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffSource), static_cast<PCWSTR>(buffDestination));
  1372. // Hey, it's not there. Let's copy it and put the entry in the table.
  1373. if (!::MyCopyFile(pBuffer, cbBuffer, buffSource, buffDestination, FALSE, hFile, rfReQueue))
  1374. {
  1375. ::ReportFailure("Failed to copy \"%ls\" to \"%ls\". %srequeuing.", static_cast<PCWSTR>(buffSource), static_cast<PCWSTR>(buffDestination),
  1376. rfReQueue ? "" : "not ");
  1377. goto Exit;
  1378. }
  1379. if (fSetTimestamp)
  1380. {
  1381. if (!::SetFileTime(hFile, &pFile->m_ftSourceCreationTime, &pFile->m_ftSourceLastAccessTime, &pFile->m_ftSourceLastWriteTime))
  1382. {
  1383. ::ReportFailure("Failed call to SetFileTime on file \"%ls\".", static_cast<PCWSTR>(buffDestination));
  1384. goto Exit;
  1385. }
  1386. ::CloseHandle(hFile);
  1387. hFile = INVALID_HANDLE_VALUE;
  1388. }
  1389. if (!l.Lock())
  1390. {
  1391. ::ReportFailure("Failed to lock global critical section.");
  1392. goto Exit;
  1393. }
  1394. g_FilesCopied++;
  1395. g_FileCopiesProcessed++;
  1396. g_BytesCopied += pFile->m_cbSize;
  1397. l.Unlock();
  1398. }
  1399. else
  1400. {
  1401. if (g_fAnnounceSkips)
  1402. ::printf("Skipping copy from \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffSource), static_cast<PCWSTR>(buffDestination));
  1403. if (!l.Lock())
  1404. {
  1405. ::ReportFailure("Failed to lock global critical section.");
  1406. goto Exit;
  1407. }
  1408. g_CopiesSkipped++;
  1409. g_FileCopiesProcessed++;
  1410. l.Unlock();
  1411. }
  1412. fSuccess = TRUE;
  1413. Exit:
  1414. if (hFile != INVALID_HANDLE_VALUE)
  1415. {
  1416. CSxsPreserveLastError ple;
  1417. ::CloseHandle(hFile);
  1418. ple.Restore();
  1419. }
  1420. if (::GetLastError() == ERROR_TOO_MANY_OPEN_FILES)
  1421. {
  1422. rfReQueue = true;
  1423. fSuccess = TRUE;
  1424. }
  1425. return fSuccess;
  1426. }
  1427. BOOL
  1428. ProcessFileLink(
  1429. CFileLink *pFile,
  1430. bool &rfReQueue
  1431. )
  1432. {
  1433. BOOL fSuccess = FALSE;
  1434. FN_TRACE_WIN32(fSuccess);
  1435. CSxsLockCriticalSection l(g_cs);
  1436. bool fDoLink = true;
  1437. CStringBuffer buffSource, buffDestination;
  1438. WIN32_FILE_ATTRIBUTE_DATA wfad;
  1439. rfReQueue = false;
  1440. IFW32FALSE_EXIT(pFile->GetSource(buffSource));
  1441. IFW32FALSE_EXIT(pFile->GetDestination(buffDestination));
  1442. // CreateHardLinkW() doesn't deal with replace-existing like copyfile, so we'll see if a
  1443. // file by that name is already present and if so, delete it.
  1444. if (!::GetFileAttributesExW(buffDestination, GetFileExInfoStandard, &wfad))
  1445. {
  1446. // It failed. But did it fail for the right reason?
  1447. DWORD dwLastError = ::GetLastError();
  1448. if (dwLastError != ERROR_FILE_NOT_FOUND)
  1449. {
  1450. ::ReportFailure("Error probing destination for \"%ls\".", static_cast<PCWSTR>(buffDestination));
  1451. goto Exit;
  1452. }
  1453. }
  1454. else
  1455. {
  1456. ULONGLONG cbFileSize = (wfad.nFileSizeHigh << 32) | wfad.nFileSizeLow;
  1457. ASSERT_NTC(cbFileSize >= wfad.nFileSizeLow);
  1458. if ((pFile->m_cbSize == cbFileSize) &&
  1459. (pFile->m_ftSourceCreationTime == wfad.ftCreationTime))
  1460. {
  1461. fDoLink = false;
  1462. pFile->m_fSkipped = true;
  1463. }
  1464. else
  1465. {
  1466. if (g_fAnnounceDeletes)
  1467. ::printf("Deleting file \"%ls\" in preparation for hard link creation\n", static_cast<PCWSTR>(buffDestination));
  1468. if (!::DeleteFileW(buffDestination))
  1469. {
  1470. ::ReportFailure("Error deleting destination \"%ls\" in preparation for hard link creation.", static_cast<PCWSTR>(buffDestination));
  1471. goto Exit;
  1472. }
  1473. }
  1474. }
  1475. if (fDoLink)
  1476. {
  1477. if (g_fAnnounceLinks)
  1478. ::printf("Creating hard link from \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource));
  1479. // Hey, it's already there. Let's link it.
  1480. if (!::CreateHardLinkW(buffDestination, buffSource, NULL))
  1481. {
  1482. ::ReportFailure("Error creating hard link from \"%ls\" to \"%ls\".", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource));
  1483. goto Exit;
  1484. }
  1485. if (!l.Lock())
  1486. {
  1487. ::ReportFailure("Failed to lock global critical section.");
  1488. goto Exit;
  1489. }
  1490. g_FilesLinked++;
  1491. g_FileLinksProcessed++;
  1492. l.Unlock();
  1493. }
  1494. else
  1495. {
  1496. if (g_fAnnounceSkips)
  1497. ::printf("Skipping hard link creation from \"%ls\" to \"%ls\"\n", static_cast<PCWSTR>(buffDestination), static_cast<PCWSTR>(buffSource));
  1498. if (!l.Lock())
  1499. {
  1500. ::ReportFailure("Failed to lock global critical section.");
  1501. goto Exit;
  1502. }
  1503. g_LinksSkipped++;
  1504. g_FileLinksProcessed++;
  1505. l.Unlock();
  1506. }
  1507. fSuccess = TRUE;
  1508. Exit:
  1509. if (::GetLastError() == ERROR_TOO_MANY_OPEN_FILES)
  1510. {
  1511. rfReQueue = true;
  1512. fSuccess = TRUE;
  1513. }
  1514. return fSuccess;
  1515. }
  1516. BOOL
  1517. MyCopyFile(
  1518. BYTE *pBuffer,
  1519. DWORD cbBuffer,
  1520. PCWSTR lpExistingFileName,
  1521. PCWSTR lpNewFileName,
  1522. BOOL fFailIfExists,
  1523. HANDLE &rhFile,
  1524. bool &rfRequeue
  1525. )
  1526. {
  1527. BOOL fSuccess = FALSE;
  1528. FN_TRACE_WIN32(fSuccess);
  1529. LARGE_INTEGER liFileSize;
  1530. rhFile = INVALID_HANDLE_VALUE;
  1531. HANDLE hIn = INVALID_HANDLE_VALUE, hOut = INVALID_HANDLE_VALUE;
  1532. hIn = ::CreateFileW(lpExistingFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  1533. if (hIn == INVALID_HANDLE_VALUE)
  1534. {
  1535. ::ReportFailure("Failed to open input file \"%ls\".", lpExistingFileName);
  1536. // we'll assume that we should requeue failures that have to do with the input file.
  1537. rfRequeue = true;
  1538. goto Exit;
  1539. }
  1540. hOut = ::CreateFileW(lpNewFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);
  1541. if (hOut == INVALID_HANDLE_VALUE)
  1542. {
  1543. ::ReportFailure("Failed to open output file \"%ls\".", lpNewFileName);
  1544. goto Exit;
  1545. }
  1546. liFileSize.QuadPart = 0;
  1547. // let the games begin...
  1548. for (;;)
  1549. {
  1550. DWORD dwBytesRead, dwBytesWritten;
  1551. DWORD dwBytesToWrite = 0;
  1552. if (!::ReadFile(hIn, pBuffer, cbBuffer, &dwBytesRead, NULL))
  1553. {
  1554. ::ReportFailure("Error reading from file \"%ls\".", lpExistingFileName);
  1555. // we'll assume that we should requeue failures that have to do with the input file.
  1556. rfRequeue = true;
  1557. goto Exit;
  1558. }
  1559. if (dwBytesRead == 0)
  1560. break;
  1561. liFileSize.QuadPart += dwBytesRead;
  1562. if (dwBytesRead != cbBuffer)
  1563. {
  1564. // We have to round to the cluster size for the write...
  1565. dwBytesToWrite = dwBytesRead + (g_dwDestinationBytesPerSector - 1);
  1566. dwBytesToWrite -= (dwBytesToWrite % g_dwDestinationBytesPerSector);
  1567. }
  1568. else
  1569. dwBytesToWrite = dwBytesRead;
  1570. if (!::WriteFile(hOut, pBuffer, dwBytesToWrite, &dwBytesWritten, NULL))
  1571. {
  1572. ::ReportFailure("Error writing to file \"%ls\".", lpNewFileName);
  1573. // we'll assume that we should requeue failures that have to do with the input file.
  1574. rfRequeue = true;
  1575. goto Exit;
  1576. }
  1577. }
  1578. // If the file size wasn't a multiple of the sector size, we need to open the
  1579. // file without the unbuffered flag so that we can set its size to the exact
  1580. // correct byte count.
  1581. if ((liFileSize.QuadPart % g_dwDestinationBytesPerSector) != 0)
  1582. {
  1583. ::CloseHandle(hOut);
  1584. hOut = INVALID_HANDLE_VALUE;
  1585. hOut = ::CreateFileW(lpNewFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  1586. if (hOut == INVALID_HANDLE_VALUE)
  1587. {
  1588. ::ReportFailure("Unable to reopen output file \"%ls\".", lpNewFileName);
  1589. goto Exit;
  1590. }
  1591. // Truncate the file appropriately
  1592. if (!::FusionpSetFilePointerEx(hOut, liFileSize, NULL, FILE_BEGIN))
  1593. {
  1594. ::ReportFailure("Error setting file pointer on file \"%ls\".", lpNewFileName);
  1595. goto Exit;
  1596. }
  1597. if (!::SetEndOfFile(hOut))
  1598. {
  1599. ::ReportFailure("Error setting end of file on file \"%ls\".", lpNewFileName);
  1600. goto Exit;
  1601. }
  1602. }
  1603. ::CloseHandle(hIn);
  1604. hIn = INVALID_HANDLE_VALUE;
  1605. // Pass the handle back out and set it to INVALID_HANDLE_VALUE so that we don't
  1606. // try to close it in the exit path.
  1607. rhFile = hOut;
  1608. hOut = INVALID_HANDLE_VALUE;
  1609. fSuccess = TRUE;
  1610. Exit:
  1611. if (hIn != INVALID_HANDLE_VALUE)
  1612. {
  1613. CSxsPreserveLastError ple;
  1614. ::CloseHandle(hIn);
  1615. ple.Restore();
  1616. }
  1617. if (hOut != INVALID_HANDLE_VALUE)
  1618. {
  1619. CSxsPreserveLastError ple;
  1620. ::CloseHandle(hOut);
  1621. ple.Restore();
  1622. }
  1623. return fSuccess;
  1624. }