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.

555 lines
16 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. copyfile.c
  5. Abstract:
  6. File copy functions
  7. The code in this source file traverses a drive tree and calls
  8. an external callback function for each file. An INF can be
  9. provided to exclude files and/or directories from enumeration.
  10. Author:
  11. Mike Condra 16-Aug-1996
  12. Revision History:
  13. calinn 29-Ian-1998 Modified CopyFileCallback to reset directory attributes for delete op.
  14. jimschm 20-Dec-1996 Modified return codes
  15. --*/
  16. #include "no_pch.h"
  17. #include "fileenum.h"
  18. #include <winnls.h>
  19. #ifndef UNICODE
  20. #ifdef DEBUG
  21. #error UNICODE required for DEBUGMSG macro
  22. #endif
  23. #endif
  24. BOOL
  25. CALLBACK
  26. CopyFileCallbackA(
  27. LPCSTR szFullFileSpecIn,
  28. LPCSTR DontCare,
  29. WIN32_FIND_DATAA *pFindData,
  30. DWORD dwEnumHandle,
  31. LPVOID pVoid,
  32. PDWORD CurrentDirData
  33. )
  34. /*
  35. This function is the built-in callback for CopyTree. Its purpose is to
  36. build the target filespec, give the user-supplied callback a chance to
  37. veto the copy, then perform the copy and any directory creation it
  38. requires. The signature of this function is the generic callback
  39. used for EnumerateTree.
  40. */
  41. {
  42. COPYTREE_PARAMSA *pCopyParams = (COPYTREE_PARAMSA*)pVoid;
  43. int nCharsInFullFileSpec = ByteCountA (szFullFileSpecIn);
  44. INT rc;
  45. // Set return code
  46. if (COPYTREE_IGNORE_ERRORS & pCopyParams->flags)
  47. rc = CALLBACK_CONTINUE;
  48. else
  49. rc = CALLBACK_FAILED;
  50. // Build output path
  51. if (pCopyParams->szEnumRootOutWack)
  52. {
  53. StringCopyA(pCopyParams->szFullFileSpecOut, pCopyParams->szEnumRootOutWack);
  54. StringCatA (pCopyParams->szFullFileSpecOut, szFullFileSpecIn + pCopyParams->nCharsInRootInWack);
  55. }
  56. //
  57. // If a callback was supplied, give it a chance to veto the copy. This callback is
  58. // different from the one given to an EnumerateTree function, since the latter can
  59. // terminate enumeration by returning FALSE.
  60. //
  61. if (pCopyParams->pfnCallback)
  62. {
  63. if (!pCopyParams->pfnCallback(
  64. szFullFileSpecIn,
  65. pCopyParams->szFullFileSpecOut,
  66. pFindData,
  67. dwEnumHandle,
  68. pVoid,
  69. CurrentDirData
  70. ))
  71. {
  72. return CALLBACK_CONTINUE;
  73. }
  74. }
  75. // Copy, move or delete the file if requested
  76. if ((COPYTREE_DOCOPY & pCopyParams->flags) ||
  77. (COPYTREE_DOMOVE & pCopyParams->flags))
  78. {
  79. BOOL fNoOverwrite = (0 != (COPYTREE_NOOVERWRITE & pCopyParams->flags));
  80. //
  81. // Create the directory. The function we call expects a full filename,
  82. // and considers the directory to end at the last wack. If this object
  83. // is a directory, we need to add at least a wack to make sure the last
  84. // path element is treated as part of the directory, not as a filename.
  85. //
  86. {
  87. CHAR strTemp[MAX_MBCHAR_PATH];
  88. StringCopyA(strTemp, pCopyParams->szFullFileSpecOut);
  89. if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  90. {
  91. AppendUncWackA(strTemp);
  92. }
  93. if (ERROR_SUCCESS != MakeSurePathExistsA(strTemp,FALSE))
  94. {
  95. return rc;
  96. }
  97. }
  98. //
  99. // Copy or move the file
  100. //
  101. if (0 == (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  102. {
  103. if (COPYTREE_DOCOPY & pCopyParams->flags)
  104. {
  105. if (!CopyFileA(
  106. szFullFileSpecIn,
  107. pCopyParams->szFullFileSpecOut,
  108. fNoOverwrite
  109. ))
  110. {
  111. if (!fNoOverwrite)
  112. {
  113. return rc;
  114. }
  115. if (ERROR_FILE_EXISTS != GetLastError())
  116. {
  117. return rc;
  118. }
  119. }
  120. }
  121. else if (COPYTREE_DOMOVE & pCopyParams->flags)
  122. {
  123. // If allowed to overwrite, delete the target if it exists
  124. if (!fNoOverwrite && DoesFileExistA(pCopyParams->szFullFileSpecOut))
  125. {
  126. SetFileAttributesA (pCopyParams->szFullFileSpecOut, FILE_ATTRIBUTE_NORMAL);
  127. if (!DeleteFileA(pCopyParams->szFullFileSpecOut))
  128. {
  129. return rc;
  130. }
  131. }
  132. // Move the file
  133. if (!MoveFileA(
  134. szFullFileSpecIn,
  135. pCopyParams->szFullFileSpecOut
  136. ))
  137. {
  138. return rc;
  139. }
  140. }
  141. }
  142. //
  143. // Copy the source file-or-directory's attributes to the target
  144. //
  145. SetFileAttributesA(pCopyParams->szFullFileSpecOut,
  146. pFindData->dwFileAttributes);
  147. }
  148. else if (COPYTREE_DODELETE & pCopyParams->flags) {
  149. SetFileAttributesA (szFullFileSpecIn, FILE_ATTRIBUTE_NORMAL);
  150. if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  151. //
  152. // We don't care about the error. We won't stop the enumeration just
  153. // because we could not delete something.
  154. //
  155. RemoveDirectoryA (szFullFileSpecIn);
  156. }
  157. else {
  158. //
  159. // We don't care about the error. We won't stop the enumeration just
  160. // because we could not delete something.
  161. //
  162. DeleteFileA (szFullFileSpecIn);
  163. }
  164. }
  165. return CALLBACK_CONTINUE;
  166. }
  167. BOOL
  168. CALLBACK
  169. CopyFileCallbackW(
  170. LPCWSTR szFullFileSpecIn,
  171. LPCWSTR DontCare,
  172. WIN32_FIND_DATAW *pFindData,
  173. DWORD dwEnumHandle,
  174. LPVOID pVoid,
  175. PDWORD CurrentDirData
  176. )
  177. {
  178. COPYTREE_PARAMSW *pCopyParams = (COPYTREE_PARAMSW*)pVoid;
  179. int nCharsInFullFileSpec = wcslen (szFullFileSpecIn);
  180. INT rc;
  181. // Set return code
  182. if (COPYTREE_IGNORE_ERRORS & pCopyParams->flags)
  183. rc = CALLBACK_CONTINUE;
  184. else
  185. rc = CALLBACK_FAILED;
  186. // Build output path
  187. if (pCopyParams->szEnumRootOutWack)
  188. {
  189. StringCopyW (pCopyParams->szFullFileSpecOut, pCopyParams->szEnumRootOutWack);
  190. StringCatW (pCopyParams->szFullFileSpecOut, szFullFileSpecIn + pCopyParams->nCharsInRootInWack);
  191. }
  192. //
  193. // If a callback was supplied, give it a chance to veto the copy. This callback is
  194. // different from the one given to an EnumerateTree function, since the latter can
  195. // terminate enumeration by returning FALSE.
  196. //
  197. if (pCopyParams->pfnCallback)
  198. {
  199. if (!pCopyParams->pfnCallback(
  200. szFullFileSpecIn,
  201. pCopyParams->szFullFileSpecOut,
  202. pFindData,
  203. dwEnumHandle,
  204. pVoid,
  205. CurrentDirData
  206. ))
  207. {
  208. return CALLBACK_CONTINUE;
  209. }
  210. }
  211. // Copy or move the file if requested
  212. if ((COPYTREE_DOCOPY & pCopyParams->flags) ||
  213. (COPYTREE_DOMOVE & pCopyParams->flags))
  214. {
  215. BOOL fNoOverwrite = (0 != (COPYTREE_NOOVERWRITE & pCopyParams->flags));
  216. //
  217. // Create the directory. The function we call expects a full filename,
  218. // and considers the directory to end at the last wack. If this object
  219. // is a directory, we need to add at least a wack to make sure the last
  220. // path element is treated as part of the directory, not as a filename.
  221. //
  222. {
  223. WCHAR strTemp[MAX_WCHAR_PATH];
  224. StringCopyW(strTemp, pCopyParams->szFullFileSpecOut);
  225. if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  226. {
  227. AppendUncWackW(strTemp);
  228. }
  229. if (ERROR_SUCCESS != MakeSurePathExistsW(strTemp,FALSE))
  230. {
  231. return rc;
  232. }
  233. }
  234. //
  235. // Copy or move the file
  236. //
  237. if (0 == (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  238. {
  239. if (COPYTREE_DOCOPY & pCopyParams->flags)
  240. {
  241. DEBUGMSG ((DBG_NAUSEA, "Copying %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut));
  242. if (!CopyFileW(
  243. szFullFileSpecIn,
  244. pCopyParams->szFullFileSpecOut,
  245. fNoOverwrite
  246. ))
  247. {
  248. if (!fNoOverwrite)
  249. {
  250. LOG ((LOG_ERROR, "CopyFileW failed. Could not copy %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut));
  251. return rc;
  252. }
  253. if (ERROR_FILE_EXISTS != GetLastError())
  254. {
  255. LOG ((LOG_ERROR, "CopyFileW failed. Could not copy %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut));
  256. return rc;
  257. }
  258. }
  259. }
  260. else if (COPYTREE_DOMOVE & pCopyParams->flags)
  261. {
  262. // If allowed to overwrite, delete the target if it exists
  263. if (!fNoOverwrite && DoesFileExistW(pCopyParams->szFullFileSpecOut))
  264. {
  265. SetFileAttributesW (pCopyParams->szFullFileSpecOut, FILE_ATTRIBUTE_NORMAL);
  266. if (!DeleteFileW(pCopyParams->szFullFileSpecOut))
  267. {
  268. LOG ((LOG_ERROR, "DeleteFileW failed. Could remove %s before moving", pCopyParams->szFullFileSpecOut));
  269. return rc;
  270. }
  271. }
  272. // Move the file
  273. if (!MoveFileW(
  274. szFullFileSpecIn,
  275. pCopyParams->szFullFileSpecOut
  276. ))
  277. {
  278. LOG ((LOG_ERROR, "MoveFileW failed. Could not move %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut));
  279. return rc;
  280. }
  281. }
  282. }
  283. //
  284. // Copy the source file-or-directory's attributes to the target
  285. //
  286. SetFileAttributesW(pCopyParams->szFullFileSpecOut,
  287. pFindData->dwFileAttributes);
  288. }
  289. else if (COPYTREE_DODELETE & pCopyParams->flags) {
  290. SetFileAttributesW (szFullFileSpecIn, FILE_ATTRIBUTE_NORMAL);
  291. if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  292. DEBUGMSG ((DBG_NAUSEA, "Delete dir %ls", szFullFileSpecIn));
  293. //
  294. // We don't care about the error. We won't stop the enumeration just
  295. // because we could not delete something.
  296. //
  297. RemoveDirectoryW (szFullFileSpecIn);
  298. }
  299. else {
  300. DEBUGMSG ((DBG_NAUSEA, "Delete file %ls", szFullFileSpecIn));
  301. //
  302. // We don't care about the error. We won't stop the enumeration just
  303. // because we could not delete something.
  304. //
  305. DeleteFileW (szFullFileSpecIn);
  306. }
  307. }
  308. return CALLBACK_CONTINUE;
  309. }
  310. BOOL
  311. CopyTreeA(
  312. IN LPCSTR szEnumRootIn,
  313. IN LPCSTR szEnumRootOut,
  314. IN DWORD dwEnumHandle,
  315. IN DWORD flags,
  316. IN DWORD Levels,
  317. IN DWORD AttributeFilter,
  318. IN PEXCLUDEINFA ExcludeInfStruct, OPTIONAL
  319. IN FILEENUMPROCA pfnCallback, OPTIONAL
  320. IN FILEENUMFAILPROCA pfnFailCallback OPTIONAL
  321. )
  322. /*
  323. This function enumerates a subtree of a disk drive, and optionally
  324. copies it to another location. No check is made to ensure the target
  325. is not contained within the source tree -- this condition could lead
  326. to unpredictable results.
  327. The parameters are a superset of those for EnumerateTree. The caller-
  328. supplied optional callback function can veto the copying of individual
  329. files, but cannot (as of 9/10) end the enumeration.
  330. Directories will be created as necessary to complete the copy.
  331. */
  332. {
  333. COPYTREE_PARAMSA copyParams;
  334. CHAR szEnumRootInWack[MAX_MBCHAR_PATH];
  335. CHAR szEnumRootOutWack[MAX_MBCHAR_PATH];
  336. //
  337. // Build wacked copies of paths for use in parameter block.
  338. //
  339. //
  340. // Input path
  341. //
  342. StringCopyA(szEnumRootInWack, szEnumRootIn);
  343. AppendUncWackA(szEnumRootInWack);
  344. copyParams.szEnumRootInWack = szEnumRootInWack;
  345. copyParams.nCharsInRootInWack = ByteCountA(szEnumRootInWack);
  346. //
  347. // If output path is NULL, store 0 length and a NULL ptr in param block.
  348. //
  349. if (NULL != szEnumRootOut)
  350. {
  351. StringCopyA(szEnumRootOutWack, szEnumRootOut);
  352. AppendUncWackA(szEnumRootOutWack);
  353. copyParams.szEnumRootOutWack = szEnumRootOutWack;
  354. copyParams.nCharsInRootOutWack = ByteCountA(szEnumRootOutWack);
  355. }
  356. else
  357. {
  358. copyParams.szEnumRootOutWack = NULL;
  359. copyParams.nCharsInRootOutWack = 0;
  360. }
  361. copyParams.pfnCallback = pfnCallback;
  362. copyParams.flags = flags;
  363. if ((flags & COPYTREE_DOCOPY) &&
  364. (flags & COPYTREE_DOMOVE))
  365. {
  366. return ERROR_INVALID_PARAMETER;
  367. }
  368. if (flags & COPYTREE_DODELETE) {
  369. AttributeFilter |= FILTER_DIRS_LAST;
  370. }
  371. return EnumerateTreeA(
  372. szEnumRootInWack,
  373. CopyFileCallbackA,
  374. pfnFailCallback,
  375. dwEnumHandle,
  376. (LPVOID)&copyParams,
  377. Levels,
  378. ExcludeInfStruct,
  379. AttributeFilter);
  380. }
  381. BOOL
  382. CopyTreeW(
  383. IN LPCWSTR szEnumRootIn,
  384. IN LPCWSTR szEnumRootOut,
  385. IN DWORD dwEnumHandle,
  386. IN DWORD flags,
  387. IN DWORD Levels,
  388. IN DWORD AttributeFilter,
  389. IN PEXCLUDEINFW ExcludeInfStruct, OPTIONAL
  390. IN FILEENUMPROCW pfnCallback, OPTIONAL
  391. IN FILEENUMFAILPROCW pfnFailCallback OPTIONAL
  392. )
  393. {
  394. COPYTREE_PARAMSW copyParams;
  395. WCHAR szEnumRootInWack[MAX_WCHAR_PATH];
  396. WCHAR szEnumRootOutWack[MAX_WCHAR_PATH];
  397. //
  398. // Place wacked copies of paths in parameter block.
  399. //
  400. //
  401. // Input Path
  402. //
  403. StringCopyW(szEnumRootInWack, szEnumRootIn);
  404. AppendUncWackW(szEnumRootInWack);
  405. copyParams.szEnumRootInWack = szEnumRootInWack;
  406. copyParams.nCharsInRootInWack = wcslen(szEnumRootInWack);
  407. //
  408. // If output path is NULL, put 0 length and NULL ptr in param block.
  409. //
  410. if (NULL != szEnumRootOut)
  411. {
  412. StringCopyW(szEnumRootOutWack, szEnumRootOut);
  413. AppendUncWackW(szEnumRootOutWack);
  414. copyParams.szEnumRootOutWack = szEnumRootOutWack;
  415. copyParams.nCharsInRootOutWack = wcslen(szEnumRootOutWack);
  416. }
  417. else
  418. {
  419. copyParams.szEnumRootOutWack = NULL;
  420. copyParams.nCharsInRootOutWack = 0;
  421. }
  422. copyParams.pfnCallback = pfnCallback;
  423. copyParams.flags = flags;
  424. if ((flags & COPYTREE_DOCOPY) &&
  425. (flags & COPYTREE_DOMOVE))
  426. {
  427. return ERROR_INVALID_PARAMETER;
  428. }
  429. if (flags & COPYTREE_DODELETE) {
  430. AttributeFilter |= FILTER_DIRS_LAST;
  431. }
  432. return EnumerateTreeW(
  433. szEnumRootInWack,
  434. CopyFileCallbackW,
  435. pfnFailCallback,
  436. dwEnumHandle,
  437. (LPVOID)&copyParams,
  438. Levels,
  439. ExcludeInfStruct,
  440. AttributeFilter);
  441. }
  442. DWORD
  443. CreateEmptyDirectoryA (
  444. PCSTR Dir
  445. )
  446. {
  447. DWORD rc;
  448. if (!DeleteDirectoryContentsA (Dir)) {
  449. rc = GetLastError();
  450. if (rc != ERROR_PATH_NOT_FOUND)
  451. return rc;
  452. }
  453. if (!RemoveDirectoryA (Dir)) {
  454. rc = GetLastError();
  455. if (rc != ERROR_PATH_NOT_FOUND) {
  456. return rc;
  457. }
  458. }
  459. return MakeSurePathExistsA (Dir, TRUE);
  460. }
  461. DWORD
  462. CreateEmptyDirectoryW (
  463. PCWSTR Dir
  464. )
  465. {
  466. DWORD rc;
  467. if (!DeleteDirectoryContentsW (Dir)) {
  468. rc = GetLastError();
  469. if (rc != ERROR_PATH_NOT_FOUND)
  470. return rc;
  471. }
  472. if (!RemoveDirectoryW (Dir)) {
  473. rc = GetLastError();
  474. if (rc != ERROR_PATH_NOT_FOUND) {
  475. return rc;
  476. }
  477. }
  478. return MakeSurePathExistsW (Dir, TRUE);
  479. }