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.

1168 lines
30 KiB

  1. /*
  2. * copy.c - File copy handler module.
  3. */
  4. /* Headers
  5. **********/
  6. #include "project.h"
  7. #pragma hdrstop
  8. #include "stub.h"
  9. #include "oleutil.h"
  10. /* Constants
  11. ************/
  12. /* size of file copy buffer in bytes */
  13. #define COPY_BUF_SIZE (64 * 1024)
  14. /* Module Variables
  15. *******************/
  16. /* lock count for file copy buffer */
  17. PRIVATE_DATA ULONG MulcCopyBufLock = 0;
  18. /* buffer for file copying */
  19. PRIVATE_DATA PBYTE MpbyteCopyBuf = NULL;
  20. /* length of file copy buffer in bytes */
  21. PRIVATE_DATA UINT MucbCopyBufLen = 0;
  22. /***************************** Private Functions *****************************/
  23. /* Module Prototypes
  24. ********************/
  25. PRIVATE_CODE TWINRESULT SimpleCopy(PRECNODE, RECSTATUSPROC, LPARAM);
  26. PRIVATE_CODE TWINRESULT CreateDestinationFolders(PCRECNODE);
  27. PRIVATE_CODE TWINRESULT CreateCopyBuffer(void);
  28. PRIVATE_CODE void DestroyCopyBuffer(void);
  29. PRIVATE_CODE TWINRESULT CopyFileByHandle(HANDLE, HANDLE, RECSTATUSPROC, LPARAM, ULONG, PULONG);
  30. PRIVATE_CODE TWINRESULT CopyFileByName(PCRECNODE, PRECNODE, RECSTATUSPROC, LPARAM, ULONG, PULONG);
  31. PRIVATE_CODE ULONG DetermineCopyScale(PCRECNODE);
  32. PRIVATE_CODE BOOL IsCopyDestination(PCRECNODE);
  33. PRIVATE_CODE BOOL SetDestinationTimeStamps(PCRECNODE);
  34. PRIVATE_CODE BOOL DeleteFolderProc(LPCTSTR, PCWIN32_FIND_DATA, PVOID);
  35. #ifdef DEBUG
  36. PRIVATE_CODE BOOL CopyBufferIsOk(void);
  37. PRIVATE_CODE BOOL VerifyRECITEMAndSrcRECNODE(PCRECNODE);
  38. #endif
  39. /*
  40. ** SimpleCopy()
  41. **
  42. **
  43. **
  44. ** Arguments:
  45. **
  46. ** Returns:
  47. **
  48. ** Side Effects: none
  49. */
  50. PRIVATE_CODE TWINRESULT SimpleCopy(PRECNODE prnSrc, RECSTATUSPROC rsp,
  51. LPARAM lpCallbackData)
  52. {
  53. TWINRESULT tr;
  54. ULONG ulScale;
  55. PRECNODE prnDest;
  56. ULONG ulCurrent = 0;
  57. /* lpCallbackData may be any value. */
  58. ASSERT(IS_VALID_STRUCT_PTR(prnSrc, CRECNODE));
  59. ASSERT(! rsp ||
  60. IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
  61. ulScale = DetermineCopyScale(prnSrc);
  62. /* Copy the source file to each destination file. */
  63. tr = TR_SUCCESS;
  64. BeginCopy();
  65. for (prnDest = prnSrc->priParent->prnFirst;
  66. prnDest;
  67. prnDest = prnDest->prnNext)
  68. {
  69. if (prnDest != prnSrc)
  70. {
  71. if (IsCopyDestination(prnDest))
  72. {
  73. tr = CopyFileByName(prnSrc, prnDest, rsp, lpCallbackData,
  74. ulScale, &ulCurrent);
  75. if (tr != TR_SUCCESS)
  76. break;
  77. ASSERT(ulCurrent <= ulScale);
  78. }
  79. }
  80. }
  81. EndCopy();
  82. return(tr);
  83. }
  84. /*
  85. ** CreateDestinationFolders()
  86. **
  87. **
  88. **
  89. ** Arguments:
  90. **
  91. ** Returns:
  92. **
  93. ** Side Effects: none
  94. */
  95. PRIVATE_CODE TWINRESULT CreateDestinationFolders(PCRECNODE pcrnSrc)
  96. {
  97. TWINRESULT tr = TR_SUCCESS;
  98. PCRECNODE pcrnDest;
  99. for (pcrnDest = pcrnSrc->priParent->prnFirst;
  100. pcrnDest;
  101. pcrnDest = pcrnDest->prnNext)
  102. {
  103. if (pcrnDest->rnaction == RNA_COPY_TO_ME)
  104. {
  105. tr = CreateFolders(pcrnDest->pcszFolder,
  106. ((PCOBJECTTWIN)(pcrnDest->hObjectTwin))->hpath);
  107. if (tr != TR_SUCCESS)
  108. break;
  109. }
  110. }
  111. return(tr);
  112. }
  113. /*
  114. ** CreateCopyBuffer()
  115. **
  116. **
  117. **
  118. ** Arguments:
  119. **
  120. ** Returns:
  121. **
  122. ** Side Effects: none
  123. */
  124. PRIVATE_CODE TWINRESULT CreateCopyBuffer(void)
  125. {
  126. TWINRESULT tr;
  127. ASSERT(CopyBufferIsOk());
  128. /* Has the copy buffer already been allocated? */
  129. if (MpbyteCopyBuf)
  130. /* Yes. */
  131. tr = TR_SUCCESS;
  132. else
  133. {
  134. /* No. Allocate it. */
  135. if (AllocateMemory(COPY_BUF_SIZE, &MpbyteCopyBuf))
  136. {
  137. MucbCopyBufLen = COPY_BUF_SIZE;
  138. tr = TR_SUCCESS;
  139. TRACE_OUT((TEXT("CreateCopyBuffer(): %u byte file copy buffer allocated."),
  140. MucbCopyBufLen));
  141. }
  142. else
  143. tr = TR_OUT_OF_MEMORY;
  144. }
  145. ASSERT(CopyBufferIsOk());
  146. return(tr);
  147. }
  148. /*
  149. ** DestroyCopyBuffer()
  150. **
  151. **
  152. **
  153. ** Arguments:
  154. **
  155. ** Returns:
  156. **
  157. ** Side Effects: none
  158. */
  159. PRIVATE_CODE void DestroyCopyBuffer(void)
  160. {
  161. ASSERT(CopyBufferIsOk());
  162. /* Has the copy buffer already been allocated? */
  163. if (MpbyteCopyBuf)
  164. {
  165. /* Yes. Free it. */
  166. FreeMemory(MpbyteCopyBuf);
  167. MpbyteCopyBuf = NULL;
  168. TRACE_OUT((TEXT("DestroyCopyBuffer(): %u byte file copy buffer freed."),
  169. MucbCopyBufLen));
  170. MucbCopyBufLen = 0;
  171. }
  172. ASSERT(CopyBufferIsOk());
  173. return;
  174. }
  175. /*
  176. ** CopyFileByHandle()
  177. **
  178. ** Copies one file to another.
  179. **
  180. ** Arguments: hfSrc - file handle to open source file
  181. ** hfDest - file handle to open destination file
  182. **
  183. ** Returns: TWINRESULT
  184. **
  185. ** Side Effects: Leaves the file pointer of each file at the end of the file.
  186. */
  187. PRIVATE_CODE TWINRESULT CopyFileByHandle(HANDLE hfSrc, HANDLE hfDest,
  188. RECSTATUSPROC rsp, LPARAM lpCallbackData,
  189. ULONG ulScale, PULONG pulcbTotal)
  190. {
  191. TWINRESULT tr;
  192. /* lpCallbackData may be any value. */
  193. /* ulScale may be any value. */
  194. ASSERT(IS_VALID_HANDLE(hfSrc, FILE));
  195. ASSERT(IS_VALID_HANDLE(hfDest, FILE));
  196. ASSERT(! rsp ||
  197. IS_VALID_CODE_PTR(rsp, RECSTATUSROC));
  198. ASSERT(IS_VALID_WRITE_PTR(pulcbTotal, ULONG));
  199. /* Make sure the copy buffer has been created. */
  200. tr = CreateCopyBuffer();
  201. if (tr == TR_SUCCESS)
  202. {
  203. BeginCopy();
  204. /* Move to the beginning of the files. */
  205. if (SetFilePointer(hfSrc, 0, NULL, FILE_BEGIN) != INVALID_SEEK_POSITION)
  206. {
  207. if (SetFilePointer(hfDest, 0, NULL, FILE_BEGIN) != INVALID_SEEK_POSITION)
  208. {
  209. do
  210. {
  211. DWORD dwcbRead;
  212. if (ReadFile(hfSrc, MpbyteCopyBuf, MucbCopyBufLen, &dwcbRead,
  213. NULL))
  214. {
  215. if (dwcbRead)
  216. {
  217. DWORD dwcbWritten;
  218. if (WriteFile(hfDest, MpbyteCopyBuf, dwcbRead,
  219. &dwcbWritten, NULL) &&
  220. dwcbWritten == dwcbRead)
  221. {
  222. RECSTATUSUPDATE rsu;
  223. ASSERT(*pulcbTotal <= ULONG_MAX - dwcbRead);
  224. *pulcbTotal += dwcbRead;
  225. rsu.ulProgress = *pulcbTotal;
  226. rsu.ulScale = ulScale;
  227. if (! NotifyReconciliationStatus(rsp, RS_DELTA_COPY,
  228. (LPARAM)&rsu,
  229. lpCallbackData))
  230. tr = TR_ABORT;
  231. }
  232. else
  233. tr = TR_DEST_WRITE_FAILED;
  234. }
  235. else
  236. /* Hit EOF. Stop. */
  237. break;
  238. }
  239. else
  240. tr = TR_SRC_READ_FAILED;
  241. } while (tr == TR_SUCCESS);
  242. }
  243. else
  244. tr = TR_DEST_WRITE_FAILED;
  245. }
  246. else
  247. tr = TR_SRC_READ_FAILED;
  248. EndCopy();
  249. }
  250. return(tr);
  251. }
  252. // MakeAnsiPath
  253. //
  254. // Copys path pszIn to pszOut, ensuring that pszOut has a valid ANSI mapping
  255. void MakeAnsiPath(LPTSTR pszIn, LPTSTR pszOut, int cchMax)
  256. {
  257. #ifdef UNICODE
  258. CHAR szAnsi[MAX_PATH];
  259. pszOut[0] = L'\0';
  260. WideCharToMultiByte(CP_ACP, 0, pszIn, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL);
  261. MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, pszOut, cchMax);
  262. if (lstrcmp(pszOut, pszIn))
  263. {
  264. // Cannot convert losslessly from Unicode -> Ansi, so get the short path
  265. lstrcpyn(pszOut, pszIn, cchMax);
  266. SheShortenPath(pszOut, TRUE);
  267. }
  268. #else
  269. lstrcpyn(pszOut, pszIn, cchMax);
  270. #endif
  271. }
  272. /*
  273. ** CopyFileByName()
  274. **
  275. ** Copies one file over another.
  276. **
  277. ** Arguments:
  278. **
  279. ** Returns: TWINRESULT
  280. **
  281. ** Side Effects: Copies source file's time stamp to destination file.
  282. */
  283. PRIVATE_CODE TWINRESULT CopyFileByName(PCRECNODE pcrnSrc, PRECNODE prnDest,
  284. RECSTATUSPROC rsp, LPARAM lpCallbackData,
  285. ULONG ulScale, PULONG pulcbTotal)
  286. {
  287. TWINRESULT tr;
  288. TCHAR rgchSrcPath[MAX_PATH_LEN];
  289. TCHAR rgchDestPath[MAX_PATH_LEN];
  290. /* lpCallbackData may be any value. */
  291. /* ulScale may be any value. */
  292. ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE));
  293. ASSERT(IS_VALID_STRUCT_PTR(prnDest, CRECNODE));
  294. ASSERT(! rsp ||
  295. IS_VALID_CODE_PTR(rsp, RECSTATUSROC));
  296. ASSERT(IS_VALID_WRITE_PTR(pulcbTotal, ULONG));
  297. /* Create source path string. */
  298. ComposePath(rgchSrcPath, pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName, ARRAYSIZE(rgchSrcPath));
  299. ASSERT(lstrlen(rgchSrcPath) < ARRAYSIZE(rgchSrcPath));
  300. /* Create destination path string. */
  301. ComposePath(rgchDestPath, prnDest->pcszFolder, prnDest->priParent->pcszName, ARRAYSIZE(rgchDestPath));
  302. ASSERT(lstrlen(rgchDestPath) < ARRAYSIZE(rgchDestPath));
  303. /* Check volumes. */
  304. if (MyIsPathOnVolume(rgchSrcPath, (HPATH)(pcrnSrc->hvid)) &&
  305. MyIsPathOnVolume(rgchDestPath, (HPATH)(prnDest->hvid)))
  306. {
  307. FILESTAMP fsSrc;
  308. FILESTAMP fsDest;
  309. /* Compare current file stamps with recorded file stamps. */
  310. MyGetFileStamp(rgchSrcPath, &fsSrc);
  311. MyGetFileStamp(rgchDestPath, &fsDest);
  312. if (! MyCompareFileStamps(&(pcrnSrc->fsCurrent), &fsSrc) &&
  313. ! MyCompareFileStamps(&(prnDest->fsCurrent), &fsDest))
  314. {
  315. HANDLE hfSrc;
  316. /* Open source file. Assume source file will be read sequentially. */
  317. hfSrc = CreateFile(rgchSrcPath, GENERIC_READ, FILE_SHARE_READ, NULL,
  318. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  319. if (hfSrc != INVALID_HANDLE_VALUE)
  320. {
  321. HANDLE hfDest;
  322. /*
  323. * Create destination file. Assume destination file will be
  324. * written sequentially.
  325. */
  326. TCHAR szAnsiPath[MAX_PATH];
  327. MakeAnsiPath(rgchDestPath, szAnsiPath, ARRAYSIZE(szAnsiPath));
  328. hfDest = CreateFile(szAnsiPath, GENERIC_WRITE, 0, NULL,
  329. CREATE_ALWAYS,
  330. (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN),
  331. NULL);
  332. if (hfDest != INVALID_HANDLE_VALUE)
  333. {
  334. /* Everything is cool. Copy the file. */
  335. tr = CopyFileByHandle(hfSrc, hfDest, rsp,
  336. lpCallbackData, ulScale,
  337. pulcbTotal);
  338. if (tr == TR_SUCCESS)
  339. {
  340. /*
  341. * Set the destination file's time stamp to the source
  342. * file's time stamp to assist clients that don't maintain
  343. * a persistent briefcase database, like MPR. Failure to
  344. * set the time stamp is not fatal.
  345. */
  346. if (! SetFileTime(hfDest, NULL, NULL,
  347. &(pcrnSrc->fsCurrent.ftMod)))
  348. WARNING_OUT((TEXT("CopyFileByName(): Failed to set last modification time stamp of destination file %s to match source file %s."),
  349. rgchDestPath,
  350. rgchSrcPath));
  351. }
  352. /* Failing to close the destination file is fatal here. */
  353. if (! CloseHandle(hfDest) && tr == TR_SUCCESS)
  354. tr = TR_DEST_WRITE_FAILED;
  355. }
  356. else
  357. tr = TR_DEST_OPEN_FAILED;
  358. /* Failing to close the source file successfully is not fatal. */
  359. CloseHandle(hfSrc);
  360. }
  361. else
  362. tr = TR_SRC_OPEN_FAILED;
  363. }
  364. else
  365. tr = TR_FILE_CHANGED;
  366. }
  367. else
  368. tr = TR_UNAVAILABLE_VOLUME;
  369. #ifdef DEBUG
  370. if (tr == TR_SUCCESS)
  371. TRACE_OUT((TEXT("CopyFileByName(): %s\\%s copied to %s\\%s."),
  372. pcrnSrc->pcszFolder,
  373. pcrnSrc->priParent->pcszName,
  374. prnDest->pcszFolder,
  375. prnDest->priParent->pcszName));
  376. else
  377. TRACE_OUT((TEXT("CopyFileByName(): Failed to copy %s\\%s to %s\\%s."),
  378. pcrnSrc->pcszFolder,
  379. pcrnSrc->priParent->pcszName,
  380. prnDest->pcszFolder,
  381. prnDest->priParent->pcszName));
  382. #endif
  383. return(tr);
  384. }
  385. /*
  386. ** DetermineCopyScale()
  387. **
  388. **
  389. **
  390. ** Arguments:
  391. **
  392. ** Returns:
  393. **
  394. ** Side Effects: none
  395. */
  396. PRIVATE_CODE ULONG DetermineCopyScale(PCRECNODE pcrnSrc)
  397. {
  398. DWORD dwcbSrcFileLen;
  399. PCRECNODE pcrn;
  400. ULONG ulScale = 0;
  401. ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE));
  402. /*
  403. * RAIDRAID: (16257) If anyone tries to copy more than 4 Gb of files, this
  404. * scaling calculation is broken.
  405. */
  406. ASSERT(! pcrnSrc->fsCurrent.dwcbHighLength);
  407. dwcbSrcFileLen = pcrnSrc->fsCurrent.dwcbLowLength;
  408. for (pcrn = pcrnSrc->priParent->prnFirst; pcrn; pcrn = pcrn->prnNext)
  409. {
  410. if (pcrn != pcrnSrc)
  411. {
  412. if (IsCopyDestination(pcrn))
  413. {
  414. ASSERT(ulScale < ULONG_MAX - dwcbSrcFileLen);
  415. ulScale += dwcbSrcFileLen;
  416. }
  417. }
  418. }
  419. TRACE_OUT((TEXT("DetermineCopyScale(): Scale for %s is %lu."),
  420. pcrnSrc->priParent->pcszName,
  421. ulScale));
  422. return(ulScale);
  423. }
  424. /*
  425. ** IsCopyDestination()
  426. **
  427. **
  428. **
  429. ** Arguments:
  430. **
  431. ** Returns:
  432. **
  433. ** Side Effects: none
  434. */
  435. PRIVATE_CODE BOOL IsCopyDestination(PCRECNODE pcrn)
  436. {
  437. BOOL bDest = FALSE;
  438. ASSERT(IS_VALID_STRUCT_PTR(pcrn, CRECNODE));
  439. switch (pcrn->priParent->riaction)
  440. {
  441. case RIA_COPY:
  442. switch (pcrn->rnaction)
  443. {
  444. case RNA_COPY_TO_ME:
  445. bDest = TRUE;
  446. break;
  447. default:
  448. break;
  449. }
  450. break;
  451. case RIA_MERGE:
  452. switch (pcrn->rnaction)
  453. {
  454. case RNA_COPY_TO_ME:
  455. case RNA_MERGE_ME:
  456. bDest = TRUE;
  457. break;
  458. default:
  459. break;
  460. }
  461. break;
  462. default:
  463. ERROR_OUT((TEXT("IsCopyDestination(): Bad RECITEM action %d."),
  464. pcrn->priParent->riaction));
  465. break;
  466. }
  467. return(bDest);
  468. }
  469. /*
  470. ** SetDestinationTimeStamps()
  471. **
  472. **
  473. **
  474. ** Arguments:
  475. **
  476. ** Returns:
  477. **
  478. ** Side Effects: none
  479. */
  480. PRIVATE_CODE BOOL SetDestinationTimeStamps(PCRECNODE pcrnSrc)
  481. {
  482. BOOL bResult = TRUE;
  483. PCRECNODE pcrn;
  484. ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE));
  485. for (pcrn = pcrnSrc->priParent->prnFirst;
  486. pcrn;
  487. pcrn = pcrn->prnNext)
  488. {
  489. if (pcrn->rnaction == RNA_COPY_TO_ME)
  490. {
  491. TCHAR rgchPath[MAX_PATH_LEN];
  492. HANDLE hfDest;
  493. ComposePath(rgchPath, pcrn->pcszFolder, pcrn->priParent->pcszName, ARRAYSIZE(rgchPath));
  494. ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
  495. hfDest = CreateFile(rgchPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
  496. FILE_ATTRIBUTE_NORMAL, NULL);
  497. if (hfDest != INVALID_HANDLE_VALUE)
  498. {
  499. if (! SetFileTime(hfDest, NULL, NULL, &(pcrnSrc->fsCurrent.ftMod)))
  500. bResult = FALSE;
  501. if (! CloseHandle(hfDest))
  502. bResult = FALSE;
  503. }
  504. else
  505. bResult = FALSE;
  506. if (bResult)
  507. TRACE_OUT((TEXT("SetDestinationTimeStamps(): Set last modification time stamp of %s to match last modification time stamp of %s\\%s."),
  508. rgchPath,
  509. pcrnSrc->pcszFolder,
  510. pcrnSrc->priParent->pcszName));
  511. else
  512. WARNING_OUT((TEXT("SetDestinationTimeStamps(): Failed to set last modification time stamp of %s to match last modification time stamp of %s\\%s."),
  513. rgchPath,
  514. pcrnSrc->pcszFolder,
  515. pcrnSrc->priParent->pcszName));
  516. }
  517. }
  518. return(bResult);
  519. }
  520. /*
  521. ** DeleteFolderProc()
  522. **
  523. **
  524. **
  525. ** Arguments:
  526. **
  527. ** Returns:
  528. **
  529. ** Side Effects: none
  530. */
  531. PRIVATE_CODE BOOL DeleteFolderProc(LPCTSTR pcszFolder, PCWIN32_FIND_DATA pcwfd,
  532. PVOID ptr)
  533. {
  534. ASSERT(IsCanonicalPath(pcszFolder));
  535. ASSERT(IS_VALID_READ_PTR(pcwfd, CWIN32_FIND_DATA));
  536. ASSERT(IS_VALID_WRITE_PTR(ptr, TWINRESULT));
  537. if (IS_ATTR_DIR(pcwfd->dwFileAttributes))
  538. {
  539. TCHAR rgchPath[MAX_PATH_LEN];
  540. ComposePath(rgchPath, pcszFolder, pcwfd->cFileName, ARRAYSIZE(rgchPath));
  541. ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
  542. if (RemoveDirectory(rgchPath))
  543. {
  544. WARNING_OUT((TEXT("DeleteFolderProc(): Removed folder %s."),
  545. rgchPath));
  546. NotifyShell(rgchPath, NSE_DELETE_FOLDER);
  547. }
  548. else
  549. {
  550. WARNING_OUT((TEXT("DeleteFolderProc(): Failed to remove folder %s."),
  551. rgchPath));
  552. *(PTWINRESULT)ptr = TR_DEST_WRITE_FAILED;
  553. }
  554. }
  555. else
  556. TRACE_OUT((TEXT("DeleteFolderProc(): Skipping file %s\\%s."),
  557. pcszFolder,
  558. pcwfd->cFileName));
  559. return(TRUE);
  560. }
  561. #ifdef DEBUG
  562. /*
  563. ** CopyBufferIsOk()
  564. **
  565. **
  566. **
  567. ** Arguments:
  568. **
  569. ** Returns:
  570. **
  571. ** Side Effects: none
  572. */
  573. PRIVATE_CODE BOOL CopyBufferIsOk(void)
  574. {
  575. /* Are the module copy buffer variables in a correct state? */
  576. return((! MucbCopyBufLen &&
  577. ! MpbyteCopyBuf) ||
  578. (MucbCopyBufLen > 0 &&
  579. IS_VALID_WRITE_BUFFER_PTR(MpbyteCopyBuf, BYTE, MucbCopyBufLen)));
  580. }
  581. /*
  582. ** VerifyRECITEMAndSrcRECNODE()
  583. **
  584. **
  585. **
  586. ** Arguments:
  587. **
  588. ** Returns:
  589. **
  590. ** Side Effects: none
  591. */
  592. PRIVATE_CODE BOOL VerifyRECITEMAndSrcRECNODE(PCRECNODE pcrnSrc)
  593. {
  594. /* Do the RECITEM and source RECNODE actions match? */
  595. return((pcrnSrc->priParent->riaction == RIA_COPY &&
  596. pcrnSrc->rnaction == RNA_COPY_FROM_ME) ||
  597. (pcrnSrc->priParent->riaction == RIA_MERGE &&
  598. pcrnSrc->rnaction == RNA_MERGE_ME));
  599. }
  600. #endif
  601. /****************************** Public Functions *****************************/
  602. /*
  603. ** BeginCopy()
  604. **
  605. ** Increments copy buffer lock count.
  606. **
  607. ** Arguments:
  608. **
  609. ** Returns: void
  610. **
  611. ** Side Effects: none
  612. */
  613. PUBLIC_CODE void BeginCopy(void)
  614. {
  615. ASSERT(CopyBufferIsOk());
  616. ASSERT(MulcCopyBufLock < ULONG_MAX);
  617. MulcCopyBufLock++;
  618. ASSERT(CopyBufferIsOk());
  619. return;
  620. }
  621. /*
  622. ** EndCopy()
  623. **
  624. ** Decrements copy buffer lock count.
  625. **
  626. ** Arguments:
  627. **
  628. ** Returns: void
  629. **
  630. ** Side Effects: Frees copy buffer if lock count goes to 0.
  631. */
  632. PUBLIC_CODE void EndCopy(void)
  633. {
  634. ASSERT(CopyBufferIsOk());
  635. /* Is the copy buffer still locked? */
  636. if (! --MulcCopyBufLock)
  637. DestroyCopyBuffer();
  638. ASSERT(CopyBufferIsOk());
  639. return;
  640. }
  641. /*
  642. ** CopyHandler()
  643. **
  644. **
  645. **
  646. ** Arguments:
  647. **
  648. ** Returns:
  649. **
  650. ** Side Effects: none
  651. */
  652. PUBLIC_CODE TWINRESULT CopyHandler(PRECNODE prnSrc, RECSTATUSPROC rsp,
  653. LPARAM lpCallbackData, DWORD dwFlags,
  654. HWND hwndOwner, HWND hwndProgressFeedback)
  655. {
  656. TWINRESULT tr;
  657. RECSTATUSUPDATE rsu;
  658. /* lpCallbackData may be any value. */
  659. ASSERT(IS_VALID_STRUCT_PTR(prnSrc, CRECNODE));
  660. ASSERT(! rsp ||
  661. IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
  662. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RI_FLAGS));
  663. ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_ALLOW_UI) ||
  664. IS_VALID_HANDLE(hwndOwner, WND));
  665. ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID) ||
  666. IS_VALID_HANDLE(hwndProgressFeedback, WND));
  667. ASSERT(VerifyRECITEMAndSrcRECNODE(prnSrc));
  668. /* 0% complete. */
  669. rsu.ulScale = 1;
  670. rsu.ulProgress = 0;
  671. if (NotifyReconciliationStatus(rsp, RS_BEGIN_COPY, (LPARAM)&rsu,
  672. lpCallbackData))
  673. {
  674. tr = CreateDestinationFolders(prnSrc);
  675. if (tr == TR_SUCCESS)
  676. {
  677. TCHAR rgchPath[MAX_PATH_LEN];
  678. CLSID clsidReconciler;
  679. HRESULT hr;
  680. ComposePath(rgchPath, prnSrc->pcszFolder, prnSrc->priParent->pcszName, ARRAYSIZE(rgchPath));
  681. ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
  682. if (SUCCEEDED(GetCopyHandlerClassID(rgchPath, &clsidReconciler)))
  683. {
  684. hr = OLECopy(prnSrc, &clsidReconciler, rsp, lpCallbackData,
  685. dwFlags, hwndOwner, hwndProgressFeedback);
  686. if (SUCCEEDED(hr))
  687. {
  688. if (hr != S_FALSE)
  689. {
  690. /*
  691. * Set the destination files' time stamps to the source
  692. * file's time stamp to assist clients that don't maintain
  693. * a persistent briefcase database, like MPR. Failure to
  694. * set the time stamps is not fatal.
  695. */
  696. ASSERT(hr == REC_S_IDIDTHEUPDATES);
  697. TRACE_OUT((TEXT("CopyHandler(): OLECopy() on %s returned %s."),
  698. rgchPath,
  699. GetHRESULTString(hr)));
  700. if (! SetDestinationTimeStamps(prnSrc))
  701. WARNING_OUT((TEXT("CopyHandler(): SetDestinationTimeStamps() failed. Not all destination files have been marked with source file's time stamp.")));
  702. tr = TR_SUCCESS;
  703. }
  704. else
  705. {
  706. WARNING_OUT((TEXT("CopyHandler(): OLECopy() on %s returned %s. Resorting to internal copy routine."),
  707. rgchPath,
  708. GetHRESULTString(hr)));
  709. /*
  710. * Update the source RECNODE's file stamp in case it was
  711. * changed by the reconciler.
  712. */
  713. MyGetFileStampByHPATH(((PCOBJECTTWIN)(prnSrc->hObjectTwin))->hpath,
  714. GetString(((PCOBJECTTWIN)(prnSrc->hObjectTwin))->ptfParent->hsName),
  715. &(prnSrc->fsCurrent));
  716. tr = SimpleCopy(prnSrc, rsp, lpCallbackData);
  717. }
  718. }
  719. else
  720. tr = TranslateHRESULTToTWINRESULT(hr);
  721. }
  722. else
  723. tr = SimpleCopy(prnSrc, rsp, lpCallbackData);
  724. if (tr == TR_SUCCESS)
  725. {
  726. /* 100% complete. */
  727. rsu.ulScale = 1;
  728. rsu.ulProgress = 1;
  729. /* Don't allow abort here. */
  730. NotifyReconciliationStatus(rsp, RS_END_COPY, (LPARAM)&rsu,
  731. lpCallbackData);
  732. }
  733. }
  734. }
  735. else
  736. tr = TR_ABORT;
  737. return(tr);
  738. }
  739. /*
  740. ** NotifyReconciliationStatus()
  741. **
  742. **
  743. **
  744. ** Arguments:
  745. **
  746. ** Returns:
  747. **
  748. ** Side Effects: none
  749. */
  750. PUBLIC_CODE BOOL NotifyReconciliationStatus(RECSTATUSPROC rsp, UINT uMsg, LPARAM lp,
  751. LPARAM lpCallbackData)
  752. {
  753. BOOL bContinue;
  754. /* lp may be any value. */
  755. /* lpCallbackData may be any value. */
  756. ASSERT(! rsp ||
  757. IS_VALID_CODE_PTR(rsp, RECSTATUSROC));
  758. ASSERT(IsValidRecStatusProcMsg(uMsg));
  759. if (rsp)
  760. {
  761. TRACE_OUT((TEXT("NotifyReconciliationStatus(): Calling RECSTATUSPROC with message %s, ulProgress %lu, ulScale %lu, callback data %#lx."),
  762. GetRECSTATUSPROCMSGString(uMsg),
  763. ((PCRECSTATUSUPDATE)lp)->ulProgress,
  764. ((PCRECSTATUSUPDATE)lp)->ulScale,
  765. lpCallbackData));
  766. bContinue = (*rsp)(uMsg, lp, lpCallbackData);
  767. }
  768. else
  769. {
  770. TRACE_OUT((TEXT("NotifyReconciliationStatus(): Not calling NULL RECSTATUSPROC with message %s, ulProgress %lu, ulScale %lu, callback data %#lx."),
  771. GetRECSTATUSPROCMSGString(uMsg),
  772. ((PCRECSTATUSUPDATE)lp)->ulProgress,
  773. ((PCRECSTATUSUPDATE)lp)->ulScale,
  774. lpCallbackData));
  775. bContinue = TRUE;
  776. }
  777. if (! bContinue)
  778. WARNING_OUT((TEXT("NotifyReconciliationStatus(): Client callback aborted reconciliation.")));
  779. return(bContinue);
  780. }
  781. /*
  782. ** CreateFolders()
  783. **
  784. **
  785. **
  786. ** Arguments:
  787. **
  788. ** Returns:
  789. **
  790. ** Side Effects: none
  791. */
  792. PUBLIC_CODE TWINRESULT CreateFolders(LPCTSTR pcszPath, HPATH hpath)
  793. {
  794. TWINRESULT tr;
  795. ASSERT(IsCanonicalPath(pcszPath));
  796. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  797. if (MyIsPathOnVolume(pcszPath, hpath))
  798. {
  799. TCHAR rgchPath[MAX_PATH_LEN];
  800. LPTSTR pszRootEnd;
  801. LPTSTR pszHackSlash;
  802. /* Create working copy of path. */
  803. ASSERT(lstrlen(pcszPath) < ARRAYSIZE(rgchPath));
  804. lstrcpyn(rgchPath, pcszPath, ARRAYSIZE(rgchPath));
  805. pszRootEnd = FindEndOfRootSpec(rgchPath, hpath);
  806. /*
  807. * Hack off the path at each successive slash, and check to see if that
  808. * folder needs to be created.
  809. */
  810. tr = TR_SUCCESS;
  811. pszHackSlash = pszRootEnd;
  812. while (*pszHackSlash)
  813. {
  814. TCHAR chReplaced;
  815. while (*pszHackSlash && *pszHackSlash != TEXT('\\'))
  816. pszHackSlash = CharNext(pszHackSlash);
  817. /* Replace the slash with a null terminator to set the current folder. */
  818. chReplaced = *pszHackSlash;
  819. *pszHackSlash = TEXT('\0');
  820. /* Does the folder exist? */
  821. if (! PathExists(rgchPath))
  822. {
  823. /* No. Try to create it. */
  824. TCHAR szAnsiPath[MAX_PATH];
  825. MakeAnsiPath(rgchPath, szAnsiPath, ARRAYSIZE(szAnsiPath));
  826. if (CreateDirectory(szAnsiPath, NULL))
  827. {
  828. WARNING_OUT((TEXT("CreateFolders(): Created folder %s."),
  829. rgchPath));
  830. NotifyShell(rgchPath, NSE_CREATE_FOLDER);
  831. }
  832. else
  833. {
  834. WARNING_OUT((TEXT("CreateFolders(): Failed to create folder %s."),
  835. rgchPath));
  836. tr = TR_DEST_OPEN_FAILED;
  837. break;
  838. }
  839. }
  840. *pszHackSlash = chReplaced;
  841. if (chReplaced)
  842. pszHackSlash++;
  843. }
  844. }
  845. else
  846. tr = TR_UNAVAILABLE_VOLUME;
  847. return(tr);
  848. }
  849. /*
  850. ** DestroySubtree()
  851. **
  852. **
  853. **
  854. ** Arguments:
  855. **
  856. ** Returns:
  857. **
  858. ** Side Effects: none
  859. */
  860. PUBLIC_CODE TWINRESULT DestroySubtree(LPCTSTR pcszPath, HPATH hpath)
  861. {
  862. TWINRESULT tr;
  863. ASSERT(IsCanonicalPath(pcszPath));
  864. ASSERT(IS_VALID_HANDLE(hpath, PATH));
  865. if (MyIsPathOnVolume(pcszPath, hpath))
  866. {
  867. tr = ExpandSubtree(hpath, &DeleteFolderProc, &tr);
  868. if (tr == TR_SUCCESS)
  869. {
  870. if (RemoveDirectory(pcszPath))
  871. {
  872. WARNING_OUT((TEXT("DestroySubtree(): Subtree %s removed successfully."),
  873. pcszPath));
  874. NotifyShell(pcszPath, NSE_DELETE_FOLDER);
  875. }
  876. else
  877. {
  878. if (PathExists(pcszPath))
  879. {
  880. /* Still there. */
  881. WARNING_OUT((TEXT("DestroySubtree(): Failed to remove subtree root %s."),
  882. pcszPath));
  883. tr = TR_DEST_WRITE_FAILED;
  884. }
  885. else
  886. /* Already gone. */
  887. tr = TR_SUCCESS;
  888. }
  889. }
  890. }
  891. else
  892. tr = TR_UNAVAILABLE_VOLUME;
  893. return(tr);
  894. }
  895. #ifdef DEBUG
  896. /*
  897. ** IsValidRecStatusProcMsg()
  898. **
  899. **
  900. **
  901. ** Arguments:
  902. **
  903. ** Returns:
  904. **
  905. ** Side Effects: none
  906. */
  907. PUBLIC_CODE BOOL IsValidRecStatusProcMsg(UINT uMsg)
  908. {
  909. BOOL bResult;
  910. switch (uMsg)
  911. {
  912. case RS_BEGIN_COPY:
  913. case RS_DELTA_COPY:
  914. case RS_END_COPY:
  915. case RS_BEGIN_MERGE:
  916. case RS_DELTA_MERGE:
  917. case RS_END_MERGE:
  918. case RS_BEGIN_DELETE:
  919. case RS_DELTA_DELETE:
  920. case RS_END_DELETE:
  921. bResult = TRUE;
  922. break;
  923. default:
  924. bResult = FALSE;
  925. ERROR_OUT((TEXT("IsValidRecStatusProcMsg(): Invalid RecStatusProc() message %u."),
  926. uMsg));
  927. break;
  928. }
  929. return(bResult);
  930. }
  931. #endif