Source code of Windows XP (NT5)
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.

2351 lines
71 KiB

  1. //*************************************************************
  2. //
  3. // Functions to copy the profile directory
  4. //
  5. // Microsoft Confidential
  6. // Copyright (c) Microsoft Corporation 1995
  7. // All rights reserved
  8. //
  9. //*************************************************************
  10. #include "uenv.h"
  11. //
  12. // Local function proto-types
  13. //
  14. BOOL RecurseDirectory (HANDLE hTokenUser, LPTSTR lpSrcDir, LPTSTR lpDestDir, DWORD dwFlags,
  15. LPFILEINFO *llSrcDirs, LPFILEINFO *llSrcFiles,
  16. BOOL bSkipNtUser, LPTSTR lpExcludeList, BOOL bRecurseDest);
  17. BOOL AddFileInfoNode (LPFILEINFO *lpFileInfo, LPTSTR lpSrcFile,
  18. LPTSTR lpDestFile, LPFILETIME ftLastWrite, LPFILETIME ftCreate,
  19. DWORD dwFileSize, DWORD dwFileAttribs, BOOL bHive);
  20. BOOL FreeFileInfoList (LPFILEINFO lpFileInfo);
  21. BOOL SyncItems (LPFILEINFO lpSrcItems, LPFILEINFO lpDestItems,
  22. BOOL bFile, LPFILETIME ftDelRefTime);
  23. void CopyFileFunc (LPTHREADINFO lpThreadInfo);
  24. LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList);
  25. DWORD FindDirectorySize(LPTSTR lpDir, LPFILEINFO lpFiles, DWORD dwFlags, DWORD* pdwLargestHiveFile, DWORD* pdwTotalFiles);
  26. DWORD FindTotalDiskSpaceNeeded(DWORD dwTotalSrcFiles,
  27. DWORD dwTotalDestFiles,
  28. DWORD dwLargestHiveFile,
  29. LPFILEINFO lpSrcFiles,
  30. DWORD dwFlags);
  31. DWORD FindTotalNMaxFileSize(LPFILEINFO lpSrcFiles, DWORD dwNumOfFiles);
  32. BOOL ReconcileDirectory(LPTSTR lpSrcDir, LPTSTR lpDestDir,
  33. DWORD dwFlags, DWORD dwSrcAttribs);
  34. //*************************************************************
  35. //
  36. // CopyProfileDirectoryEx()
  37. //
  38. // Purpose: Copies the profile directory from the source
  39. // to the destination
  40. //
  41. //
  42. // Parameters: LPCTSTR lpSourceDir - Source directory
  43. // LPCTSTR lpDestDir - Destination directory
  44. // DWORD dwFlags - Flags
  45. // LPFILETIME ftDelRefTime - Delete file reference time
  46. // LPCTSTR lpExclusionList - List of directories to exclude
  47. //
  48. //
  49. // Return: (BOOL) TRUE if successful
  50. // FALSE if an error occurs
  51. //
  52. //
  53. // Comments: Called after impersonating the user.
  54. //
  55. //
  56. // History: Date Author Comment
  57. // 5/24/95 ericflo Created
  58. // 4/09/98 ericflo Converted to CopyProfileDirectoryEx
  59. // 9/28/98 ushaji Modified to check for free space
  60. // 3/14/00 weiruc Modified to copy hive file even if
  61. // hive is still loaded and ignore copy
  62. // hive file errors.
  63. //
  64. //*************************************************************
  65. BOOL CopyProfileDirectoryEx (LPCTSTR lpSourceDir,
  66. LPCTSTR lpDestinationDir,
  67. DWORD dwFlags,
  68. LPFILETIME ftDelRefTime,
  69. LPCTSTR lpExclusionList)
  70. {
  71. LPTSTR lpSrcDir = NULL, lpDestDir = NULL;
  72. LPTSTR lpSrcEnd, lpDestEnd;
  73. LPTSTR lpExcludeListSrc = NULL;
  74. LPTSTR lpExcludeListDest = NULL;
  75. LPFILEINFO lpSrcFiles = NULL, lpDestFiles = NULL;
  76. LPFILEINFO lpSrcDirs = NULL, lpDestDirs = NULL;
  77. LPFILEINFO lpTemp;
  78. THREADINFO ThreadInfo = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL};
  79. DWORD dwThreadId;
  80. HANDLE hThreads[NUM_COPY_THREADS];
  81. HANDLE hStatusThread = 0;
  82. DWORD dwThreadCount = 0;
  83. HANDLE hFile;
  84. WIN32_FIND_DATA fd;
  85. BOOL bResult = FALSE;
  86. BOOL bSynchronize = FALSE;
  87. UINT i;
  88. DWORD dwTotalSrcFiles = 0;
  89. DWORD dwTotalDestFiles = 0;
  90. DWORD dwLargestHiveFile;
  91. DWORD dwTotalDiskSpaceNeeded;
  92. ULARGE_INTEGER ulFreeBytesAvailableToCaller, ulTotalNumberOfBytes, ulTotalNumberOfFreeBytes;
  93. DWORD dwErr, dwErr1=0;
  94. TCHAR szErr[MAX_PATH];
  95. TCHAR szTmpHive[MAX_PATH];
  96. BOOL bReconcileHiveSucceeded;
  97. HKEY hkCurrentUser = NULL;
  98. HANDLE hTokenUser = NULL;
  99. dwErr = GetLastError();
  100. //
  101. // Verbose output
  102. //
  103. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Entering, lpSourceDir = <%s>, lpDestinationDir = <%s>, dwFlags = 0x%x"),
  104. lpSourceDir, lpDestinationDir, dwFlags));
  105. //
  106. // Validate parameters
  107. //
  108. if (!lpSourceDir || !lpDestinationDir) {
  109. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: received NULL pointer")));
  110. SetLastError(ERROR_INVALID_PARAMETER);
  111. return FALSE;
  112. }
  113. //
  114. // Get the caller's token
  115. //
  116. if (!OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &hTokenUser)) {
  117. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE, &hTokenUser)) {
  118. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Fail to get process token with error %d."), GetLastError()));
  119. return FALSE;
  120. }
  121. }
  122. //
  123. // If there is an exclusion list, convert it into an array of null
  124. // terminate strings (double null at the end) based upon the source
  125. // directory
  126. //
  127. if ((dwFlags & CPD_USEEXCLUSIONLIST) && lpExclusionList) {
  128. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: lpExclusionList = <%s>"),
  129. lpExclusionList));
  130. lpExcludeListSrc = ConvertExclusionList (lpSourceDir, lpExclusionList);
  131. if (!lpExcludeListSrc) {
  132. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to convert exclusion list for source")));
  133. goto Exit;
  134. }
  135. if (!(dwFlags & CPD_DELDESTEXCLUSIONS)) {
  136. lpExcludeListDest = ConvertExclusionList (lpDestinationDir, lpExclusionList);
  137. if (!lpExcludeListDest) {
  138. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to convert exclusion list for destination")));
  139. dwErr = ERROR_INVALID_DATA;
  140. goto Exit;
  141. }
  142. }
  143. }
  144. //
  145. // Get the desktop handle
  146. //
  147. ThreadInfo.hDesktop = GetThreadDesktop(GetCurrentThreadId());
  148. //
  149. // Is this a full sync copy (delete extra files / directories in dest).
  150. //
  151. if (dwFlags & CPD_SYNCHRONIZE) {
  152. bSynchronize = TRUE;
  153. }
  154. //
  155. // Test / Create the destination directory
  156. //
  157. if (!CreateNestedDirectory(lpDestinationDir, NULL)) {
  158. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to create the destination directory. Error = %d"),
  159. GetLastError()));
  160. dwErr = GetLastError();
  161. goto Exit;
  162. }
  163. //
  164. // Create and set up the directory buffers
  165. //
  166. lpSrcDir = LocalAlloc(LPTR, (2 * MAX_PATH) * sizeof(TCHAR));
  167. lpDestDir = LocalAlloc(LPTR, (2 * MAX_PATH) * sizeof(TCHAR));
  168. if (!lpSrcDir || !lpDestDir) {
  169. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to allocate memory for working directories")));
  170. dwErr = GetLastError();
  171. goto Exit;
  172. }
  173. lstrcpy (lpSrcDir, lpSourceDir);
  174. lstrcpy (lpDestDir, lpDestinationDir);
  175. //
  176. // Setup ending pointers
  177. //
  178. lpSrcEnd = CheckSlash (lpSrcDir);
  179. lpDestEnd = CheckSlash (lpDestDir);
  180. //
  181. // Recurse through the folders gathering info
  182. //
  183. if (!(dwFlags & CPD_COPYHIVEONLY)) {
  184. //
  185. // Recurse the source directory
  186. //
  187. if (!RecurseDirectory(hTokenUser, lpSrcDir, lpDestDir, dwFlags,
  188. &lpSrcDirs, &lpSrcFiles, TRUE, lpExcludeListSrc, FALSE)) {
  189. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: RecurseDirectory returned FALSE")));
  190. dwErr = GetLastError();
  191. goto Exit;
  192. }
  193. if (bSynchronize) {
  194. //
  195. // Recurse the destination directory
  196. //
  197. if (!RecurseDirectory(hTokenUser, lpDestDir, lpSrcDir, dwFlags,
  198. &lpDestDirs, &lpDestFiles, TRUE, lpExcludeListDest, TRUE)) {
  199. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: RecurseDirectory returned FALSE")));
  200. dwErr = GetLastError();
  201. goto Exit;
  202. }
  203. }
  204. }
  205. //
  206. // determine the source and destination sizes
  207. //
  208. if(FindDirectorySize(lpSrcDir, lpSrcFiles, dwFlags, &dwLargestHiveFile, &dwTotalSrcFiles) != ERROR_SUCCESS) {
  209. DebugMsg((DM_WARNING, TEXT("FindDirectorySize: Error = %08x"), GetLastError()));
  210. dwErr = GetLastError();
  211. goto Exit;
  212. }
  213. if(FindDirectorySize(lpDestDir, lpDestFiles, dwFlags, NULL, &dwTotalDestFiles)) {
  214. DebugMsg((DM_WARNING, TEXT("FindDirectorySize: Error = %08x"), GetLastError()));
  215. dwErr = GetLastError();
  216. goto Exit;
  217. }
  218. //
  219. // determine the disk space needed
  220. //
  221. dwTotalDiskSpaceNeeded = FindTotalDiskSpaceNeeded(dwTotalSrcFiles,
  222. dwTotalDestFiles,
  223. dwLargestHiveFile,
  224. lpSrcFiles,
  225. dwFlags);
  226. //
  227. // CopyProfileDirectoryEx is called with impersonation on
  228. //
  229. if (!GetDiskFreeSpaceEx(lpDestDir, &ulFreeBytesAvailableToCaller, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes)) {
  230. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to get the Free Disk Space <%s>. Error = %d"),
  231. lpDestDir, GetLastError()));
  232. dwErr = GetLastError();
  233. goto Exit;
  234. }
  235. DebugMsg((DM_VERBOSE, TEXT("Available\t\t%d"), ulFreeBytesAvailableToCaller.QuadPart));
  236. DebugMsg((DM_VERBOSE, TEXT("Needed\t\t%d"), dwTotalDiskSpaceNeeded));
  237. DebugMsg((DM_VERBOSE, TEXT("Src size\t\t%d"), dwTotalSrcFiles));
  238. DebugMsg((DM_VERBOSE, TEXT("Dest size\t\t%d"), dwTotalDestFiles));
  239. DebugMsg((DM_VERBOSE, TEXT("Largest hive file\t\t%d"), dwLargestHiveFile));
  240. if(dwTotalDiskSpaceNeeded > ulFreeBytesAvailableToCaller.QuadPart) {
  241. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Not enough disk space on <%s>"), lpDestDir));
  242. dwErr = ERROR_DISK_FULL;
  243. goto Exit;
  244. }
  245. //
  246. // Synchronize the directories and files if appropriate
  247. //
  248. if (bSynchronize) {
  249. //
  250. // Files first...
  251. //
  252. SyncItems (lpSrcFiles, lpDestFiles, TRUE,
  253. (dwFlags & CPD_USEDELREFTIME) ? ftDelRefTime : NULL);
  254. //
  255. // Now the directories...
  256. //
  257. SyncItems (lpSrcDirs, lpDestDirs, FALSE,
  258. (dwFlags & CPD_USEDELREFTIME) ? ftDelRefTime : NULL);
  259. }
  260. //
  261. // Copy the actual hive, log, ini files first
  262. //
  263. if (!(dwFlags & CPD_IGNOREHIVE)) {
  264. //
  265. // Search for all user hives
  266. //
  267. if (dwFlags & CPD_WIN95HIVE) {
  268. lstrcpy (lpSrcEnd, c_szUserStar);
  269. } else {
  270. lstrcpy (lpSrcEnd, c_szNTUserStar);
  271. }
  272. //
  273. // Enumerate
  274. //
  275. hFile = FindFirstFile(lpSrcDir, &fd);
  276. if (hFile != INVALID_HANDLE_VALUE) {
  277. do {
  278. //
  279. // Setup the filename
  280. //
  281. if((dwFlags & CPD_USETMPHIVEFILE) &&
  282. (lstrcmpi(fd.cFileName, c_szNTUserMan) == 0 ||
  283. lstrcmpi(fd.cFileName, c_szNTUserDat) == 0)) {
  284. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Hive is still loaded, use temporary hive file")));
  285. //
  286. // Use temporary hive file because unloading hive failed.
  287. //
  288. lstrcpy(lpSrcEnd, c_szNTUserTmp);
  289. }
  290. else {
  291. lstrcpy (lpSrcEnd, fd.cFileName);
  292. }
  293. lstrcpy (lpDestEnd, fd.cFileName);
  294. //
  295. // Do not reconcile the log file if we are using the tmp hive
  296. // file. Skip it.
  297. //
  298. if((dwFlags & CPD_USETMPHIVEFILE) &&
  299. lstrcmpi(fd.cFileName + lstrlen(fd.cFileName) - lstrlen(c_szLog), c_szLog) == 0) {
  300. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Log file %s skipped"), lpDestDir));
  301. continue;
  302. }
  303. //
  304. // Skip the temporary hive file here. Will deal with it when
  305. // we find an actual hive file.
  306. //
  307. if(lstrcmpi(fd.cFileName, c_szNTUserTmp) == 0) {
  308. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: %s skipped"), fd.cFileName));
  309. continue;
  310. }
  311. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Found hive file %s"), fd.cFileName));
  312. if(!ReconcileFile(lpSrcDir, lpDestDir, dwFlags, NULL,
  313. fd.nFileSizeLow, TRUE)) {
  314. dwErr1 = GetLastError();
  315. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: ReconcileFile failed with error = %d"), dwErr1));
  316. if (!(dwFlags & CPD_IGNORECOPYERRORS)) {
  317. dwErr = dwErr1;
  318. ReportError(hTokenUser,
  319. PI_NOUI,
  320. 3,
  321. EVENT_COPYERROR,
  322. lpSrcDir,
  323. lpDestDir,
  324. GetErrString(dwErr, szErr));
  325. FindClose(hFile);
  326. goto Exit;
  327. }
  328. else {
  329. ReportError(hTokenUser,
  330. PI_NOUI | EVENT_WARNING_TYPE,
  331. 3,
  332. EVENT_COPYERROR,
  333. lpSrcDir,
  334. lpDestDir,
  335. GetErrString(dwErr1, szErr));
  336. }
  337. }
  338. //
  339. // Find the next entry
  340. //
  341. } while (FindNextFile(hFile, &fd));
  342. FindClose(hFile);
  343. dwErr = ERROR_SUCCESS;
  344. } else {
  345. dwErr = GetLastError();
  346. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: FindFirstFile failed to find a hive!. Error = %d"),
  347. dwErr));
  348. }
  349. }
  350. //
  351. // Create all the directories
  352. //
  353. if (!(dwFlags & CPD_COPYHIVEONLY)) {
  354. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Calling ReconcileDirectory for all Directories")));
  355. lpTemp = lpSrcDirs;
  356. while (lpTemp) {
  357. if (!ReconcileDirectory(lpTemp->szSrc, lpTemp->szDest, dwFlags, lpTemp->dwFileAttribs)) {
  358. dwErr1 = GetLastError();
  359. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to create the destination directory <%s>. Error = %d"),
  360. lpTemp->szDest, GetLastError()));
  361. if (!(dwFlags & CPD_IGNORECOPYERRORS)) {
  362. //
  363. // Show the error UI and bail out.
  364. //
  365. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  366. lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr));
  367. dwErr = dwErr1;
  368. goto Exit;
  369. }
  370. else {
  371. ReportError(hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR,
  372. lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr));
  373. }
  374. }
  375. lpTemp = lpTemp->pNext;
  376. }
  377. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Reconcile Directory Done for all Directories")));
  378. //
  379. // Copy the files
  380. //
  381. if (dwFlags & CPD_SLOWCOPY) {
  382. //
  383. // Copy the files one at a time...
  384. //
  385. lpTemp = lpSrcFiles;
  386. while (lpTemp) {
  387. if (lpTemp->bHive) {
  388. //
  389. // Hive files have already been copied..
  390. //
  391. lpTemp = lpTemp->pNext;
  392. continue;
  393. }
  394. if (!ReconcileFile (lpTemp->szSrc, lpTemp->szDest, dwFlags,
  395. &lpTemp->ftLastWrite,
  396. lpTemp->dwFileSize, FALSE)) {
  397. dwErr1 = GetLastError();
  398. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to copy the file <%s> to <%s> due to error = %d"),
  399. lpTemp->szSrc, lpTemp->szDest, GetLastError()));
  400. if (!(dwFlags & CPD_IGNORECOPYERRORS)) {
  401. //
  402. // Show the error UI and since the user picks to abort
  403. // then we leave now.
  404. //
  405. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  406. lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr));
  407. dwErr = dwErr1;
  408. goto Exit;
  409. }
  410. else {
  411. ReportError(hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR,
  412. lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr));
  413. }
  414. }
  415. lpTemp = lpTemp->pNext;
  416. }
  417. } else {
  418. if (lpSrcFiles) {
  419. HANDLE hThreadToken=NULL;
  420. if (!OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE,
  421. TRUE, &hThreadToken)) {
  422. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Failed to get token with %d. This is ok if thread is not impersonating"),
  423. GetLastError()));
  424. }
  425. //
  426. // Multi-threaded copy
  427. //
  428. // Null sd, auto set, initially signalled, unnamed..
  429. if (!(ThreadInfo.hCopyEvent = CreateEvent(NULL, FALSE, TRUE, NULL))) {
  430. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: CreateEvent for CopyEvent failed with error %d"), GetLastError()));
  431. dwErr = GetLastError();
  432. goto Exit;
  433. }
  434. ThreadInfo.dwFlags = dwFlags;
  435. ThreadInfo.lpSrcFiles = lpSrcFiles;
  436. ThreadInfo.hTokenUser = hTokenUser;
  437. //
  438. // Required for PrivCopyFileEx to work, threads should be created using the
  439. // process token
  440. //
  441. RevertToSelf();
  442. //
  443. // Create the file copy threads
  444. //
  445. for (i = 0; i < NUM_COPY_THREADS; i++) {
  446. if (hThreads[dwThreadCount] = CreateThread (NULL,
  447. 0,
  448. (LPTHREAD_START_ROUTINE) CopyFileFunc,
  449. (LPVOID) &ThreadInfo,
  450. CREATE_SUSPENDED,
  451. &dwThreadId)) {
  452. dwThreadCount++;
  453. }
  454. }
  455. //
  456. // Put the token back.
  457. //
  458. if (!SetThreadToken(NULL, hThreadToken)) {
  459. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Impersonation failed with error %d"), GetLastError()));
  460. dwErr = GetLastError();
  461. // terminate and close handles for all the threads
  462. for (i = 0; i < dwThreadCount; i++) {
  463. TerminateThread (hThreads[i], 1);
  464. CloseHandle (hThreads[i]);
  465. }
  466. if (hThreadToken)
  467. CloseHandle (hThreadToken);
  468. goto Exit;
  469. }
  470. for (i = 0; i < dwThreadCount; i++) {
  471. if (!SetThreadToken(&hThreads[i], hThreadToken) || !ResumeThread (hThreads[i])) {
  472. TerminateThread(hThreads[i], 1);
  473. }
  474. }
  475. //
  476. // Wait for the threads to finish
  477. //
  478. if (WaitForMultipleObjects (dwThreadCount, hThreads, TRUE, INFINITE) == WAIT_FAILED) {
  479. ThreadInfo.dwError = GetLastError();
  480. }
  481. //
  482. // Clean up
  483. //
  484. if (hThreadToken)
  485. CloseHandle (hThreadToken);
  486. for (i = 0; i < dwThreadCount; i++) {
  487. CloseHandle (hThreads[i]);
  488. }
  489. if (ThreadInfo.dwError) {
  490. dwErr = ThreadInfo.dwError;
  491. goto Exit;
  492. }
  493. }
  494. }
  495. }
  496. //
  497. // Restore the time on the directories to be the same as from Src.
  498. // This is required because the times on directory have been modified by
  499. // creation and deletion of files above.
  500. //
  501. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Setting Directory TimeStamps all Directories")));
  502. lpTemp = lpSrcDirs;
  503. while (lpTemp) {
  504. HANDLE hFile;
  505. SetFileAttributes (lpTemp->szDest, FILE_ATTRIBUTE_NORMAL);
  506. hFile = CreateFile(lpTemp->szDest, GENERIC_WRITE,
  507. FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  508. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
  509. if (hFile != INVALID_HANDLE_VALUE) {
  510. if (!SetFileTime(hFile, NULL, NULL, &(lpTemp->ftLastWrite))) {
  511. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to set the write time on the directory <%s>. Error = %d"),
  512. lpTemp->szDest, GetLastError()));
  513. }
  514. CloseHandle(hFile);
  515. }
  516. else {
  517. DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: CreateFile failed for directory %s with error %d"), lpTemp->szDest,
  518. GetLastError()));
  519. }
  520. SetFileAttributes (lpTemp->szDest, lpTemp->dwFileAttribs);
  521. lpTemp = lpTemp->pNext;
  522. }
  523. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Set times on all directories")));
  524. //
  525. // Success
  526. //
  527. bResult = TRUE;
  528. Exit:
  529. if (ThreadInfo.hCopyEvent) {
  530. CloseHandle (ThreadInfo.hCopyEvent);
  531. }
  532. if ( ThreadInfo.hDesktop ) {
  533. CloseDesktop( ThreadInfo.hDesktop );
  534. }
  535. //
  536. // Free the memory allocated above
  537. //
  538. if (hTokenUser) {
  539. CloseHandle(hTokenUser);
  540. }
  541. if (lpSrcDir) {
  542. LocalFree(lpSrcDir);
  543. }
  544. if (lpDestDir) {
  545. LocalFree(lpDestDir);
  546. }
  547. if (lpExcludeListSrc) {
  548. LocalFree (lpExcludeListSrc);
  549. }
  550. if (lpExcludeListDest) {
  551. LocalFree (lpExcludeListDest);
  552. }
  553. if (lpSrcFiles) {
  554. FreeFileInfoList(lpSrcFiles);
  555. }
  556. if (lpDestFiles) {
  557. FreeFileInfoList(lpDestFiles);
  558. }
  559. if (lpSrcDirs) {
  560. FreeFileInfoList(lpSrcDirs);
  561. }
  562. if (lpDestDirs) {
  563. FreeFileInfoList(lpDestDirs);
  564. }
  565. SetLastError(dwErr);
  566. //
  567. // Verbose output
  568. //
  569. DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Leaving with a return value of %d"), bResult));
  570. return bResult;
  571. }
  572. //*************************************************************
  573. //
  574. // RecurseDirectory()
  575. //
  576. // Purpose: Recurses through the subdirectory coping files.
  577. //
  578. // Parameters: hTokenUser - User's token
  579. // lpSrcDir - Source directory working buffer
  580. // lpDestDir - Destination directory working buffer
  581. // dwFlags - dwFlags
  582. // llSrcDirs - Link list of directories
  583. // llSrcFiles - Link list of files
  584. // bSkipNtUser - Skip ntuser.* files
  585. // lpExcludeList - List of directories to exclude
  586. // bRecurseDest - The destination Dir is being recursed
  587. //
  588. // Return: TRUE if successful
  589. // FALSE if an error occurs
  590. //
  591. // Comments: 1) The source and dest directories will already have
  592. // the trailing backslash when entering this function.
  593. // 2) The current working directory is the source directory.
  594. //
  595. //
  596. // Notes:
  597. // CPD_SYSTEMDIRSONLY Do not keep track of anything unless the dir is
  598. // marked with system bit.
  599. // CPD_SYSTEMFILES Only Systemfiles
  600. // CPD_NONENCRYPTEDONLY Only Non EncryptedFile/Directory.
  601. //
  602. // History: Date Author Comment
  603. // 5/25/95 ericflo Created
  604. //
  605. //*************************************************************
  606. BOOL RecurseDirectory (HANDLE hTokenUser, LPTSTR lpSrcDir, LPTSTR lpDestDir, DWORD dwFlags,
  607. LPFILEINFO *llSrcDirs, LPFILEINFO *llSrcFiles,
  608. BOOL bMarkNtUser, LPTSTR lpExcludeList, BOOL bRecurseDest)
  609. {
  610. HANDLE hFile = INVALID_HANDLE_VALUE;
  611. WIN32_FIND_DATA fd;
  612. LPTSTR lpSrcEnd, lpDestEnd, lpTemp;
  613. BOOL bResult = TRUE;
  614. BOOL bSkip;
  615. DWORD dwErr, dwErr1 = 0;
  616. TCHAR szErr[MAX_PATH];
  617. BOOL bHive;
  618. dwErr = GetLastError();
  619. //
  620. // Setup the ending pointers
  621. //
  622. lpSrcEnd = CheckSlash (lpSrcDir);
  623. lpDestEnd = CheckSlash (lpDestDir);
  624. //
  625. // Append *.* to the source directory
  626. //
  627. lstrcpy(lpSrcEnd, c_szStarDotStar);
  628. //
  629. // Search through the source directory
  630. //
  631. hFile = FindFirstFile(lpSrcDir, &fd);
  632. if (hFile == INVALID_HANDLE_VALUE) {
  633. if ( (GetLastError() == ERROR_FILE_NOT_FOUND) ||
  634. (GetLastError() == ERROR_PATH_NOT_FOUND) ) {
  635. //
  636. // bResult is already initialized to TRUE, so
  637. // just fall through.
  638. //
  639. } else {
  640. dwErr1 = GetLastError();
  641. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: FindFirstFile failed. Error = %d"),
  642. dwErr1));
  643. *lpSrcEnd = TEXT('\0');
  644. *lpDestEnd = TEXT('\0');
  645. if (!(dwFlags & CPD_IGNORECOPYERRORS)) {
  646. if (!bRecurseDest) {
  647. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  648. lpSrcDir, lpDestDir, GetErrString(dwErr1, szErr));
  649. }
  650. else {
  651. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  652. lpDestDir, lpSrcDir, GetErrString(dwErr1, szErr));
  653. }
  654. dwErr = dwErr1;
  655. bResult = FALSE;
  656. }
  657. }
  658. goto RecurseDir_Exit;
  659. }
  660. do {
  661. bHive = FALSE;
  662. //
  663. // Check whether we have enough space in our buffers
  664. //
  665. *lpSrcEnd = TEXT('\0');
  666. *lpDestEnd = TEXT('\0');
  667. if ((lstrlen(lpSrcDir)+lstrlen(fd.cFileName) >= MAX_PATH) ||
  668. (lstrlen(lpDestDir)+lstrlen(fd.cFileName) >= MAX_PATH)) {
  669. LPTSTR lpErrSrc=NULL, lpErrDest=NULL;
  670. BOOL bRet;
  671. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: %s is too long. src = %s, dest = %s"), fd.cFileName, lpSrcDir, lpDestDir));
  672. if (dwFlags & CPD_IGNORELONGFILENAMES)
  673. continue;
  674. //
  675. // Allocate a buffer to show the file names
  676. //
  677. lpErrSrc = LocalAlloc(LPTR, (lstrlen(lpSrcDir)+lstrlen(fd.cFileName)+1)*sizeof(TCHAR));
  678. lpErrDest = LocalAlloc(LPTR, (lstrlen(lpDestDir)+lstrlen(fd.cFileName)+1)*sizeof(TCHAR));
  679. //
  680. // Show the UI
  681. //
  682. if ((!lpErrSrc) || (!lpErrDest)) {
  683. if (!bRecurseDest) {
  684. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  685. lpSrcDir, lpDestDir, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr));
  686. } else {
  687. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  688. lpDestDir, lpSrcDir, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr));
  689. }
  690. }
  691. else {
  692. lstrcpy(lpErrSrc, lpSrcDir);
  693. lstrcpy(lpErrSrc+lstrlen(lpErrSrc), fd.cFileName);
  694. lstrcpy(lpErrDest, lpDestDir);
  695. lstrcpy(lpErrDest+lstrlen(lpErrDest), fd.cFileName);
  696. if (!bRecurseDest) {
  697. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  698. lpErrSrc, lpErrDest, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr));
  699. } else {
  700. ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR,
  701. lpErrDest, lpErrSrc, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr));
  702. }
  703. }
  704. if (lpErrSrc)
  705. LocalFree(lpErrSrc);
  706. if (lpErrDest)
  707. LocalFree(lpErrDest);
  708. //
  709. // Set the error and quit.
  710. //
  711. dwErr = ERROR_FILENAME_EXCED_RANGE;
  712. bResult = FALSE;
  713. goto RecurseDir_Exit;
  714. }
  715. //
  716. // Append the file / directory name to the working buffers
  717. //
  718. lstrcpy (lpSrcEnd, fd.cFileName);
  719. lstrcpy (lpDestEnd, fd.cFileName);
  720. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  721. //
  722. // Check for "." and ".."
  723. //
  724. if (!lstrcmpi(fd.cFileName, c_szDot)) {
  725. continue;
  726. }
  727. if (!lstrcmpi(fd.cFileName, c_szDotDot)) {
  728. continue;
  729. }
  730. //
  731. // Check for reparse point
  732. //
  733. if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
  734. {
  735. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: Found a reparse point <%s>, skip it!"), lpSrcDir));
  736. continue;
  737. }
  738. //
  739. // Check if this directory should be excluded
  740. //
  741. if (lpExcludeList) {
  742. bSkip = FALSE;
  743. lpTemp = lpExcludeList;
  744. while (*lpTemp) {
  745. if (lstrcmpi (lpTemp, lpSrcDir) == 0) {
  746. bSkip = TRUE;
  747. break;
  748. }
  749. lpTemp += lstrlen (lpTemp) + 1;
  750. }
  751. if (bSkip) {
  752. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to exclusion list."),
  753. lpSrcDir));
  754. continue;
  755. }
  756. }
  757. //
  758. // Found a directory.
  759. //
  760. // 1) Change into that subdirectory on the source drive.
  761. // 2) Recurse down that tree.
  762. // 3) Back up one level.
  763. //
  764. //
  765. // Add to the list of directories
  766. //
  767. if (dwFlags & CPD_SYSTEMDIRSONLY) {
  768. //
  769. // if it is encrypted, don't recurse into it
  770. //
  771. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
  772. DWORD dwNewFlags = dwFlags;
  773. //
  774. // Add to the list of directories only if marked as system, o/w just recurse through
  775. //
  776. if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
  777. if (!AddFileInfoNode (llSrcDirs, lpSrcDir, lpDestDir, &fd.ftLastWriteTime,
  778. &fd.ftCreationTime, 0, fd.dwFileAttributes, bHive)) {
  779. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed")));
  780. dwErr = GetLastError();
  781. goto RecurseDir_Exit;
  782. }
  783. dwNewFlags ^= CPD_SYSTEMDIRSONLY;
  784. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Adding %s to the list of directories because system bit is on"), lpSrcDir));
  785. }
  786. //
  787. // Recurse the subdirectory
  788. //
  789. if (!RecurseDirectory(hTokenUser, lpSrcDir, lpDestDir, dwNewFlags,
  790. llSrcDirs, llSrcFiles, FALSE, lpExcludeList, bRecurseDest)) {
  791. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: RecurseDirectory returned FALSE")));
  792. bResult = FALSE;
  793. dwErr = GetLastError();
  794. goto RecurseDir_Exit;
  795. }
  796. } else {
  797. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set."),
  798. lpSrcDir));
  799. }
  800. continue;
  801. }
  802. //
  803. // Popup time
  804. //
  805. if (dwFlags & CPD_NONENCRYPTEDONLY) {
  806. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  807. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Detected Encrypted file %s, Aborting.."), lpSrcDir));
  808. dwErr = ERROR_FILE_ENCRYPTED;
  809. bResult = FALSE;
  810. goto RecurseDir_Exit;
  811. }
  812. }
  813. //
  814. // Ignore encrypted file
  815. //
  816. if (dwFlags & CPD_IGNOREENCRYPTEDFILES) {
  817. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  818. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set..."),
  819. lpSrcDir));
  820. continue;
  821. }
  822. }
  823. //
  824. // Add it to the list
  825. //
  826. if (!AddFileInfoNode (llSrcDirs, lpSrcDir, lpDestDir, &fd.ftLastWriteTime,
  827. &fd.ftCreationTime, 0, fd.dwFileAttributes, bHive)) {
  828. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed")));
  829. dwErr = GetLastError();
  830. goto RecurseDir_Exit;
  831. }
  832. //
  833. // Recurse the subdirectory
  834. //
  835. if (!RecurseDirectory(hTokenUser, lpSrcDir, lpDestDir, dwFlags,
  836. llSrcDirs, llSrcFiles, FALSE, lpExcludeList, bRecurseDest)) {
  837. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: RecurseDirectory returned FALSE")));
  838. bResult = FALSE;
  839. dwErr = GetLastError();
  840. goto RecurseDir_Exit;
  841. }
  842. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Adding %s to the list of directories"), lpSrcDir));
  843. } else {
  844. //
  845. // if the directories only bit is set, don't copy anything else
  846. //
  847. if (dwFlags & CPD_SYSTEMDIRSONLY) {
  848. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the system directories only attribute is set."),
  849. lpSrcDir));
  850. continue;
  851. }
  852. //
  853. // If the filename found starts with "ntuser", then ignore
  854. // it because the hive will be copied below (if appropriate).
  855. //
  856. if (bMarkNtUser && lstrlen(fd.cFileName) >= 6) {
  857. if (CompareString (LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
  858. fd.cFileName, 6,
  859. TEXT("ntuser"), 6) == CSTR_EQUAL) {
  860. bHive = TRUE;
  861. }
  862. }
  863. //
  864. // Check if this file should be excluded
  865. //
  866. if (dwFlags & CPD_SYSTEMFILES) {
  867. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) {
  868. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the system attribute is not set."),
  869. lpSrcDir));
  870. continue;
  871. }
  872. }
  873. //
  874. // if it is systemfile, it can not be encrypted.
  875. //
  876. if (dwFlags & CPD_NONENCRYPTEDONLY) {
  877. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  878. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Detected Encrypted file %s, Aborting..."), lpSrcDir));
  879. dwErr = ERROR_FILE_ENCRYPTED;
  880. bResult = FALSE;
  881. goto RecurseDir_Exit;
  882. }
  883. }
  884. if (dwFlags & CPD_IGNOREENCRYPTEDFILES) {
  885. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  886. DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set."),
  887. lpSrcDir));
  888. continue;
  889. }
  890. }
  891. //
  892. // We found a file. Add it to the list.
  893. //
  894. if (!AddFileInfoNode (llSrcFiles, lpSrcDir, lpDestDir,
  895. &fd.ftLastWriteTime, &fd.ftCreationTime,
  896. fd.nFileSizeLow, fd.dwFileAttributes, bHive)) {
  897. DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed")));
  898. dwErr = GetLastError();
  899. goto RecurseDir_Exit;
  900. }
  901. }
  902. //
  903. // Find the next entry
  904. //
  905. } while (FindNextFile(hFile, &fd));
  906. RecurseDir_Exit:
  907. //
  908. // Remove the file / directory name appended above
  909. //
  910. *lpSrcEnd = TEXT('\0');
  911. *lpDestEnd = TEXT('\0');
  912. //
  913. // Close the search handle
  914. //
  915. if (hFile != INVALID_HANDLE_VALUE) {
  916. FindClose(hFile);
  917. }
  918. SetLastError(dwErr);
  919. return bResult;
  920. }
  921. //*************************************************************
  922. //
  923. // CopyProgressRoutine()
  924. //
  925. // Purpose: Callback function for CopyFileEx
  926. //
  927. // Parameters: See doc's.
  928. //
  929. // Return: PROGRESS_CONTINUE
  930. //
  931. //*************************************************************
  932. DWORD WINAPI CopyProgressRoutine(LARGE_INTEGER TotalFileSize,
  933. LARGE_INTEGER TotalBytesTransferred,
  934. LARGE_INTEGER StreamSize,
  935. LARGE_INTEGER StreamBytesTransferred,
  936. DWORD dwStreamNumber,
  937. DWORD dwCallbackReason,
  938. HANDLE hSourceFile,
  939. HANDLE hDestinationFile,
  940. LPVOID lpData)
  941. {
  942. switch (dwCallbackReason)
  943. {
  944. case PRIVCALLBACK_ENCRYPTION_FAILED:
  945. case PRIVCALLBACK_COMPRESSION_FAILED:
  946. case PRIVCALLBACK_SPARSE_FAILED:
  947. case PRIVCALLBACK_OWNER_GROUP_FAILED:
  948. case PRIVCALLBACK_DACL_ACCESS_DENIED:
  949. case PRIVCALLBACK_SACL_ACCESS_DENIED:
  950. case PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED:
  951. return PROGRESS_CANCEL;
  952. default:
  953. return PROGRESS_CONTINUE; //all other conditions can be safely ignored
  954. }
  955. }
  956. //*************************************************************
  957. //
  958. // ReconcileDirectory()
  959. //
  960. // Purpose: Compares the source and destination file.
  961. // If the source is newer, then it is copied
  962. // over the destination.
  963. //
  964. // Parameters: lpSrcDir - source filename
  965. // lpDestDir - destination filename
  966. // dwFlags - flags
  967. // dwSrcAttribs Source Attributes for decompression,
  968. // decryption later on.
  969. //
  970. //
  971. // Return: 1 if successful (no file copied)
  972. // 0 if an error occurs
  973. //
  974. // Comments:
  975. //
  976. // History: Date Author Comment
  977. // 2/26/99 ushaji Created
  978. //
  979. //*************************************************************
  980. BOOL ReconcileDirectory(LPTSTR lpSrcDir, LPTSTR lpDestDir,
  981. DWORD dwFlags, DWORD dwSrcAttribs)
  982. {
  983. DWORD dwCopyFlags=0, dwErr;
  984. BOOL bCancel = FALSE;
  985. //
  986. // Clear any existing attributes
  987. //
  988. SetFileAttributes (lpDestDir, FILE_ATTRIBUTE_NORMAL);
  989. if (!CreateNestedDirectory(lpDestDir, NULL)) {
  990. DebugMsg((DM_WARNING, TEXT("ReconcileDirectory: Failed to create the destination directory <%s>. Error = %d"),
  991. lpDestDir, GetLastError()));
  992. return FALSE;
  993. }
  994. //
  995. // Set up the copy flags to copy the encryption/compression on dirs over.
  996. //
  997. if (!(dwFlags & CPD_IGNORESECURITY))
  998. dwCopyFlags = PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_SKIP_DACL;
  999. dwCopyFlags |= PRIVCOPY_FILE_DIRECTORY | PRIVCOPY_FILE_SUPERSEDE;
  1000. if (!PrivCopyFileExW(lpSrcDir, lpDestDir,
  1001. (LPPROGRESS_ROUTINE) CopyProgressRoutine,
  1002. NULL, &bCancel, dwCopyFlags)) {
  1003. dwErr = GetLastError();
  1004. DebugMsg((DM_WARNING, TEXT("ReconcileDirectory: Failed to copy over the attributes src <%s> to destination directory <%s>. Error = %d"),
  1005. lpSrcDir, lpDestDir, dwErr));
  1006. RemoveDirectory(lpDestDir);
  1007. SetLastError(dwErr);
  1008. return FALSE;
  1009. }
  1010. return TRUE;
  1011. }
  1012. //*************************************************************
  1013. //
  1014. // ReconcileFile()
  1015. //
  1016. // Purpose: Compares the source and destination file.
  1017. // If the source is newer, then it is copied
  1018. // over the destination.
  1019. //
  1020. // Parameters: lpSrcFile - source filename
  1021. // lpDestFile - destination filename
  1022. // dwFlags - flags
  1023. // ftSrcTime - Src file time (can be NULL)
  1024. // dwFileSize - File size
  1025. // bHiveFile - Flag to indicate hive file
  1026. //
  1027. //
  1028. // Return: 1 if successful (no file copied)
  1029. // 2 if successful (and a file was copied)
  1030. // 0 if an error occurs
  1031. //
  1032. // Comments:
  1033. //
  1034. // History: Date Author Comment
  1035. // 5/25/95 ericflo Created
  1036. // 7/20/00 santanuc added flag bHiveFile
  1037. //
  1038. //*************************************************************
  1039. INT ReconcileFile (LPCTSTR lpSrcFile, LPCTSTR lpDestFile,
  1040. DWORD dwFlags, LPFILETIME ftSrcTime,
  1041. DWORD dwFileSize, BOOL bHiveFile)
  1042. {
  1043. WIN32_FILE_ATTRIBUTE_DATA fad;
  1044. FILETIME ftWriteSrc, ftWriteDest;
  1045. INT iCopyFile = 0;
  1046. DWORD dwErr = ERROR_SUCCESS, dwErr1 = 0;
  1047. //
  1048. // If the flags have CPD_FORCECOPY, then skip to the
  1049. // copy file call without checking the timestamps.
  1050. //
  1051. if (!(dwFlags & CPD_FORCECOPY)) {
  1052. //
  1053. // If we were given a source file time, use that
  1054. //
  1055. if (ftSrcTime) {
  1056. ftWriteSrc.dwLowDateTime = ftSrcTime->dwLowDateTime;
  1057. ftWriteSrc.dwHighDateTime = ftSrcTime->dwHighDateTime;
  1058. } else {
  1059. //
  1060. // Query for the source file time
  1061. //
  1062. if (!GetFileAttributesEx (lpSrcFile, GetFileExInfoStandard, &fad)) {
  1063. dwErr = GetLastError();
  1064. DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributes on the source failed with error = %d"),
  1065. dwErr));
  1066. goto Exit;
  1067. }
  1068. ftWriteSrc.dwLowDateTime = fad.ftLastWriteTime.dwLowDateTime;
  1069. ftWriteSrc.dwHighDateTime = fad.ftLastWriteTime.dwHighDateTime;
  1070. }
  1071. //
  1072. // Attempt to open the destination file
  1073. //
  1074. if (!GetFileAttributesEx (lpDestFile, GetFileExInfoStandard, &fad)) {
  1075. DWORD dwError;
  1076. //
  1077. // GetFileAttributesEx failed to query the destination
  1078. // file. If the last error is file not found
  1079. // then we automaticaly will copy the file.
  1080. //
  1081. dwError = GetLastError();
  1082. if (dwError == ERROR_FILE_NOT_FOUND) {
  1083. iCopyFile = 1;
  1084. } else {
  1085. //
  1086. // GetFileAttributesEx failed with some other error
  1087. //
  1088. DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributesEx on the destination failed with error = %d"),
  1089. dwError));
  1090. dwErr = dwError;
  1091. goto Exit;
  1092. }
  1093. } else {
  1094. ftWriteDest.dwLowDateTime = fad.ftLastWriteTime.dwLowDateTime;
  1095. ftWriteDest.dwHighDateTime = fad.ftLastWriteTime.dwHighDateTime;
  1096. }
  1097. } else {
  1098. //
  1099. // The CPD_FORCECOPY flag is turned on, set iCopyFile to 1.
  1100. //
  1101. iCopyFile = 1;
  1102. }
  1103. //
  1104. // If iCopyFile is still zero, then we need to compare
  1105. // the last write time stamps.
  1106. //
  1107. if (!iCopyFile) {
  1108. LONG lResult;
  1109. //
  1110. // If the source is later than the destination
  1111. // we need to copy the file.
  1112. //
  1113. lResult = CompareFileTime(&ftWriteSrc, &ftWriteDest);
  1114. if (lResult == 1) {
  1115. iCopyFile = 1;
  1116. }
  1117. if ( (dwFlags & CPD_COPYIFDIFFERENT) && (lResult == -1) ) {
  1118. iCopyFile = 1;
  1119. }
  1120. }
  1121. //
  1122. // Copy the file if appropriate
  1123. //
  1124. if (iCopyFile) {
  1125. BOOL bCancel = FALSE;
  1126. TCHAR szTempFile[MAX_PATH + 1];
  1127. TCHAR szTempDir[MAX_PATH + 1];
  1128. LPTSTR lpTemp;
  1129. DWORD dwCopyFlags;
  1130. //
  1131. // Clear any existing attributes
  1132. //
  1133. SetFileAttributes (lpDestFile, FILE_ATTRIBUTE_NORMAL);
  1134. if (!(dwFlags & CPD_IGNORESECURITY))
  1135. dwCopyFlags = PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_SKIP_DACL;
  1136. else
  1137. dwCopyFlags = 0;
  1138. dwCopyFlags |= PRIVCOPY_FILE_SUPERSEDE;
  1139. //
  1140. // Figure out what the destination directory is
  1141. //
  1142. lstrcpy (szTempDir, lpDestFile);
  1143. lpTemp = szTempDir + lstrlen (szTempDir);
  1144. while ((lpTemp > szTempDir) && (*lpTemp != TEXT('\\'))) {
  1145. lpTemp--;
  1146. }
  1147. if (lpTemp == szTempDir) {
  1148. lstrcpy (szTempDir, TEXT("."));
  1149. } else {
  1150. *lpTemp = TEXT('\0');
  1151. }
  1152. //
  1153. // Generate a temporary file name
  1154. //
  1155. if (GetTempFileName (szTempDir, TEXT("prf"), 0, szTempFile)) {
  1156. //
  1157. // Copy the file to the temp file name
  1158. //
  1159. if (PrivCopyFileExW(lpSrcFile, szTempFile,
  1160. (LPPROGRESS_ROUTINE) CopyProgressRoutine,
  1161. NULL, &bCancel, dwCopyFlags)) {
  1162. // If it is hive file then open the temporary file, flush and close it to make it more transacted
  1163. if (bHiveFile) {
  1164. HANDLE hTempFile;
  1165. hTempFile = CreateFile(szTempFile, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  1166. if ( hTempFile != INVALID_HANDLE_VALUE ) {
  1167. if ( !FlushFileBuffers(hTempFile) )
  1168. DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to flush temporary file")));
  1169. if ( !CloseHandle(hTempFile) )
  1170. DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to close temporary file handle")));
  1171. }
  1172. else
  1173. DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to open temporary file")));
  1174. }
  1175. //
  1176. // Delete the original file
  1177. //
  1178. if (!DeleteFile (lpDestFile)) {
  1179. if (GetLastError() != ERROR_FILE_NOT_FOUND) {
  1180. dwErr1 = GetLastError();
  1181. DebugMsg((DM_WARNING, TEXT("ReconcileFile: Failed to delete file <%s> with error = %d"),
  1182. lpDestFile, dwErr1));
  1183. DeleteFile(szTempFile);
  1184. goto CopyError;
  1185. }
  1186. }
  1187. //
  1188. // Rename the temp file to the original file name
  1189. //
  1190. if (!MoveFile (szTempFile, lpDestFile)) {
  1191. DWORD dwError = ERROR_SUCCESS; //isolate MoveFile error from other below
  1192. dwErr1 = GetLastError();
  1193. //
  1194. // If we get access denied, let's try to remove the READ ONLY attribute (can't rename files
  1195. // with +r attribute on a Netware Server) from the temp file, do the rename and restore the
  1196. // attributes
  1197. //
  1198. if ( dwErr1 == ERROR_ACCESS_DENIED ) {
  1199. if (!GetFileAttributesEx (szTempFile, GetFileExInfoStandard, &fad)) {
  1200. dwError = GetLastError();
  1201. DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributes on file <%s> failed with error = %d\n"),
  1202. szTempFile,dwError));
  1203. } else {
  1204. if ( fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) {
  1205. dwErr1 = ERROR_SUCCESS ;
  1206. if (!SetFileAttributes (szTempFile, fad.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) {
  1207. dwError = GetLastError();
  1208. DebugMsg((DM_WARNING, TEXT("ReconcileFile: SetFileAttributes on file <%s> failed with error = %d\n"),
  1209. szTempFile,dwError));
  1210. } else {
  1211. if (!MoveFile (szTempFile,lpDestFile)) {
  1212. // Debug message displayed below
  1213. dwErr1 = GetLastError();
  1214. } else {
  1215. if ( !SetFileAttributes (lpDestFile,fad.dwFileAttributes) ) {
  1216. dwError = GetLastError();
  1217. DebugMsg((DM_WARNING, TEXT("ReconcileFile: SetFileAttributes on file <%s> failed with error = %d\n"),
  1218. szTempFile,dwError));
  1219. }
  1220. }
  1221. }
  1222. }
  1223. }
  1224. } // End of ERROR_ACCESS_DENIED test
  1225. if (dwErr1 != ERROR_SUCCESS || dwError != ERROR_SUCCESS) {
  1226. DebugMsg((DM_WARNING, TEXT("ReconcileFile: Failed to rename file <%s> to <%s> with error = %d"),
  1227. szTempFile, lpDestFile, dwErr1));
  1228. // do not remove it in this case.
  1229. goto CopyError;
  1230. }
  1231. }
  1232. DebugMsg((DM_VERBOSE, TEXT("ReconcileFile: %s ==> %s [OK]"),
  1233. lpSrcFile, lpDestFile));
  1234. iCopyFile = 2;
  1235. } else {
  1236. dwErr1 = GetLastError();
  1237. DeleteFile(szTempFile);
  1238. DebugMsg((DM_WARNING, TEXT("ReconcileFile: %s ==> %s [FAILED!!!]"),
  1239. lpSrcFile, szTempFile));
  1240. DebugMsg((DM_WARNING, TEXT("ReconcileFile: CopyFile failed with error = %d"),
  1241. dwErr1));
  1242. goto CopyError;
  1243. }
  1244. } else {
  1245. dwErr1 = GetLastError();
  1246. DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetTempFileName failed with %d"),
  1247. dwErr1));
  1248. goto CopyError;
  1249. }
  1250. } else {
  1251. //
  1252. // No need to copy the file since the time stamps are the same
  1253. // Set iCopyFile to 1 so the return value is success without
  1254. // copying a file.
  1255. //
  1256. iCopyFile = 1;
  1257. }
  1258. goto Exit;
  1259. CopyError:
  1260. iCopyFile = 0;
  1261. dwErr = dwErr1;
  1262. Exit:
  1263. SetLastError(dwErr);
  1264. return iCopyFile;
  1265. }
  1266. //*************************************************************
  1267. //
  1268. // AddFileInfoNode()
  1269. //
  1270. // Purpose: Adds a node to the linklist of files
  1271. //
  1272. // Parameters: lpFileInfo - Link list to add to
  1273. // lpSrcFile - Source filename
  1274. // lpDestFile - Destination filename
  1275. // ftLastWrite - Last write time stamp
  1276. // ftCreationTime - File creation time
  1277. // dwFileSize - Size of the file
  1278. // dwFileAttribs - File attributes
  1279. //
  1280. // Return: TRUE if successful
  1281. // FALSE if an error occurs
  1282. //
  1283. // Comments:
  1284. //
  1285. // History: Date Author Comment
  1286. // 9/28/95 ericflo Created
  1287. //
  1288. //*************************************************************
  1289. BOOL AddFileInfoNode (LPFILEINFO *lpFileInfo, LPTSTR lpSrcFile,
  1290. LPTSTR lpDestFile, LPFILETIME ftLastWrite,
  1291. LPFILETIME ftCreationTime, DWORD dwFileSize,
  1292. DWORD dwFileAttribs, BOOL bHive)
  1293. {
  1294. LPFILEINFO lpNode;
  1295. lpNode = (LPFILEINFO) LocalAlloc(LPTR, sizeof(FILEINFO));
  1296. if (!lpNode) {
  1297. return FALSE;
  1298. }
  1299. lstrcpy (lpNode->szSrc, lpSrcFile);
  1300. lstrcpy (lpNode->szDest, lpDestFile);
  1301. lpNode->ftLastWrite.dwLowDateTime = ftLastWrite->dwLowDateTime;
  1302. lpNode->ftLastWrite.dwHighDateTime = ftLastWrite->dwHighDateTime;
  1303. lpNode->ftCreationTime.dwLowDateTime = ftCreationTime->dwLowDateTime;
  1304. lpNode->ftCreationTime.dwHighDateTime = ftCreationTime->dwHighDateTime;
  1305. lpNode->dwFileSize = dwFileSize;
  1306. lpNode->bHive = bHive;
  1307. lpNode->dwFileAttribs = (dwFileAttribs & ~FILE_ATTRIBUTE_DIRECTORY);
  1308. lpNode->pNext = *lpFileInfo;
  1309. *lpFileInfo = lpNode;
  1310. return TRUE;
  1311. }
  1312. //*************************************************************
  1313. //
  1314. // FreeFileInfoList()
  1315. //
  1316. // Purpose: Free's a file info link list
  1317. //
  1318. // Parameters: lpFileInfo - List to be freed
  1319. //
  1320. // Return: TRUE if successful
  1321. // FALSE if an error occurs
  1322. //
  1323. // Comments:
  1324. //
  1325. // History: Date Author Comment
  1326. // 9/28/95 ericflo Created
  1327. //
  1328. //*************************************************************
  1329. BOOL FreeFileInfoList (LPFILEINFO lpFileInfo)
  1330. {
  1331. LPFILEINFO lpNext;
  1332. if (!lpFileInfo) {
  1333. return TRUE;
  1334. }
  1335. lpNext = lpFileInfo->pNext;
  1336. while (lpFileInfo) {
  1337. LocalFree (lpFileInfo);
  1338. lpFileInfo = lpNext;
  1339. if (lpFileInfo) {
  1340. lpNext = lpFileInfo->pNext;
  1341. }
  1342. }
  1343. return TRUE;
  1344. }
  1345. //*************************************************************
  1346. //
  1347. // SyncItems()
  1348. //
  1349. // Purpose: Removes unnecessary items from the destination
  1350. // directory tree
  1351. //
  1352. // Parameters: lpSrcItems - Link list of source items
  1353. // lpDestItems - Link list of dest items
  1354. // bFile - File or directory list
  1355. //
  1356. // Return: TRUE if successful
  1357. // FALSE if an error occurs
  1358. // Comments:
  1359. //
  1360. // History: Date Author Comment
  1361. // 9/28/95 ericflo Created
  1362. //
  1363. //*************************************************************
  1364. BOOL SyncItems (LPFILEINFO lpSrcItems, LPFILEINFO lpDestItems,
  1365. BOOL bFile, LPFILETIME ftDelRefTime)
  1366. {
  1367. LPFILEINFO lpTempSrc, lpTempDest;
  1368. //
  1369. // Check for NULL pointers
  1370. //
  1371. #ifdef DBG
  1372. if (ftDelRefTime)
  1373. {
  1374. SYSTEMTIME SystemTime;
  1375. FileTimeToSystemTime(ftDelRefTime, &SystemTime);
  1376. DebugMsg((DM_VERBOSE, TEXT("SyncItems: DelRefTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear,
  1377. SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute));
  1378. }
  1379. #endif
  1380. if (!lpSrcItems || !lpDestItems) {
  1381. return TRUE;
  1382. }
  1383. //
  1384. // Loop through everyitem in lpDestItems to see if it
  1385. // is in lpSrcItems. If not, delete it.
  1386. //
  1387. lpTempDest = lpDestItems;
  1388. while (lpTempDest) {
  1389. lpTempSrc = lpSrcItems;
  1390. while (lpTempSrc) {
  1391. if (lstrcmpi(lpTempDest->szSrc, lpTempSrc->szDest) == 0) {
  1392. break;
  1393. }
  1394. lpTempSrc = lpTempSrc->pNext;
  1395. }
  1396. //
  1397. // If lpTempSrc is NULL, then this file / directory is a candidate
  1398. // for being deleted
  1399. //
  1400. if (!lpTempSrc) {
  1401. BOOL bDelete = TRUE;
  1402. //
  1403. // If a delete reference time was offered, compare the
  1404. // source time with the ref time and only delete files
  1405. // which have a source time older than the ref time
  1406. //
  1407. if (ftDelRefTime) {
  1408. if (CompareFileTime (&lpTempDest->ftLastWrite, ftDelRefTime) == 1) {
  1409. bDelete = FALSE;
  1410. }
  1411. else if (CompareFileTime (&lpTempDest->ftCreationTime, ftDelRefTime) == 1) {
  1412. bDelete = FALSE;
  1413. }
  1414. }
  1415. if (bDelete) {
  1416. //
  1417. // Delete the file / directory
  1418. //
  1419. DebugMsg((DM_VERBOSE, TEXT("SyncItems: removing <%s>"),
  1420. lpTempDest->szSrc));
  1421. if (bFile) {
  1422. SetFileAttributes(lpTempDest->szSrc, FILE_ATTRIBUTE_NORMAL);
  1423. if (!DeleteFile (lpTempDest->szSrc)) {
  1424. DebugMsg((DM_WARNING, TEXT("SyncItems: Failed to delete <%s>. Error = %d."),
  1425. lpTempDest->szSrc, GetLastError()));
  1426. }
  1427. } else {
  1428. SetFileAttributes(lpTempDest->szSrc, FILE_ATTRIBUTE_NORMAL);
  1429. if (!RemoveDirectory (lpTempDest->szSrc)) {
  1430. DebugMsg((DM_WARNING, TEXT("SyncItems: Failed to remove <%s>. Error = %d"),
  1431. lpTempDest->szSrc, GetLastError()));
  1432. }
  1433. }
  1434. }
  1435. else
  1436. {
  1437. DebugMsg((DM_VERBOSE, TEXT("SyncItems: New file or directory <%s> in destination since this profile was loaded. This will NOT be deleted."),
  1438. lpTempDest->szSrc));
  1439. #ifdef DBG
  1440. {
  1441. SYSTEMTIME SystemTime;
  1442. FileTimeToSystemTime(&lpTempDest->ftLastWrite, &SystemTime);
  1443. DebugMsg((DM_VERBOSE, TEXT("SyncItems: File WriteTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear,
  1444. SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute));
  1445. FileTimeToSystemTime(&lpTempDest->ftCreationTime, &SystemTime);
  1446. DebugMsg((DM_VERBOSE, TEXT("SyncItems: File CreationTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear,
  1447. SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute));
  1448. }
  1449. #endif
  1450. }
  1451. }
  1452. lpTempDest = lpTempDest->pNext;
  1453. }
  1454. return TRUE;
  1455. }
  1456. //*************************************************************
  1457. //
  1458. // CopyFileFunc()
  1459. //
  1460. // Purpose: Copies files
  1461. //
  1462. // Parameters: lpThreadInfo - Thread information
  1463. //
  1464. // Return: void
  1465. //
  1466. // Comments:
  1467. //
  1468. // History: Date Author Comment
  1469. // 2/23/96 ericflo Created
  1470. //
  1471. //*************************************************************
  1472. void CopyFileFunc (LPTHREADINFO lpThreadInfo)
  1473. {
  1474. HANDLE hInstDll;
  1475. LPFILEINFO lpSrcFile;
  1476. BOOL bRetVal = TRUE;
  1477. DWORD dwError;
  1478. TCHAR szErr[MAX_PATH];
  1479. hInstDll = LoadLibrary (TEXT("userenv.dll"));
  1480. SetThreadDesktop (lpThreadInfo->hDesktop);
  1481. while (TRUE) {
  1482. if (lpThreadInfo->dwError) {
  1483. break;
  1484. }
  1485. //
  1486. // Query for the next file to copy..
  1487. // ignore the hive file since it is already copied..
  1488. //
  1489. WaitForSingleObject(lpThreadInfo->hCopyEvent, INFINITE);
  1490. do {
  1491. lpSrcFile = lpThreadInfo->lpSrcFiles;
  1492. if (lpSrcFile)
  1493. lpThreadInfo->lpSrcFiles = lpThreadInfo->lpSrcFiles->pNext;
  1494. } while (lpSrcFile && (lpSrcFile->bHive));
  1495. SetEvent(lpThreadInfo->hCopyEvent);
  1496. //
  1497. // If NULL, then we're finished.
  1498. //
  1499. if (!lpSrcFile || lpThreadInfo->dwError) {
  1500. break;
  1501. }
  1502. //
  1503. // Copy the file
  1504. //
  1505. if (!ReconcileFile (lpSrcFile->szSrc, lpSrcFile->szDest,
  1506. lpThreadInfo->dwFlags, &lpSrcFile->ftLastWrite,
  1507. lpSrcFile->dwFileSize, FALSE)) {
  1508. if (!(lpThreadInfo->dwFlags & CPD_IGNORECOPYERRORS)) {
  1509. WaitForSingleObject(lpThreadInfo->hCopyEvent, INFINITE);
  1510. if (!(lpThreadInfo->dwError)) {
  1511. dwError = GetLastError();
  1512. ReportError(lpThreadInfo->hTokenUser, ((lpThreadInfo->dwFlags & CPD_NOERRORUI) ? PI_NOUI:0), 3, EVENT_COPYERROR,
  1513. lpSrcFile->szSrc, lpSrcFile->szDest, GetErrString(dwError, szErr));
  1514. lpThreadInfo->dwError = dwError;
  1515. bRetVal = FALSE;
  1516. }
  1517. SetEvent(lpThreadInfo->hCopyEvent);
  1518. break;
  1519. }
  1520. else {
  1521. dwError = GetLastError();
  1522. ReportError(lpThreadInfo->hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR,
  1523. lpSrcFile->szSrc, lpSrcFile->szDest, GetErrString(dwError, szErr));
  1524. }
  1525. }
  1526. }
  1527. //
  1528. // Clean up
  1529. //
  1530. if (hInstDll) {
  1531. FreeLibraryAndExitThread(hInstDll, bRetVal);
  1532. } else {
  1533. ExitThread (bRetVal);
  1534. }
  1535. }
  1536. //*************************************************************
  1537. //
  1538. // ConvertExclusionList()
  1539. //
  1540. // Purpose: Converts the semi-colon profile relative exclusion
  1541. // list to fully qualified null terminated exclusion
  1542. // list
  1543. //
  1544. // Parameters: lpSourceDir - Profile root directory
  1545. // lpExclusionList - List of directories to exclude
  1546. //
  1547. // Return: List if successful
  1548. // NULL if an error occurs
  1549. //
  1550. //*************************************************************
  1551. LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList)
  1552. {
  1553. LPTSTR lpExcludeList = NULL, lpInsert, lpEnd, lpTempList;
  1554. LPCTSTR lpTemp, lpDir;
  1555. TCHAR szTemp[MAX_PATH];
  1556. DWORD dwSize = 2; // double null terminator
  1557. DWORD dwStrLen;
  1558. //
  1559. // Setup a temp buffer to work with
  1560. //
  1561. lstrcpy (szTemp, lpSourceDir);
  1562. lpEnd = CheckSlash (szTemp);
  1563. //
  1564. // Loop through the list
  1565. //
  1566. lpTemp = lpDir = lpExclusionList;
  1567. while (*lpTemp) {
  1568. //
  1569. // Look for the semicolon separator
  1570. //
  1571. while (*lpTemp && ((*lpTemp) != TEXT(';'))) {
  1572. lpTemp++;
  1573. }
  1574. //
  1575. // Remove any leading spaces
  1576. //
  1577. while (*lpDir == TEXT(' ')) {
  1578. lpDir++;
  1579. }
  1580. //
  1581. // Note:
  1582. // Empty Spaces will not make the whole profile dir excluded
  1583. // in RecurseDirectory.
  1584. //
  1585. //
  1586. // Put the directory name on the temp buffer
  1587. //
  1588. lstrcpyn (lpEnd, lpDir, (int)(lpTemp - lpDir + 1));
  1589. DebugMsg((DM_VERBOSE, TEXT("ConvertExclusionList: Adding %s to ExclusionList"), szTemp));
  1590. //
  1591. // Add the string to the exclusion list
  1592. //
  1593. if (lpExcludeList) {
  1594. dwStrLen = lstrlen (szTemp) + 1;
  1595. dwSize += dwStrLen;
  1596. lpTempList = LocalReAlloc (lpExcludeList, dwSize * sizeof(TCHAR),
  1597. LMEM_MOVEABLE | LMEM_ZEROINIT);
  1598. if (!lpTempList) {
  1599. DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to realloc memory with %d"), GetLastError()));
  1600. LocalFree (lpExcludeList);
  1601. lpExcludeList = NULL;
  1602. goto Exit;
  1603. }
  1604. lpExcludeList = lpTempList;
  1605. lpInsert = lpExcludeList + dwSize - dwStrLen - 1;
  1606. lstrcpy (lpInsert, szTemp);
  1607. } else {
  1608. dwSize += lstrlen (szTemp);
  1609. lpExcludeList = LocalAlloc (LPTR, dwSize * sizeof(TCHAR));
  1610. if (!lpExcludeList) {
  1611. DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to alloc memory with %d"), GetLastError()));
  1612. goto Exit;
  1613. }
  1614. lstrcpy (lpExcludeList, szTemp);
  1615. }
  1616. //
  1617. // If we are at the end of the exclusion list, we're done
  1618. //
  1619. if (!(*lpTemp)) {
  1620. goto Exit;
  1621. }
  1622. //
  1623. // Prep for the next entry
  1624. //
  1625. lpTemp++;
  1626. lpDir = lpTemp;
  1627. }
  1628. Exit:
  1629. return lpExcludeList;
  1630. }
  1631. //*************************************************************
  1632. //
  1633. // FindDirectorySize()
  1634. //
  1635. // Purpose: Takes the Directory Name and the list of files
  1636. // returned by RecurseDir and gets the total size.
  1637. //
  1638. // Parameters: lpDir - '\' terminated Source Directory
  1639. // lpFiles - List of files to be copied
  1640. // dwFlags - Flags
  1641. // pdwLargestHiveFile - optional parameter that
  1642. // returns the largest hive
  1643. // file size.
  1644. // pdwTotalFiles - the size of the directory
  1645. //
  1646. // Return: Win32 error code.
  1647. //
  1648. //*************************************************************
  1649. DWORD FindDirectorySize(LPTSTR lpDir, LPFILEINFO lpFiles, DWORD dwFlags, DWORD* pdwLargestHiveFile, DWORD* pdwTotalFiles)
  1650. {
  1651. LPFILEINFO lpTemp = NULL;
  1652. if(pdwLargestHiveFile) {
  1653. *pdwLargestHiveFile = 0;
  1654. }
  1655. if(!pdwTotalFiles) {
  1656. SetLastError(ERROR_INVALID_PARAMETER);
  1657. return ERROR_INVALID_PARAMETER;
  1658. }
  1659. else {
  1660. *pdwTotalFiles = 0;
  1661. }
  1662. lpTemp = lpFiles;
  1663. while (lpTemp) {
  1664. if (lpTemp->bHive) {
  1665. if (!(dwFlags & CPD_IGNOREHIVE)) {
  1666. if(pdwLargestHiveFile && (*pdwLargestHiveFile < lpTemp->dwFileSize)) {
  1667. *pdwLargestHiveFile = lpTemp->dwFileSize;
  1668. }
  1669. *pdwTotalFiles += lpTemp->dwFileSize;
  1670. }
  1671. }
  1672. else {
  1673. *pdwTotalFiles += lpTemp->dwFileSize;
  1674. }
  1675. lpTemp = lpTemp->pNext;
  1676. }
  1677. return ERROR_SUCCESS;
  1678. }
  1679. //*************************************************************
  1680. //
  1681. // FindTotalDiskSpaceNeeded()
  1682. //
  1683. // Purpose: Calculate the maximum amount of disk space on the
  1684. // destination drive that is needed to reconcile the
  1685. // source and the destination directories. The
  1686. // algorithm is as follows:
  1687. // max(source size, destination size) +
  1688. // NUM_COPY_THREADS * size of the largest file in source dir -
  1689. // destination size
  1690. // The reason for this algorithm is that the copy
  1691. // operation is done by NUM_COPY_THREADS threads.
  1692. // They copy the files to a temp file and then delete
  1693. // the destination file and rename the temp file.
  1694. //
  1695. // Parameters: dwTotalSrcFiles total size of the source files
  1696. // dwTotalDestFiles total size of the destination files
  1697. // dwLargestHiveFile largest hive file
  1698. // lpSrcFiles List of source files
  1699. // dwFlags - Flags
  1700. //
  1701. // Return: Disk space needed.
  1702. //
  1703. // History: Created WeiruC 2/10/2000
  1704. //
  1705. //*************************************************************
  1706. DWORD FindTotalDiskSpaceNeeded(DWORD dwTotalSrcFiles,
  1707. DWORD dwTotalDestFiles,
  1708. DWORD dwLargestHiveFile,
  1709. LPFILEINFO lpSrcFiles,
  1710. DWORD dwFlags)
  1711. {
  1712. DWORD dwNumOfCopyThreads = NUM_COPY_THREADS;
  1713. DWORD dwDiskSpaceNeeded = 0;
  1714. LPFILEINFO lpCur = lpSrcFiles;
  1715. DWORD i, j; // loop counters
  1716. //
  1717. // Check for empty file list.
  1718. //
  1719. if(!lpSrcFiles) {
  1720. return dwLargestHiveFile;
  1721. }
  1722. //
  1723. // How many copy threads are there actually?
  1724. //
  1725. if(dwFlags & CPD_SLOWCOPY) {
  1726. dwNumOfCopyThreads = 1;
  1727. }
  1728. //
  1729. // Find the size of the largest file in the source file list. The hive
  1730. // files are not in this file list, be careful not to forget them. Hive
  1731. // files have to be treated very carefully because they are always copied
  1732. // over before we create those copy threads.
  1733. //
  1734. dwDiskSpaceNeeded = FindTotalNMaxFileSize(lpSrcFiles, dwNumOfCopyThreads);
  1735. DebugMsg((DM_VERBOSE, TEXT("FindTotalDiskSpaceNeeded: Largest %d file size is %d"), dwNumOfCopyThreads, dwDiskSpaceNeeded));
  1736. //
  1737. // The actual disk space needed.
  1738. //
  1739. if(dwDiskSpaceNeeded < dwLargestHiveFile) {
  1740. dwDiskSpaceNeeded = dwLargestHiveFile;
  1741. }
  1742. if(dwTotalSrcFiles > dwTotalDestFiles) {
  1743. dwDiskSpaceNeeded += dwTotalSrcFiles - dwTotalDestFiles;
  1744. }
  1745. //
  1746. // It is too much of a pain to actually figure out cluster size impact.
  1747. // We'll just add an extra 10% of the disk space needed.
  1748. //
  1749. dwDiskSpaceNeeded += dwDiskSpaceNeeded / 10;
  1750. return dwDiskSpaceNeeded;
  1751. }
  1752. //*************************************************************
  1753. //
  1754. // FindTotalNMaxFileSize()
  1755. //
  1756. // Purpose: Calculates the total size for dwNumOfFiles
  1757. // number of largest files.
  1758. //
  1759. // Parameters: lpSrcFiles - List of source files
  1760. // dwNumOfFiles - Number of files.
  1761. // dwNumOfFiles <= NUM_COPY_THREADS
  1762. //
  1763. // Return: Disk space needed for n largest files.
  1764. //
  1765. // History: Created santanuc 10/03/2000
  1766. //
  1767. //*************************************************************
  1768. DWORD FindTotalNMaxFileSize(LPFILEINFO lpSrcFiles, DWORD dwNumOfFiles)
  1769. {
  1770. DWORD pdwNMaxVal[NUM_COPY_THREADS], dwIndex;
  1771. LPFILEINFO lpCur;
  1772. DWORD dwTotalSize = 0, dwTmp;
  1773. if (!lpSrcFiles)
  1774. return 0;
  1775. for(dwIndex = 0; dwIndex < dwNumOfFiles; dwIndex++) {
  1776. pdwNMaxVal[dwIndex] = 0;
  1777. }
  1778. for(lpCur = lpSrcFiles; lpCur; lpCur = lpCur->pNext) {
  1779. if (!lpCur->bHive) {
  1780. dwIndex = dwNumOfFiles-1;
  1781. if (lpCur->dwFileSize > pdwNMaxVal[dwIndex]) {
  1782. pdwNMaxVal[dwIndex] = lpCur->dwFileSize;
  1783. while (dwIndex > 0 && pdwNMaxVal[dwIndex] > pdwNMaxVal[dwIndex-1]) {
  1784. dwTmp = pdwNMaxVal[dwIndex-1];
  1785. pdwNMaxVal[dwIndex-1] = pdwNMaxVal[dwIndex];
  1786. pdwNMaxVal[dwIndex] = dwTmp;
  1787. dwIndex--;
  1788. }
  1789. }
  1790. }
  1791. }
  1792. for(dwIndex = 0; dwIndex < dwNumOfFiles; dwIndex++) {
  1793. dwTotalSize += pdwNMaxVal[dwIndex];
  1794. }
  1795. return dwTotalSize;
  1796. }