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.

2801 lines
84 KiB

  1. /*
  2. Module Name:
  3. mirror.c
  4. Abstract:
  5. This module implements routines to copy up a tree to a destination.
  6. Author:
  7. Andy Herron May 27 1998
  8. Revision History:
  9. */
  10. #include "precomp.h"
  11. #pragma hdrstop
  12. #define RNDM_CONSTANT 314159269 /* default scrambling constant */
  13. #define RNDM_PRIME 1000000007 /* prime number for scrambling */
  14. //
  15. // Compute a string hash value that is invariant to case
  16. //
  17. #define COMPUTE_STRING_HASH( _pus, _phash ) { \
  18. PWCHAR _p = _pus; \
  19. ULONG _chHolder =0; \
  20. \
  21. while( *_p != L'\0' ) { \
  22. _chHolder = 37 * _chHolder + (unsigned int) *(_p++); \
  23. } \
  24. \
  25. *(_phash) = abs(RNDM_CONSTANT * _chHolder) % RNDM_PRIME; \
  26. }
  27. BOOLEAN IMirrorUpdatedTokens = FALSE;
  28. //
  29. // this is the structure we use to track what files already exist on the
  30. // destination
  31. //
  32. typedef struct _EXISTING_MIRROR_FILE {
  33. LIST_ENTRY ListEntry;
  34. DWORD NameHashValue;
  35. DWORD FileAttributes;
  36. LARGE_INTEGER CreationTime;
  37. LARGE_INTEGER LastWriteTime;
  38. LARGE_INTEGER ChangeTime;
  39. LARGE_INTEGER EndOfFile;
  40. ULONG FileNameLength;
  41. ULONG EaSize;
  42. WCHAR FileName[1];
  43. } EXISTING_MIRROR_FILE, *PEXISTING_MIRROR_FILE;
  44. //
  45. // this is the structure we use to track the directories that we still need
  46. // to copy.
  47. //
  48. typedef struct _COPY_DIRECTORY {
  49. LIST_ENTRY ListEntry;
  50. PCOPY_TREE_CONTEXT CopyContext;
  51. BOOLEAN DirectoryRoot; // is this the root of the volume?
  52. DWORD SourceAttributes;
  53. PWCHAR Source;
  54. PWCHAR Dest;
  55. PWCHAR NtSourceName;
  56. PWCHAR NtDestName;
  57. WCHAR SourceBuffer[1];
  58. } COPY_DIRECTORY, *PCOPY_DIRECTORY;
  59. DWORD
  60. CreateMatchingDirectory (
  61. PIMIRROR_THREAD_CONTEXT ThreadContext,
  62. PCOPY_DIRECTORY DirectoryInfo
  63. );
  64. DWORD
  65. MirrorFile(
  66. PIMIRROR_THREAD_CONTEXT ThreadContext,
  67. PWCHAR SourceFileName,
  68. PFILE_FULL_DIR_INFORMATION SourceFindData,
  69. PWCHAR DestFileName,
  70. PEXISTING_MIRROR_FILE ExistingMirrorFile
  71. );
  72. DWORD
  73. UnconditionalDelete (
  74. PIMIRROR_THREAD_CONTEXT ThreadContext,
  75. PWCHAR SourceFile,
  76. PWCHAR FileToDelete,
  77. DWORD Attributes,
  78. PWCHAR NameBuffer
  79. );
  80. DWORD
  81. StoreOurSecurityStream (
  82. PIMIRROR_THREAD_CONTEXT ThreadContext,
  83. PWCHAR Source,
  84. PWCHAR Dest,
  85. DWORD AttributesToStore,
  86. LARGE_INTEGER ChangeTime
  87. );
  88. DWORD
  89. StoreOurSFNStream (
  90. PIMIRROR_THREAD_CONTEXT ThreadContext,
  91. PWCHAR Source,
  92. PWCHAR Dest,
  93. PWCHAR ShortFileName
  94. );
  95. DWORD
  96. GetOurSecurityStream (
  97. PIMIRROR_THREAD_CONTEXT ThreadContext,
  98. PWCHAR Dest,
  99. PMIRROR_ACL_STREAM MirrorAclStream
  100. );
  101. DWORD
  102. GetOurSFNStream (
  103. PIMIRROR_THREAD_CONTEXT ThreadContext,
  104. PWCHAR Dest,
  105. PMIRROR_SFN_STREAM MirrorSFNStream,
  106. PWCHAR SFNBuffer,
  107. DWORD SFNBufferSize
  108. );
  109. DWORD
  110. CopySubtree(
  111. PIMIRROR_THREAD_CONTEXT ThreadContext,
  112. PCOPY_DIRECTORY DirectoryInfo
  113. );
  114. BOOL
  115. IMSetFileTime(
  116. HANDLE hFile,
  117. CONST FILETIME *lpCreationTime,
  118. CONST FILETIME *lpLastAccessTime,
  119. CONST FILETIME *lpLastWriteTime,
  120. CONST FILETIME *lpChangeTime
  121. );
  122. DWORD
  123. IMirrorOpenDirectory (
  124. HANDLE *Handle,
  125. PWCHAR NtDirName,
  126. DWORD Disposition,
  127. BOOLEAN IsSource,
  128. DWORD SourceAttributes,
  129. PFILE_BASIC_INFORMATION BasicDirInfo OPTIONAL
  130. );
  131. NTSTATUS
  132. CanHandleReparsePoint (
  133. PIMIRROR_THREAD_CONTEXT ThreadContext,
  134. PWCHAR SourceFileName,
  135. DWORD FileAttributes
  136. );
  137. DWORD
  138. AllocateCopyTreeContext (
  139. PCOPY_TREE_CONTEXT *CopyContext,
  140. BOOLEAN DeleteOtherFiles
  141. )
  142. /*++
  143. Description:
  144. This routine allocates the necessary structure that we pass around
  145. that contains all of our "global" data during copying a large tree.
  146. Parameters:
  147. CopyContext : Location to put allocated structure.
  148. DeleteOtherFiles : Do we remove files and dirs that aren't on the master?
  149. Return Value:
  150. Win32 error code
  151. ++*/
  152. {
  153. PCOPY_TREE_CONTEXT context;
  154. *CopyContext = IMirrorAllocMem(sizeof( COPY_TREE_CONTEXT ));
  155. context = *CopyContext;
  156. if (context == NULL) {
  157. return GetLastError();
  158. }
  159. InitializeListHead( &(context->PendingDirectoryList) );
  160. InitializeCriticalSection( &(context->Lock) );
  161. context->Cancelled = FALSE;
  162. context->DeleteOtherFiles = DeleteOtherFiles;
  163. return ERROR_SUCCESS;
  164. }
  165. VOID
  166. FreeCopyTreeContext (
  167. PCOPY_TREE_CONTEXT CopyContext
  168. )
  169. /*++
  170. Description:
  171. This routine frees the necessary structures that we pass around
  172. that contains all of our "global" data during copying a large tree.
  173. Parameters:
  174. CopyContext : Structure that is no longer needed.
  175. Return Value:
  176. None
  177. ++*/
  178. {
  179. while (IsListEmpty( &(CopyContext->PendingDirectoryList) ) == FALSE) {
  180. PCOPY_DIRECTORY copyDir;
  181. PLIST_ENTRY listEntry = RemoveHeadList( &(CopyContext->PendingDirectoryList) );
  182. copyDir = (PCOPY_DIRECTORY) CONTAINING_RECORD( listEntry,
  183. COPY_DIRECTORY,
  184. ListEntry );
  185. IMirrorFreeMem( copyDir );
  186. }
  187. DeleteCriticalSection( &CopyContext->Lock );
  188. return;
  189. }
  190. DWORD
  191. CopyTree (
  192. PCOPY_TREE_CONTEXT CopyContext,
  193. BOOLEAN IsNtfs,
  194. PWCHAR SourceRoot,
  195. PWCHAR DestRoot
  196. )
  197. /*++
  198. Description:
  199. This is the main routine that initiates the full subtree copy.
  200. Parameters:
  201. CopyContext : Structure that is no longer needed.
  202. SourceRoot : source tree to copy in NT format, not DOS format.
  203. DestRoot : location to copy it to
  204. Return Value:
  205. Win32 error code
  206. ++*/
  207. {
  208. DWORD err;
  209. DWORD sourceAttributes;
  210. IMIRROR_THREAD_CONTEXT threadContext;
  211. COPY_DIRECTORY rootDir;
  212. //
  213. // if we were to create multiple threads handling copying this subtree,
  214. // this is where we'll setup the threads where each has their own
  215. // thread context.
  216. //
  217. if (! IMirrorUpdatedTokens) {
  218. HANDLE hToken;
  219. // Enable the privileges necessary to copy security information.
  220. err = GetTokenHandle(&hToken);
  221. if (err == ERROR_SUCCESS) {
  222. SetPrivs(hToken, TEXT("SeSecurityPrivilege"));
  223. IMirrorUpdatedTokens = TRUE;
  224. }
  225. }
  226. retryCopyTree:
  227. RtlZeroMemory( &threadContext, sizeof( threadContext ));
  228. threadContext.CopyContext = CopyContext;
  229. threadContext.IsNTFS = IsNtfs;
  230. threadContext.SourceDirHandle = INVALID_HANDLE_VALUE;
  231. threadContext.DestDirHandle = INVALID_HANDLE_VALUE;
  232. threadContext.SDBufferLength = IMIRROR_INITIAL_SD_LENGTH;
  233. threadContext.SFNBufferLength = IMIRROR_INITIAL_SFN_LENGTH;
  234. InitializeListHead( &threadContext.FilesToIgnore );
  235. sourceAttributes = GetFileAttributes( SourceRoot );
  236. if (sourceAttributes == (DWORD) -1) {
  237. ULONG action;
  238. err = GetLastError();
  239. action = ReportCopyError( CopyContext,
  240. SourceRoot,
  241. COPY_ERROR_ACTION_GETATTR,
  242. err );
  243. if (action == STATUS_RETRY) {
  244. goto retryCopyTree;
  245. } else if (action == STATUS_REQUEST_ABORTED) {
  246. goto exitCopyTree;
  247. }
  248. //
  249. // the user told us to ignore the error. we'll do our best.
  250. //
  251. sourceAttributes = FILE_ATTRIBUTE_DIRECTORY;
  252. }
  253. err = GetRegistryFileList( &threadContext.FilesToIgnore );
  254. if( err != NO_ERROR ) {
  255. goto exitCopyTree;
  256. }
  257. RtlZeroMemory( &rootDir, sizeof( COPY_DIRECTORY ));
  258. rootDir.CopyContext = CopyContext;
  259. rootDir.DirectoryRoot = TRUE;
  260. rootDir.SourceAttributes = sourceAttributes;
  261. rootDir.Source = SourceRoot;
  262. rootDir.Dest = DestRoot;
  263. err = CopySubtree( &threadContext,
  264. &rootDir
  265. );
  266. ASSERT( threadContext.SourceDirHandle == INVALID_HANDLE_VALUE );
  267. ASSERT( threadContext.DestDirHandle == INVALID_HANDLE_VALUE );
  268. EnterCriticalSection( &CopyContext->Lock );
  269. while ((CopyContext->Cancelled == FALSE) &&
  270. (IsListEmpty( &(CopyContext->PendingDirectoryList) ) == FALSE)) {
  271. PCOPY_DIRECTORY copyDir;
  272. PLIST_ENTRY listEntry = RemoveHeadList( &(CopyContext->PendingDirectoryList) );
  273. copyDir = (PCOPY_DIRECTORY) CONTAINING_RECORD( listEntry,
  274. COPY_DIRECTORY,
  275. ListEntry );
  276. LeaveCriticalSection( &CopyContext->Lock );
  277. err = CopySubtree( &threadContext,
  278. copyDir
  279. );
  280. ASSERT( threadContext.SourceDirHandle == INVALID_HANDLE_VALUE );
  281. ASSERT( threadContext.DestDirHandle == INVALID_HANDLE_VALUE );
  282. IMirrorFreeMem( copyDir );
  283. EnterCriticalSection( &CopyContext->Lock );
  284. }
  285. exitCopyTree:
  286. if (threadContext.SDBuffer) {
  287. IMirrorFreeMem( threadContext.SDBuffer );
  288. }
  289. if (threadContext.SFNBuffer) {
  290. IMirrorFreeMem( threadContext.SFNBuffer );
  291. }
  292. if (threadContext.DirectoryBuffer) {
  293. IMirrorFreeMem( threadContext.DirectoryBuffer );
  294. }
  295. if ( threadContext.FindBufferBase ) {
  296. IMirrorFreeMem( threadContext.FindBufferBase );
  297. }
  298. while (IsListEmpty( &(threadContext.FilesToIgnore) ) == FALSE) {
  299. PIMIRROR_IGNORE_FILE_LIST ignoreListEntry;
  300. PLIST_ENTRY listEntry = RemoveHeadList( &(threadContext.FilesToIgnore) );
  301. ignoreListEntry = (PIMIRROR_IGNORE_FILE_LIST)
  302. CONTAINING_RECORD( listEntry,
  303. IMIRROR_IGNORE_FILE_LIST,
  304. ListEntry );
  305. IMirrorFreeMem( ignoreListEntry );
  306. }
  307. return err;
  308. }
  309. DWORD
  310. CopySubtree(
  311. PIMIRROR_THREAD_CONTEXT ThreadContext,
  312. PCOPY_DIRECTORY DirectoryInfo
  313. )
  314. /*++
  315. Description:
  316. This routine enumerates the directories on the client to continue
  317. traversing the tree. It then enumerates the files on the slave
  318. ( to compare them against what is on the master ), it then ensures
  319. that all files on the master are up on the slave. It then deletes all
  320. files on the slave that are not on the master.
  321. Parameters:
  322. ThreadContext : data for this instance of copying a tree
  323. DirectoryInfo : information on the source and dest that we know of
  324. Return Value:
  325. Win32 error code
  326. ++*/
  327. {
  328. DWORD err;
  329. PWCHAR destFileName;
  330. PWCHAR sourceFileName;
  331. ULONG destFileNameSize;
  332. ULONG sourceFileNameSize;
  333. PWCHAR endOfSourcePath;
  334. PWCHAR endOfDestPath = NULL;
  335. LIST_ENTRY existingMirrorFilesList;
  336. PLIST_ENTRY listEntry;
  337. PEXISTING_MIRROR_FILE existingMirrorFile;
  338. BOOLEAN deleteExistingMirrorFilesNotInMaster;
  339. PCOPY_TREE_CONTEXT copyContext;
  340. UNICODE_STRING ntSourcePath;
  341. UNICODE_STRING ntDestPath;
  342. PFILE_FULL_DIR_INFORMATION findData;
  343. ULONG errorCase;
  344. retryCopySubtree:
  345. errorCase = ERROR_SUCCESS;
  346. destFileName = NULL;
  347. sourceFileName = NULL;
  348. deleteExistingMirrorFilesNotInMaster = FALSE;
  349. copyContext = ThreadContext->CopyContext;
  350. InitializeListHead( &existingMirrorFilesList );
  351. RtlInitUnicodeString( &ntSourcePath, NULL );
  352. RtlInitUnicodeString( &ntDestPath, NULL );
  353. //
  354. // since some of the NT specific calls use the NT format of the name,
  355. // we grab that up front so as not to have to do it every time.
  356. //
  357. if (RtlDosPathNameToNtPathName_U( DirectoryInfo->Source,
  358. &ntSourcePath,
  359. NULL,
  360. NULL ) == FALSE) {
  361. //err = STATUS_OBJECT_PATH_NOT_FOUND;
  362. err = ERROR_PATH_NOT_FOUND;
  363. errorCase = ReportCopyError( copyContext,
  364. DirectoryInfo->Source,
  365. COPY_ERROR_ACTION_OPEN_DIR,
  366. err );
  367. goto exitCopySubtree;
  368. }
  369. if (RtlDosPathNameToNtPathName_U( DirectoryInfo->Dest,
  370. &ntDestPath,
  371. NULL,
  372. NULL ) == FALSE) {
  373. //err = STATUS_OBJECT_PATH_NOT_FOUND;
  374. err = ERROR_PATH_NOT_FOUND;
  375. errorCase = ReportCopyError( copyContext,
  376. DirectoryInfo->Dest,
  377. COPY_ERROR_ACTION_OPEN_DIR,
  378. err );
  379. goto exitCopySubtree;
  380. }
  381. DirectoryInfo->NtSourceName = ntSourcePath.Buffer;
  382. DirectoryInfo->NtDestName = ntDestPath.Buffer;
  383. //
  384. // Create a directory on the slave that matches this one. This will
  385. // open handles to both the source and dest directories. We cache the
  386. // handle in case other operations need it.
  387. //
  388. err = CreateMatchingDirectory( ThreadContext, DirectoryInfo );
  389. if (err != ERROR_SUCCESS) {
  390. goto exitCopySubtree;
  391. }
  392. destFileNameSize = (lstrlenW( DirectoryInfo->Dest ) + 5 + MAX_PATH) * sizeof(WCHAR);
  393. destFileName = IMirrorAllocMem( destFileNameSize );
  394. if (destFileName == NULL) {
  395. err = GetLastError();
  396. errorCase = ReportCopyError( copyContext,
  397. DirectoryInfo->Dest,
  398. COPY_ERROR_ACTION_MALLOC,
  399. err );
  400. goto exitCopySubtree;
  401. }
  402. lstrcpyW( destFileName, DirectoryInfo->Dest );
  403. lstrcatW( destFileName, L"\\" );
  404. // track the next character after the trailing backslash
  405. endOfDestPath = destFileName + lstrlenW( destFileName );
  406. sourceFileNameSize = (lstrlenW( DirectoryInfo->Source ) + 5 + MAX_PATH) * sizeof(WCHAR);
  407. sourceFileName = IMirrorAllocMem( sourceFileNameSize );
  408. if (sourceFileName == NULL) {
  409. err = GetLastError();
  410. errorCase = ReportCopyError( copyContext,
  411. DirectoryInfo->Source,
  412. COPY_ERROR_ACTION_MALLOC,
  413. err );
  414. goto exitCopySubtree;
  415. }
  416. lstrcpyW( sourceFileName, DirectoryInfo->Source );
  417. if (!DirectoryInfo->DirectoryRoot) {
  418. lstrcatW( sourceFileName, L"\\" );
  419. }
  420. // track the next character after the trailing backslash
  421. endOfSourcePath = sourceFileName + lstrlenW( sourceFileName );
  422. //
  423. // enumerate all files/directories on the target so that we have the
  424. // details to grovel correctly.
  425. //
  426. err = IMFindFirstFile( ThreadContext,
  427. ThreadContext->DestDirHandle,
  428. &findData );
  429. while ( findData != NULL &&
  430. err == ERROR_SUCCESS &&
  431. copyContext->Cancelled == FALSE) {
  432. InterlockedIncrement( (PLONG) &copyContext->DestFilesScanned ); // this is really a ULONG
  433. if (((findData->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
  434. (findData->FileName[0] == L'.')) {
  435. if ((findData->FileNameLength == sizeof(WCHAR)) ||
  436. (findData->FileName[1] == L'.' &&
  437. findData->FileNameLength == 2*sizeof(WCHAR))) {
  438. goto skipToNextDir1;
  439. }
  440. }
  441. if (DirectoryInfo->DirectoryRoot &&
  442. ((findData->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
  443. ((!_wcsnicmp(&findData->FileName[0],
  444. L"pagefile.sys",
  445. findData->FileNameLength)) ||
  446. (!_wcsnicmp(&findData->FileName[0],
  447. L"hiberfil.sys",
  448. findData->FileNameLength)))) {
  449. goto skipToNextDir1;
  450. }
  451. existingMirrorFile = (PEXISTING_MIRROR_FILE) IMirrorAllocMem(
  452. sizeof(EXISTING_MIRROR_FILE) +
  453. findData->FileNameLength);
  454. if (existingMirrorFile == NULL) {
  455. err = GetLastError();
  456. errorCase = ReportCopyError( copyContext,
  457. destFileName,
  458. COPY_ERROR_ACTION_MALLOC,
  459. err );
  460. goto exitCopySubtree;
  461. }
  462. existingMirrorFile->FileAttributes = findData->FileAttributes;
  463. existingMirrorFile->CreationTime = findData->CreationTime;
  464. existingMirrorFile->LastWriteTime = findData->LastWriteTime;
  465. existingMirrorFile->ChangeTime = findData->ChangeTime;
  466. existingMirrorFile->EndOfFile = findData->EndOfFile;
  467. existingMirrorFile->EaSize = findData->EaSize;
  468. existingMirrorFile->FileNameLength = findData->FileNameLength;
  469. RtlCopyMemory( &existingMirrorFile->FileName[0],
  470. &findData->FileName[0],
  471. findData->FileNameLength );
  472. existingMirrorFile->FileName[ findData->FileNameLength / sizeof(WCHAR) ] = UNICODE_NULL;
  473. COMPUTE_STRING_HASH( &existingMirrorFile->FileName[0],
  474. &existingMirrorFile->NameHashValue );
  475. InsertTailList( &existingMirrorFilesList, &existingMirrorFile->ListEntry );
  476. skipToNextDir1:
  477. err = IMFindNextFile( ThreadContext,
  478. ThreadContext->DestDirHandle,
  479. &findData );
  480. }
  481. //
  482. // copy all files up from the source to the dest
  483. //
  484. err = IMFindFirstFile( ThreadContext,
  485. ThreadContext->SourceDirHandle,
  486. &findData );
  487. if (err != ERROR_SUCCESS) {
  488. if (err == STATUS_NO_MORE_FILES) {
  489. err = ERROR_SUCCESS;
  490. } else {
  491. errorCase = ReportCopyError( copyContext,
  492. DirectoryInfo->Source,
  493. COPY_ERROR_ACTION_ENUMERATE,
  494. err );
  495. goto exitCopySubtree;
  496. }
  497. }
  498. while ( findData != NULL &&
  499. err == ERROR_SUCCESS &&
  500. copyContext->Cancelled == FALSE) {
  501. DWORD nameHashValue;
  502. if (((findData->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
  503. (findData->FileName[0] == L'.')) {
  504. if ((findData->FileNameLength == sizeof(WCHAR)) ||
  505. (findData->FileName[1] == L'.' &&
  506. findData->FileNameLength == 2*sizeof(WCHAR))) {
  507. goto skipToNextDir;
  508. }
  509. }
  510. if (DirectoryInfo->DirectoryRoot &&
  511. ((findData->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
  512. ((!_wcsnicmp(&findData->FileName[0],
  513. L"pagefile.sys",
  514. findData->FileNameLength)) ||
  515. (!_wcsnicmp(&findData->FileName[0],
  516. L"hiberfil.sys",
  517. findData->FileNameLength)))) {
  518. goto skipToNextDir;
  519. }
  520. InterlockedIncrement( (PLONG) &copyContext->SourceFilesScanned ); // this is really a ULONG
  521. RtlCopyMemory( endOfSourcePath,
  522. findData->FileName,
  523. findData->FileNameLength );
  524. *(endOfSourcePath+(findData->FileNameLength/sizeof(WCHAR))) = UNICODE_NULL;
  525. RtlCopyMemory( endOfDestPath,
  526. findData->FileName,
  527. findData->FileNameLength );
  528. *(endOfDestPath+(findData->FileNameLength/sizeof(WCHAR))) = UNICODE_NULL;
  529. //
  530. // search the list of existing files on the target to see if
  531. // it's already there.
  532. //
  533. COMPUTE_STRING_HASH( endOfDestPath, &nameHashValue );
  534. listEntry = existingMirrorFilesList.Flink;
  535. existingMirrorFile = NULL;
  536. while (listEntry != &existingMirrorFilesList) {
  537. existingMirrorFile = (PEXISTING_MIRROR_FILE) CONTAINING_RECORD(
  538. listEntry,
  539. EXISTING_MIRROR_FILE,
  540. ListEntry );
  541. listEntry = listEntry->Flink;
  542. if ((existingMirrorFile->NameHashValue == nameHashValue) &&
  543. (existingMirrorFile->FileNameLength == findData->FileNameLength) &&
  544. (CompareStringW( LOCALE_SYSTEM_DEFAULT,
  545. NORM_IGNORECASE,
  546. endOfDestPath,
  547. findData->FileNameLength / sizeof(WCHAR),
  548. &existingMirrorFile->FileName[0],
  549. existingMirrorFile->FileNameLength / sizeof(WCHAR)) == 2)) {
  550. break;
  551. }
  552. existingMirrorFile = NULL;
  553. }
  554. if ((findData->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  555. //
  556. // this is a file, let's mirror it up from the master.
  557. //
  558. (VOID)MirrorFile( ThreadContext,
  559. sourceFileName,
  560. findData,
  561. destFileName,
  562. existingMirrorFile
  563. );
  564. } else {
  565. //
  566. // it's a directory, put it on the pending list.
  567. //
  568. PCOPY_DIRECTORY copyDir;
  569. ULONG sourceLength;
  570. sourceLength = lstrlenW( sourceFileName ) + 1; // space for null
  571. copyDir = (PCOPY_DIRECTORY) IMirrorAllocMem(
  572. sizeof( COPY_DIRECTORY ) +
  573. (( sourceLength +
  574. lstrlenW( destFileName ) + 1 )
  575. * sizeof(WCHAR)));
  576. if (copyDir == NULL) {
  577. err = GetLastError();
  578. errorCase = ReportCopyError( copyContext,
  579. sourceFileName,
  580. COPY_ERROR_ACTION_MALLOC,
  581. err );
  582. goto exitCopySubtree;
  583. }
  584. //
  585. // we save off all info we know about both the source and the
  586. // dest so that we don't have to go read it again.
  587. //
  588. copyDir->CopyContext = copyContext;
  589. copyDir->DirectoryRoot = FALSE;
  590. copyDir->SourceAttributes = findData->FileAttributes;
  591. copyDir->Source = &copyDir->SourceBuffer[0];
  592. lstrcpyW( copyDir->Source, sourceFileName );
  593. copyDir->Dest = &copyDir->SourceBuffer[sourceLength];
  594. lstrcpyW( copyDir->Dest, destFileName );
  595. EnterCriticalSection( &copyContext->Lock );
  596. InsertHeadList( &(copyContext->PendingDirectoryList), &copyDir->ListEntry );
  597. LeaveCriticalSection( &copyContext->Lock );
  598. }
  599. if (existingMirrorFile != NULL) {
  600. RemoveEntryList( &existingMirrorFile->ListEntry );
  601. IMirrorFreeMem( existingMirrorFile );
  602. }
  603. skipToNextDir:
  604. err = IMFindNextFile( ThreadContext,
  605. ThreadContext->SourceDirHandle,
  606. &findData );
  607. if (err != ERROR_SUCCESS) {
  608. if (err == STATUS_NO_MORE_FILES) {
  609. err = ERROR_SUCCESS;
  610. } else {
  611. errorCase = ReportCopyError( copyContext,
  612. DirectoryInfo->Source,
  613. COPY_ERROR_ACTION_ENUMERATE,
  614. err );
  615. goto exitCopySubtree;
  616. }
  617. }
  618. }
  619. if (err == ERROR_SUCCESS) {
  620. //
  621. // now go through list of remaining files and directories that were on
  622. // the destination but not on the source to delete them. We only do
  623. // that if we successfully made it through all existing master files.
  624. //
  625. if (copyContext->DeleteOtherFiles) {
  626. deleteExistingMirrorFilesNotInMaster = TRUE;
  627. }
  628. }
  629. exitCopySubtree:
  630. while (IsListEmpty( &existingMirrorFilesList ) == FALSE) {
  631. listEntry = RemoveHeadList( &existingMirrorFilesList );
  632. existingMirrorFile = (PEXISTING_MIRROR_FILE) CONTAINING_RECORD( listEntry,
  633. EXISTING_MIRROR_FILE,
  634. ListEntry );
  635. if ((errorCase == STATUS_SUCCESS) &&
  636. deleteExistingMirrorFilesNotInMaster &&
  637. (copyContext->Cancelled == FALSE)) {
  638. lstrcpyW( endOfDestPath, &existingMirrorFile->FileName[0] );
  639. UnconditionalDelete( ThreadContext,
  640. DirectoryInfo->Source,
  641. destFileName,
  642. existingMirrorFile->FileAttributes,
  643. NULL );
  644. }
  645. IMirrorFreeMem( existingMirrorFile );
  646. }
  647. if (destFileName != NULL) {
  648. IMirrorFreeMem( destFileName );
  649. }
  650. if (sourceFileName != NULL) {
  651. IMirrorFreeMem( sourceFileName );
  652. }
  653. if ( ThreadContext->SourceDirHandle != INVALID_HANDLE_VALUE ) {
  654. NtClose( ThreadContext->SourceDirHandle );
  655. ThreadContext->SourceDirHandle = INVALID_HANDLE_VALUE;
  656. }
  657. if ( ThreadContext->DestDirHandle != INVALID_HANDLE_VALUE ) {
  658. NtClose( ThreadContext->DestDirHandle );
  659. ThreadContext->DestDirHandle = INVALID_HANDLE_VALUE;
  660. }
  661. if (ntSourcePath.Buffer) {
  662. RtlFreeHeap( RtlProcessHeap(), 0, ntSourcePath.Buffer );
  663. }
  664. if (ntDestPath.Buffer) {
  665. RtlFreeHeap( RtlProcessHeap(), 0, ntDestPath.Buffer );
  666. }
  667. if (errorCase == STATUS_RETRY) {
  668. goto retryCopySubtree;
  669. }
  670. if ( errorCase == ERROR_SUCCESS ) {
  671. err = ERROR_SUCCESS; // we ignore all errors if user told us to
  672. }
  673. return err;
  674. }
  675. DWORD
  676. CreateMatchingDirectory (
  677. PIMIRROR_THREAD_CONTEXT ThreadContext,
  678. PCOPY_DIRECTORY DirectoryInfo
  679. )
  680. /*++
  681. Description:
  682. This routine ensures that the destination directory on the mirror
  683. matches the source directory. It doesn't handle the files
  684. or subdirectories, just the actual directory itself.
  685. Parameters:
  686. ThreadContext : instance data for this thread copying a tree
  687. DirectoryInfo : structure containing all the info for the directory
  688. Return Value:
  689. Win32 error code
  690. ++*/
  691. {
  692. FILE_BASIC_INFORMATION sourceDirInfo;
  693. FILE_BASIC_INFORMATION destDirInfo;
  694. DWORD err;
  695. BOOLEAN updateBasic;
  696. BOOLEAN updateStoredSecurityAttributes;
  697. BOOLEAN createdDir;
  698. IO_STATUS_BLOCK IoStatusBlock;
  699. ULONG errorCase;
  700. retryCreateDir:
  701. updateBasic = FALSE;
  702. updateStoredSecurityAttributes = FALSE;
  703. createdDir = FALSE;
  704. err = IMirrorOpenDirectory( &ThreadContext->SourceDirHandle,
  705. DirectoryInfo->NtSourceName,
  706. FILE_OPEN,
  707. TRUE,
  708. DirectoryInfo->SourceAttributes,
  709. &sourceDirInfo
  710. );
  711. if (err != ERROR_SUCCESS) {
  712. errorCase = ReportCopyError( ThreadContext->CopyContext,
  713. DirectoryInfo->Source,
  714. COPY_ERROR_ACTION_OPEN_DIR,
  715. err );
  716. if (errorCase == STATUS_RETRY) {
  717. goto retryCreateDir;
  718. }
  719. if (errorCase == ERROR_SUCCESS) {
  720. err = ERROR_SUCCESS;
  721. }
  722. return err;
  723. }
  724. if (DirectoryInfo->SourceAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  725. errorCase = ReportCopyError( ThreadContext->CopyContext,
  726. DirectoryInfo->Source,
  727. COPY_ERROR_ACTION_OPEN_DIR,
  728. ERROR_REPARSE_ATTRIBUTE_CONFLICT );
  729. err = ERROR_REPARSE_ATTRIBUTE_CONFLICT;
  730. if (errorCase == STATUS_RETRY) {
  731. goto retryCreateDir;
  732. }
  733. //
  734. // we can't ever succeed a create request, so don't allow the
  735. // code to return ERROR_SUCCESS, instead always force an abort
  736. //
  737. if (errorCase == ERROR_SUCCESS) {
  738. //err = ERROR_SUCCESS;
  739. err = ERROR_REQUEST_ABORTED;
  740. }
  741. return err;
  742. }
  743. ASSERT( (DirectoryInfo->SourceAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
  744. err = IMirrorOpenDirectory( &ThreadContext->DestDirHandle,
  745. DirectoryInfo->NtDestName,
  746. FILE_OPEN,
  747. FALSE,
  748. FILE_ATTRIBUTE_DIRECTORY,
  749. &destDirInfo
  750. );
  751. if (err == STATUS_NOT_A_DIRECTORY) {
  752. DWORD DestAttributes = GetFileAttributes( DirectoryInfo->Dest );
  753. //
  754. // this is not a directory on the dest, let's delete it.
  755. //
  756. DestAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; // be real sure of this
  757. err = UnconditionalDelete( ThreadContext,
  758. DirectoryInfo->Source,
  759. DirectoryInfo->Dest,
  760. DestAttributes,
  761. NULL );
  762. if (err != ERROR_SUCCESS) {
  763. return err;
  764. }
  765. }
  766. if (ThreadContext->DestDirHandle == INVALID_HANDLE_VALUE) {
  767. //
  768. // try to create the destination directory from the source
  769. //
  770. err = IMirrorOpenDirectory( &ThreadContext->DestDirHandle,
  771. DirectoryInfo->NtDestName,
  772. FILE_CREATE,
  773. FALSE,
  774. FILE_ATTRIBUTE_DIRECTORY,
  775. &destDirInfo
  776. );
  777. // report either success or failure up to the caller
  778. if (!NT_SUCCESS( err )) {
  779. errorCase = ReportCopyError( ThreadContext->CopyContext,
  780. DirectoryInfo->Dest,
  781. COPY_ERROR_ACTION_CREATE_DIR,
  782. err );
  783. if (errorCase == STATUS_RETRY) {
  784. goto retryCreateDir;
  785. }
  786. if (errorCase == ERROR_SUCCESS) {
  787. err = ERROR_SUCCESS;
  788. }
  789. return err;
  790. }
  791. //
  792. // this is for the success case so it won't fail.
  793. //
  794. ReportCopyError( ThreadContext->CopyContext,
  795. DirectoryInfo->Dest,
  796. COPY_ERROR_ACTION_CREATE_DIR,
  797. ERROR_SUCCESS );
  798. InterlockedIncrement( (PLONG)&ThreadContext->CopyContext->DirectoriesCreated ); // this is really a ULONG
  799. createdDir = TRUE;
  800. updateBasic = TRUE;
  801. updateStoredSecurityAttributes = TRUE;
  802. } else {
  803. MIRROR_ACL_STREAM aclStream;
  804. //
  805. // let's get the security descriptor and extended attributes to
  806. // see if we need to update our alternate data stream on the target.
  807. //
  808. err = GetOurSecurityStream( ThreadContext, DirectoryInfo->Dest, &aclStream );
  809. if (!NT_SUCCESS( err )) {
  810. updateStoredSecurityAttributes = TRUE;
  811. } else {
  812. destDirInfo.ChangeTime = aclStream.ChangeTime;
  813. if (( aclStream.ChangeTime.QuadPart != sourceDirInfo.ChangeTime.QuadPart ) ||
  814. ( aclStream.ExtendedAttributes != DirectoryInfo->SourceAttributes ) ) {
  815. updateStoredSecurityAttributes = TRUE;
  816. }
  817. }
  818. //
  819. // if the creation time or lastwrite time is different, then we'll
  820. // update them on the target to match the source.
  821. //
  822. if (( destDirInfo.CreationTime.QuadPart != sourceDirInfo.CreationTime.QuadPart ) ||
  823. ( destDirInfo.LastWriteTime.QuadPart != sourceDirInfo.LastWriteTime.QuadPart )) {
  824. updateBasic = TRUE;
  825. }
  826. }
  827. //
  828. // Save the complete attribute values in the alternate data stream
  829. // on the slave file.
  830. //
  831. if (updateStoredSecurityAttributes || DirectoryInfo->DirectoryRoot) {
  832. err = StoreOurSecurityStream( ThreadContext,
  833. DirectoryInfo->Source,
  834. DirectoryInfo->Dest,
  835. DirectoryInfo->SourceAttributes,
  836. sourceDirInfo.ChangeTime
  837. );
  838. updateBasic = TRUE;
  839. }
  840. if ((err == ERROR_SUCCESS) &&
  841. updateBasic &&
  842. (DirectoryInfo->DirectoryRoot == FALSE)) {
  843. destDirInfo.CreationTime = sourceDirInfo.CreationTime;
  844. destDirInfo.LastWriteTime = sourceDirInfo.LastWriteTime;
  845. destDirInfo.ChangeTime = sourceDirInfo.ChangeTime;
  846. destDirInfo.FileAttributes = 0; // leave dir attributes alone.
  847. err = NtSetInformationFile( ThreadContext->DestDirHandle,
  848. &IoStatusBlock,
  849. &destDirInfo,
  850. sizeof( FILE_BASIC_INFORMATION ),
  851. FileBasicInformation
  852. );
  853. err = IMConvertNT2Win32Error( err );
  854. if ( err != ERROR_SUCCESS) {
  855. errorCase = ReportCopyError( ThreadContext->CopyContext,
  856. DirectoryInfo->Dest,
  857. COPY_ERROR_ACTION_SETATTR,
  858. GetLastError() );
  859. if (errorCase == STATUS_RETRY) {
  860. goto retryCreateDir;
  861. }
  862. if (errorCase == ERROR_SUCCESS) {
  863. err = ERROR_SUCCESS;
  864. }
  865. } else if (! createdDir ) {
  866. InterlockedIncrement( (PLONG)&ThreadContext->CopyContext->AttributesModified ); // this is really a ULONG
  867. }
  868. }
  869. //
  870. // Save off our SFN information too.
  871. //
  872. if( (err == ERROR_SUCCESS) && (DirectoryInfo->DirectoryRoot == FALSE) ) {
  873. WCHAR ShortFileNameInStream[MAX_PATH*2];
  874. WCHAR *p = NULL;
  875. //
  876. // Get the short file name on the source directory.
  877. //
  878. ShortFileNameInStream[0] = L'\0';
  879. //
  880. // It's likely that our path looks like \??\C:\xxxxx,
  881. // which GetShortPathName will fail on. We need to fix
  882. // up the path so it looks like a good ol' DOS path.
  883. //
  884. if( p = wcsrchr(DirectoryInfo->NtSourceName, L':') ) {
  885. p -= 1;
  886. } else {
  887. p = DirectoryInfo->NtSourceName;
  888. }
  889. err = GetShortPathName( p,
  890. ShortFileNameInStream,
  891. ARRAYSIZE(ShortFileNameInStream) );
  892. //
  893. // If we got a short file name, then go set that information in
  894. // the alternate stream in our destination file.
  895. //
  896. if( err == 0 ) {
  897. err = GetLastError();
  898. } else {
  899. if( wcscmp(ShortFileNameInStream, p) ) {
  900. //
  901. // The short file name is different from the name of
  902. // our source file, so better save it off.
  903. //
  904. if( p = wcsrchr(ShortFileNameInStream, L'\\') ) {
  905. p += 1;
  906. } else {
  907. p = ShortFileNameInStream;
  908. }
  909. if( *p != '\0' ) {
  910. WCHAR SavedCharacter = L'\0';
  911. PWSTR q = NULL;
  912. //
  913. // Incredibly nausiating hack where CreateFile explodes
  914. // when we send him a "\??\UNC\...." path, which is exactly
  915. // what we're probably going to send him when we call into
  916. // StoreOurSFNStream(). We'll need to patch the NtDestName
  917. // here, then restore it when we come back.
  918. //
  919. if( q = wcsstr(DirectoryInfo->NtDestName, L"\\??\\UNC\\") ) {
  920. SavedCharacter = DirectoryInfo->NtDestName[6];
  921. DirectoryInfo->NtDestName[6] = L'\\';
  922. q = &DirectoryInfo->NtDestName[6];
  923. } else {
  924. q = DirectoryInfo->NtDestName;
  925. }
  926. err = StoreOurSFNStream( ThreadContext,
  927. DirectoryInfo->NtSourceName,
  928. q,
  929. p );
  930. if( SavedCharacter != L'\0' ) {
  931. // restore the destination path.
  932. DirectoryInfo->NtDestName[6] = SavedCharacter;
  933. }
  934. }
  935. }
  936. }
  937. //
  938. // Cover up any errors here because it's certainly not fatal.
  939. //
  940. err = ERROR_SUCCESS;
  941. }
  942. return err;
  943. }
  944. DWORD
  945. IMirrorOpenDirectory (
  946. HANDLE *Handle,
  947. PWCHAR NtDirName,
  948. DWORD Disposition,
  949. BOOLEAN IsSource,
  950. DWORD SourceAttributes,
  951. PFILE_BASIC_INFORMATION BasicDirInfo OPTIONAL
  952. )
  953. {
  954. NTSTATUS Status;
  955. OBJECT_ATTRIBUTES Obja;
  956. IO_STATUS_BLOCK IoStatusBlock;
  957. UNICODE_STRING UnicodeInput;
  958. DWORD createOptions;
  959. DWORD desiredAccess;
  960. BOOLEAN StrippedTrailingSlash;
  961. ASSERT( Handle != NULL );
  962. ASSERT( *Handle == INVALID_HANDLE_VALUE );
  963. RtlInitUnicodeString(&UnicodeInput,NtDirName);
  964. if ((UnicodeInput.Length > 2 * sizeof(WCHAR)) &&
  965. (UnicodeInput.Buffer[(UnicodeInput.Length>>1)-1] == L'\\') &&
  966. (UnicodeInput.Buffer[(UnicodeInput.Length>>1)-2] != L':' )) {
  967. UnicodeInput.Length -= sizeof(UNICODE_NULL);
  968. StrippedTrailingSlash = TRUE;
  969. } else {
  970. StrippedTrailingSlash = FALSE;
  971. }
  972. createOptions = FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT;
  973. desiredAccess = FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
  974. if (IsSource) {
  975. createOptions |= FILE_OPEN_FOR_BACKUP_INTENT;
  976. } else {
  977. desiredAccess |= FILE_ADD_FILE |
  978. FILE_ADD_SUBDIRECTORY |
  979. FILE_WRITE_ATTRIBUTES |
  980. FILE_DELETE_CHILD;
  981. }
  982. retryCreate:
  983. InitializeObjectAttributes(
  984. &Obja,
  985. &UnicodeInput,
  986. OBJ_CASE_INSENSITIVE,
  987. NULL,
  988. NULL
  989. );
  990. //
  991. // Open the directory for the desired access. This may create it.
  992. //
  993. Status = NtCreateFile(
  994. Handle,
  995. desiredAccess,
  996. &Obja,
  997. &IoStatusBlock,
  998. NULL,
  999. SourceAttributes,
  1000. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1001. Disposition,
  1002. createOptions,
  1003. NULL,
  1004. 0 );
  1005. if ( Status == STATUS_INVALID_PARAMETER && StrippedTrailingSlash ) {
  1006. //
  1007. // open of a pnp style path failed, so try putting back the trailing slash
  1008. //
  1009. UnicodeInput.Length += sizeof(UNICODE_NULL);
  1010. StrippedTrailingSlash = FALSE;
  1011. goto retryCreate;
  1012. }
  1013. if (*Handle == NULL) {
  1014. *Handle = INVALID_HANDLE_VALUE;
  1015. }
  1016. if (NT_SUCCESS( Status ) && BasicDirInfo != NULL) {
  1017. //
  1018. // read the attributes for the caller too
  1019. //
  1020. Status = NtQueryInformationFile( *Handle,
  1021. &IoStatusBlock,
  1022. BasicDirInfo,
  1023. sizeof( FILE_BASIC_INFORMATION ),
  1024. FileBasicInformation
  1025. );
  1026. }
  1027. return IMConvertNT2Win32Error( Status );
  1028. }
  1029. DWORD
  1030. MirrorFile(
  1031. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1032. PWCHAR SourceFileName,
  1033. PFILE_FULL_DIR_INFORMATION SourceFindData,
  1034. PWCHAR DestFileName,
  1035. PEXISTING_MIRROR_FILE ExistingMirrorFile
  1036. )
  1037. {
  1038. DWORD err;
  1039. BOOLEAN fileIsAlreadyThere;
  1040. PCOPY_TREE_CONTEXT copyContext;
  1041. BOOLEAN updateStoredSecurityAttributes;
  1042. BOOLEAN updateStoredSFNAttributes;
  1043. BOOLEAN updateBasic;
  1044. BOOLEAN isEncrypted;
  1045. FILE_BASIC_INFORMATION fileBasicInfo;
  1046. IO_STATUS_BLOCK IoStatusBlock;
  1047. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1048. MIRROR_ACL_STREAM aclStream;
  1049. MIRROR_SFN_STREAM SFNStream;
  1050. ULONG errorCase;
  1051. static FARPROC pSetFileShortName = NULL;
  1052. static BOOL AlreadyCheckedForExport = FALSE;
  1053. WCHAR ShortFileNameInStream[32];
  1054. WCHAR ShortFileName[MAX_PATH],*p;
  1055. retryMirrorFile:
  1056. if ( fileHandle != INVALID_HANDLE_VALUE ) {
  1057. CloseHandle( fileHandle );
  1058. fileHandle = INVALID_HANDLE_VALUE;
  1059. }
  1060. errorCase = STATUS_SUCCESS;
  1061. err = STATUS_SUCCESS;
  1062. fileIsAlreadyThere = FALSE;
  1063. copyContext = ThreadContext->CopyContext;
  1064. updateStoredSecurityAttributes = TRUE;
  1065. updateStoredSFNAttributes = TRUE;
  1066. updateBasic = TRUE;
  1067. isEncrypted = FALSE;
  1068. ShortFileName[0] = L'\0';
  1069. GetShortPathName(
  1070. SourceFileName,
  1071. ShortFileName,
  1072. ARRAYSIZE(ShortFileName));
  1073. if (p = wcsrchr(ShortFileName, L'\\')) {
  1074. p += 1;
  1075. } else {
  1076. p = ShortFileName;
  1077. }
  1078. if (!AlreadyCheckedForExport) {
  1079. HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
  1080. if (hKernel32) {
  1081. pSetFileShortName = GetProcAddress(
  1082. hKernel32,
  1083. "SetFileShortNameW");
  1084. }
  1085. AlreadyCheckedForExport = TRUE;
  1086. }
  1087. // don't copy file if callback says not to
  1088. if ((err = IMirrorNowDoing(CopyFiles, SourceFileName)) != ERROR_SUCCESS) {
  1089. if (err == STATUS_REQUEST_ABORTED) {
  1090. copyContext->Cancelled = TRUE;
  1091. }
  1092. return STATUS_SUCCESS;
  1093. }
  1094. //
  1095. // sorry, for this release we currently don't support encrypted files.
  1096. //
  1097. if (SourceFindData->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  1098. errorCase = ReportCopyError( copyContext,
  1099. SourceFileName,
  1100. COPY_ERROR_ACTION_CREATE_FILE,
  1101. ERROR_FILE_ENCRYPTED );
  1102. if (errorCase == STATUS_RETRY) {
  1103. SourceFindData->FileAttributes = GetFileAttributes( SourceFileName );
  1104. goto retryMirrorFile;
  1105. }
  1106. if (errorCase == ERROR_SUCCESS) {
  1107. err = STATUS_SUCCESS;
  1108. } else {
  1109. err = ERROR_FILE_ENCRYPTED;
  1110. }
  1111. return err;
  1112. }
  1113. fileBasicInfo.FileAttributes = 0; // by default, leave them alone
  1114. if (SourceFindData->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  1115. err = CanHandleReparsePoint( ThreadContext,
  1116. SourceFileName,
  1117. SourceFindData->FileAttributes
  1118. );
  1119. if (!NT_SUCCESS(err)) {
  1120. errorCase = ReportCopyError( copyContext,
  1121. SourceFileName,
  1122. COPY_ERROR_ACTION_CREATE_FILE,
  1123. err );
  1124. if (errorCase == STATUS_RETRY) {
  1125. SourceFindData->FileAttributes = GetFileAttributes( SourceFileName );
  1126. goto retryMirrorFile;
  1127. }
  1128. if (errorCase == ERROR_SUCCESS) {
  1129. err = STATUS_SUCCESS;
  1130. }
  1131. return err;
  1132. }
  1133. SourceFindData->FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
  1134. }
  1135. if (ExistingMirrorFile) {
  1136. if (ExistingMirrorFile->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1137. //
  1138. // it exists as a directory. Master is always right, let's
  1139. // delete the directory.
  1140. //
  1141. // Also, if the master and slave differ in whether the file is
  1142. // encrypted or not, delete the slave.
  1143. //
  1144. err = UnconditionalDelete( ThreadContext,
  1145. SourceFileName,
  1146. DestFileName,
  1147. ExistingMirrorFile->FileAttributes,
  1148. NULL );
  1149. if (err != ERROR_SUCCESS) {
  1150. return err;
  1151. }
  1152. ExistingMirrorFile = NULL;
  1153. } else {
  1154. //
  1155. // if the files are the same, leave it be.
  1156. //
  1157. if ((SourceFindData->CreationTime.QuadPart == ExistingMirrorFile->CreationTime.QuadPart ) &&
  1158. (SourceFindData->LastWriteTime.QuadPart == ExistingMirrorFile->LastWriteTime.QuadPart) &&
  1159. (SourceFindData->EaSize == ExistingMirrorFile->EaSize) &&
  1160. (SourceFindData->EndOfFile.QuadPart == ExistingMirrorFile->EndOfFile.QuadPart)) {
  1161. fileIsAlreadyThere = TRUE;
  1162. updateBasic = FALSE;
  1163. //
  1164. // let's get the security descriptor and extended attributes to
  1165. // see if we need to update our alternate data stream on the target.
  1166. //
  1167. err = GetOurSecurityStream( ThreadContext, DestFileName, &aclStream );
  1168. if ((err == ERROR_SUCCESS) &&
  1169. (aclStream.ChangeTime.QuadPart == SourceFindData->ChangeTime.QuadPart) &&
  1170. (SourceFindData->FileAttributes == aclStream.ExtendedAttributes)) {
  1171. updateStoredSecurityAttributes = FALSE;
  1172. } else {
  1173. err = ERROR_SUCCESS;
  1174. }
  1175. //
  1176. // let's get the short file name to see if we need to update
  1177. // our alternate data stream on the target.
  1178. //
  1179. err = GetOurSFNStream(
  1180. ThreadContext,
  1181. DestFileName,
  1182. &SFNStream,
  1183. ShortFileNameInStream,
  1184. sizeof(ShortFileNameInStream) );
  1185. if ((err == ERROR_SUCCESS) &&
  1186. *p != L'\0' &&
  1187. (wcscmp(ShortFileNameInStream, p) == 0)) {
  1188. updateStoredSFNAttributes = FALSE;
  1189. } else {
  1190. err = ERROR_SUCCESS;
  1191. }
  1192. }
  1193. }
  1194. }
  1195. //
  1196. // if the file already exists but it's not current or our alternate
  1197. // stream needs updating, let's update the attributes such that we can
  1198. // modify the file.
  1199. //
  1200. fileBasicInfo.CreationTime.QuadPart = SourceFindData->CreationTime.QuadPart;
  1201. fileBasicInfo.LastWriteTime.QuadPart = SourceFindData->LastWriteTime.QuadPart;
  1202. fileBasicInfo.LastAccessTime.QuadPart = SourceFindData->LastAccessTime.QuadPart;
  1203. fileBasicInfo.ChangeTime.QuadPart = SourceFindData->ChangeTime.QuadPart;
  1204. err = ERROR_SUCCESS;
  1205. if (! fileIsAlreadyThere) {
  1206. if (CopyFile( SourceFileName, DestFileName, FALSE) == FALSE) {
  1207. err = GetLastError();
  1208. } else {
  1209. err = ERROR_SUCCESS;
  1210. }
  1211. if (err == ERROR_SHARING_VIOLATION) {
  1212. //
  1213. // we ignore sharing violations for the following files :
  1214. // system registry files
  1215. // tracking.log
  1216. // ntuser.dat & ntuser.dat.log
  1217. // usrclass.dat & usrclass.dat.log
  1218. //
  1219. PWCHAR fileName = SourceFileName;
  1220. PIMIRROR_IGNORE_FILE_LIST ignoreListEntry;
  1221. ULONG componentLength;
  1222. PLIST_ENTRY listEntry;
  1223. if (_wcsicmp(SourceFileName, L"\\\\?\\")) {
  1224. PWCHAR firstSlash;
  1225. fileName += 4; // now fileName points to L"C:\WINNT..."
  1226. firstSlash = fileName;
  1227. while (*firstSlash != L'\\' && *firstSlash != L'\0') {
  1228. firstSlash++;
  1229. }
  1230. if (*firstSlash != L'\0') {
  1231. fileName = firstSlash+1; // now fileName points to L"WINNT\..."
  1232. }
  1233. }
  1234. componentLength = lstrlenW( fileName );
  1235. listEntry = ThreadContext->FilesToIgnore.Flink;
  1236. while (listEntry != &(ThreadContext->FilesToIgnore)) {
  1237. ignoreListEntry = (PIMIRROR_IGNORE_FILE_LIST)
  1238. CONTAINING_RECORD( listEntry,
  1239. IMIRROR_IGNORE_FILE_LIST,
  1240. ListEntry );
  1241. if (CompareStringW( LOCALE_SYSTEM_DEFAULT,
  1242. NORM_IGNORECASE,
  1243. fileName,
  1244. min( componentLength, ignoreListEntry->FileNameLength),
  1245. &ignoreListEntry->FileName[0],
  1246. ignoreListEntry->FileNameLength) == 2) {
  1247. // it matched one of our special files. we'll ignore the
  1248. // error but also not set the attributes on the image.
  1249. return err;
  1250. }
  1251. listEntry = listEntry->Flink;
  1252. }
  1253. }
  1254. // report either success or failure up to the caller
  1255. if (err == ERROR_SUCCESS) {
  1256. ReportCopyError( ThreadContext->CopyContext,
  1257. SourceFileName,
  1258. COPY_ERROR_ACTION_CREATE_FILE,
  1259. err );
  1260. InterlockedIncrement( (PLONG)&copyContext->FilesCopied ); // this is really a ULONG
  1261. copyContext->BytesCopied.QuadPart += SourceFindData->EndOfFile.QuadPart;
  1262. } else {
  1263. errorCase = ReportCopyError( ThreadContext->CopyContext,
  1264. SourceFileName,
  1265. COPY_ERROR_ACTION_CREATE_FILE,
  1266. err );
  1267. if (errorCase == STATUS_RETRY) {
  1268. goto retryMirrorFile;
  1269. }
  1270. if (errorCase == ERROR_SUCCESS) {
  1271. err = STATUS_SUCCESS;
  1272. }
  1273. return err;
  1274. }
  1275. updateStoredSecurityAttributes = TRUE;
  1276. updateStoredSFNAttributes = TRUE;
  1277. updateBasic = TRUE;
  1278. fileBasicInfo.FileAttributes = 0; // don't set the attribute again.
  1279. if (err == STATUS_SUCCESS) {
  1280. //
  1281. // we just created the file so we'll just give it the archive
  1282. // bit as an attribute since we've saved off the rest in the
  1283. // stream.
  1284. //
  1285. if (! SetFileAttributes( DestFileName, FILE_ATTRIBUTE_ARCHIVE )) {
  1286. err = GetLastError();
  1287. errorCase = ReportCopyError( copyContext,
  1288. DestFileName,
  1289. COPY_ERROR_ACTION_SETATTR,
  1290. err );
  1291. if (errorCase == STATUS_RETRY) {
  1292. goto retryMirrorFile;
  1293. }
  1294. if (errorCase == ERROR_SUCCESS) {
  1295. err = STATUS_SUCCESS;
  1296. }
  1297. }
  1298. }
  1299. }
  1300. if ((err == ERROR_SUCCESS) && updateStoredSFNAttributes && (*p != L'\0')) {
  1301. err = StoreOurSFNStream( ThreadContext,
  1302. SourceFileName,
  1303. DestFileName,
  1304. p
  1305. );
  1306. updateBasic = TRUE;
  1307. }
  1308. if ((err == ERROR_SUCCESS) && updateStoredSecurityAttributes) {
  1309. err = StoreOurSecurityStream( ThreadContext,
  1310. SourceFileName,
  1311. DestFileName,
  1312. SourceFindData->FileAttributes,
  1313. SourceFindData->ChangeTime
  1314. );
  1315. updateBasic = TRUE;
  1316. }
  1317. if ((err == ERROR_SUCCESS) && updateBasic) {
  1318. //
  1319. // set create date and lastUpdate date to correct values
  1320. //
  1321. fileHandle = CreateFile( DestFileName,
  1322. FILE_WRITE_ATTRIBUTES | DELETE,
  1323. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1324. NULL,
  1325. OPEN_EXISTING,
  1326. 0,
  1327. NULL );
  1328. if (fileHandle == INVALID_HANDLE_VALUE) {
  1329. err = GetLastError();
  1330. } else {
  1331. //
  1332. // first try to set the short name. If this fails, we just ignore
  1333. // the error.
  1334. //
  1335. if (pSetFileShortName) {
  1336. pSetFileShortName( fileHandle, p );
  1337. }
  1338. //
  1339. // if we're making a change to an existing file, update the ARCHIVE bit.
  1340. //
  1341. if (fileIsAlreadyThere &&
  1342. 0 == (fileBasicInfo.FileAttributes & FILE_ATTRIBUTE_ARCHIVE)) {
  1343. fileBasicInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
  1344. }
  1345. //
  1346. // set create date and lastUpdate date to correct values
  1347. //
  1348. err = NtSetInformationFile( fileHandle,
  1349. &IoStatusBlock,
  1350. &fileBasicInfo,
  1351. sizeof( FILE_BASIC_INFORMATION ),
  1352. FileBasicInformation
  1353. );
  1354. err = IMConvertNT2Win32Error( err );
  1355. }
  1356. if (err != STATUS_SUCCESS) {
  1357. errorCase = ReportCopyError( copyContext,
  1358. DestFileName,
  1359. COPY_ERROR_ACTION_SETTIME,
  1360. err );
  1361. if (errorCase == STATUS_RETRY) {
  1362. goto retryMirrorFile;
  1363. }
  1364. if (errorCase == ERROR_SUCCESS) {
  1365. err = STATUS_SUCCESS;
  1366. }
  1367. } else if (fileIsAlreadyThere) {
  1368. InterlockedIncrement( (PLONG) &copyContext->AttributesModified ); // this is really a ULONG
  1369. }
  1370. }
  1371. if (err == STATUS_SUCCESS) {
  1372. //
  1373. // report that we succeeded in copying the file
  1374. //
  1375. (VOID)ReportCopyError( copyContext,
  1376. SourceFileName,
  1377. COPY_ERROR_ACTION_CREATE_FILE,
  1378. err );
  1379. }
  1380. if ( fileHandle != INVALID_HANDLE_VALUE ) {
  1381. CloseHandle( fileHandle );
  1382. }
  1383. return err;
  1384. }
  1385. DWORD
  1386. UnconditionalDelete (
  1387. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1388. PWCHAR SourceFile,
  1389. PWCHAR FileToDelete,
  1390. DWORD Attributes,
  1391. PWCHAR NameBuffer
  1392. )
  1393. {
  1394. DWORD err;
  1395. BOOLEAN allocatedBuffer;
  1396. BOOLEAN reportError;
  1397. PCOPY_TREE_CONTEXT copyContext;
  1398. retryDelete:
  1399. err = ERROR_SUCCESS;
  1400. allocatedBuffer = FALSE;
  1401. reportError = TRUE;
  1402. copyContext = ThreadContext->CopyContext;
  1403. if (copyContext->DeleteOtherFiles == FALSE) {
  1404. err = ERROR_WRITE_PROTECT;
  1405. goto exitWithError;
  1406. }
  1407. if ((Attributes & (FILE_ATTRIBUTE_READONLY |
  1408. FILE_ATTRIBUTE_HIDDEN |
  1409. FILE_ATTRIBUTE_SYSTEM)) != 0) {
  1410. // set the attributes back to normal
  1411. Attributes &= ~FILE_ATTRIBUTE_READONLY;
  1412. Attributes &= ~FILE_ATTRIBUTE_HIDDEN;
  1413. Attributes &= ~FILE_ATTRIBUTE_SYSTEM;
  1414. SetFileAttributesW( FileToDelete, Attributes );
  1415. }
  1416. if ((Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  1417. if (! DeleteFile( FileToDelete )) {
  1418. err = GetLastError();
  1419. } else {
  1420. InterlockedIncrement( (PLONG)&copyContext->FilesDeleted ); // this is really a ULONG
  1421. }
  1422. } else {
  1423. //
  1424. // remove all files and subdirectories recursively here...
  1425. //
  1426. HANDLE fileEnum = INVALID_HANDLE_VALUE;
  1427. WIN32_FIND_DATA findData;
  1428. PWCHAR startFileName;
  1429. ULONG dirLength;
  1430. if (NameBuffer == NULL) {
  1431. NameBuffer = IMirrorAllocMem( TMP_BUFFER_SIZE );
  1432. if (NameBuffer == NULL) {
  1433. //err = STATUS_NO_MEMORY;
  1434. err = ERROR_NOT_ENOUGH_MEMORY;
  1435. goto exitWithError;
  1436. }
  1437. lstrcpyW( NameBuffer, FileToDelete );
  1438. allocatedBuffer = TRUE;
  1439. }
  1440. dirLength = lstrlenW( NameBuffer );
  1441. lstrcatW( NameBuffer, L"\\*" );
  1442. // remember the start of the char after the backslash to slap in the name
  1443. startFileName = NameBuffer + dirLength + 1;
  1444. err = ERROR_SUCCESS;
  1445. fileEnum = FindFirstFile( NameBuffer, &findData );
  1446. if (fileEnum != INVALID_HANDLE_VALUE) {
  1447. while (copyContext->Cancelled == FALSE) {
  1448. if (((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
  1449. (findData.cFileName[0] == L'.')) {
  1450. if ((findData.cFileName[1] == L'\0') ||
  1451. (findData.cFileName[1] == L'.' &&
  1452. findData.cFileName[2] == L'\0')) {
  1453. goto skipToNextDir;
  1454. }
  1455. }
  1456. lstrcpyW( startFileName, &findData.cFileName[0] );
  1457. err = UnconditionalDelete( ThreadContext,
  1458. SourceFile,
  1459. NameBuffer,
  1460. findData.dwFileAttributes,
  1461. NameBuffer );
  1462. if (err != ERROR_SUCCESS) {
  1463. reportError = FALSE;
  1464. break;
  1465. }
  1466. skipToNextDir:
  1467. if (! FindNextFile( fileEnum, &findData)) {
  1468. err = GetLastError();
  1469. if (err == ERROR_NO_MORE_FILES) {
  1470. err = ERROR_SUCCESS;
  1471. break;
  1472. }
  1473. }
  1474. }
  1475. FindClose( fileEnum );
  1476. *(NameBuffer+dirLength) = L'\0';
  1477. }
  1478. if (err == ERROR_SUCCESS) {
  1479. if (! RemoveDirectory( FileToDelete ) ) {
  1480. err = GetLastError();
  1481. } else {
  1482. InterlockedIncrement( (PLONG)&copyContext->DirectoriesDeleted );// this is really a ULONG
  1483. }
  1484. }
  1485. }
  1486. exitWithError:
  1487. // we report the error for both success and failure
  1488. if (allocatedBuffer && NameBuffer != NULL) {
  1489. IMirrorFreeMem( NameBuffer );
  1490. }
  1491. if (reportError) {
  1492. DWORD errorCase;
  1493. errorCase = ReportCopyError( copyContext,
  1494. FileToDelete,
  1495. COPY_ERROR_ACTION_DELETE,
  1496. err );
  1497. if (errorCase == STATUS_RETRY) {
  1498. goto retryDelete;
  1499. }
  1500. if (errorCase == ERROR_SUCCESS) {
  1501. err = ERROR_SUCCESS;
  1502. }
  1503. }
  1504. return err;
  1505. }
  1506. DWORD
  1507. StoreOurSecurityStream (
  1508. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1509. PWCHAR Source,
  1510. PWCHAR Dest,
  1511. DWORD AttributesToStore,
  1512. LARGE_INTEGER ChangeTime
  1513. )
  1514. //
  1515. // This routine stores off the acl from the master into a named alternate
  1516. // data stream on the destination. It saves off both the ACL and a few
  1517. // file attributes that couldn't be stored in the normal directory entry.
  1518. //
  1519. {
  1520. PSECURITY_DESCRIPTOR SourceSD;
  1521. PCOPY_TREE_CONTEXT copyContext;
  1522. DWORD err;
  1523. DWORD requiredLength;
  1524. HANDLE hAclFile;
  1525. PWCHAR aclFileName;
  1526. ULONG action;
  1527. MIRROR_ACL_STREAM mirrorAclStream;
  1528. DWORD BytesWritten;
  1529. DWORD deleteAclFile;
  1530. DWORD errorCase;
  1531. retryWriteStream:
  1532. errorCase = STATUS_SUCCESS;
  1533. SourceSD = NULL;
  1534. copyContext = ThreadContext->CopyContext;
  1535. err = ERROR_SUCCESS;
  1536. requiredLength = 0;
  1537. hAclFile = INVALID_HANDLE_VALUE;
  1538. action = COPY_ERROR_ACTION_GETACL;
  1539. deleteAclFile = FALSE;
  1540. //
  1541. // We use the SDBuffer on the thread context to store not only the
  1542. // security descriptor but also the file name of the alternate data stream.
  1543. //
  1544. requiredLength = (lstrlenW( Dest ) + lstrlenW( IMIRROR_ACL_STREAM_NAME ) + 1) * sizeof(WCHAR);
  1545. if (ThreadContext->SDBuffer == NULL || requiredLength > ThreadContext->SDBufferLength) {
  1546. if (ThreadContext->SDBuffer != NULL) {
  1547. IMirrorFreeMem( ThreadContext->SDBuffer );
  1548. ThreadContext->SDBuffer = NULL;
  1549. ThreadContext->SDBufferLength = requiredLength;
  1550. }
  1551. if (requiredLength > ThreadContext->SDBufferLength) {
  1552. ThreadContext->SDBufferLength = requiredLength;
  1553. }
  1554. ThreadContext->SDBuffer = IMirrorAllocMem( ThreadContext->SDBufferLength );
  1555. if (ThreadContext->SDBuffer == NULL) {
  1556. err = GetLastError();
  1557. errorCase = ReportCopyError( copyContext,
  1558. Source,
  1559. COPY_ERROR_ACTION_MALLOC,
  1560. err );
  1561. goto IMCEExit;
  1562. }
  1563. }
  1564. aclFileName = (PWCHAR) ThreadContext->SDBuffer;
  1565. lstrcpyW( aclFileName, Dest );
  1566. lstrcatW( aclFileName, IMIRROR_ACL_STREAM_NAME );
  1567. hAclFile = CreateFile( aclFileName,
  1568. GENERIC_WRITE,
  1569. 0, // Exclusive access.
  1570. NULL, // Default security descriptor.
  1571. CREATE_ALWAYS, // Overrides if file exists.
  1572. 0, // no special attributes
  1573. NULL
  1574. );
  1575. if (hAclFile == INVALID_HANDLE_VALUE) {
  1576. err = GetLastError();
  1577. errorCase = ReportCopyError( copyContext,
  1578. Source,
  1579. COPY_ERROR_ACTION_CREATE_FILE,
  1580. err );
  1581. goto IMCEExit;
  1582. }
  1583. //
  1584. // read the source security descriptor into the buffer allocated off the
  1585. // thread context.
  1586. //
  1587. if (ThreadContext->IsNTFS == FALSE) {
  1588. requiredLength = 0;
  1589. } else {
  1590. err = ERROR_INSUFFICIENT_BUFFER;
  1591. while (err == ERROR_INSUFFICIENT_BUFFER) {
  1592. if (ThreadContext->SDBuffer == NULL) {
  1593. ThreadContext->SDBuffer = IMirrorAllocMem( ThreadContext->SDBufferLength );
  1594. if (ThreadContext->SDBuffer == NULL) {
  1595. err = GetLastError();
  1596. break;
  1597. }
  1598. }
  1599. SourceSD = (PSECURITY_DESCRIPTOR) ThreadContext->SDBuffer;
  1600. //
  1601. // get SD of the SourceRoot file. This comes back self relative.
  1602. //
  1603. if (GetFileSecurity( Source,
  1604. (DACL_SECURITY_INFORMATION |
  1605. GROUP_SECURITY_INFORMATION |
  1606. SACL_SECURITY_INFORMATION |
  1607. OWNER_SECURITY_INFORMATION),
  1608. SourceSD,
  1609. ThreadContext->SDBufferLength,
  1610. &requiredLength )) {
  1611. err = ERROR_SUCCESS;
  1612. } else {
  1613. err = GetLastError();
  1614. if ((err == ERROR_INSUFFICIENT_BUFFER) ||
  1615. (requiredLength > ThreadContext->SDBufferLength)) {
  1616. // let's try it again with a bigger buffer.
  1617. ThreadContext->SDBufferLength = requiredLength;
  1618. IMirrorFreeMem( ThreadContext->SDBuffer );
  1619. ThreadContext->SDBuffer = NULL;
  1620. err = ERROR_INSUFFICIENT_BUFFER;
  1621. }
  1622. }
  1623. }
  1624. if (err != ERROR_SUCCESS) {
  1625. errorCase = ReportCopyError( copyContext,
  1626. Source,
  1627. COPY_ERROR_ACTION_GETACL,
  1628. err );
  1629. goto IMCEExit;
  1630. }
  1631. InterlockedIncrement( (PLONG)&copyContext->SourceSecurityDescriptorsRead );// this is really a ULONG
  1632. ASSERT( IsValidSecurityDescriptor(SourceSD) );
  1633. }
  1634. mirrorAclStream.StreamVersion = IMIRROR_ACL_STREAM_VERSION;
  1635. mirrorAclStream.StreamLength = sizeof( MIRROR_ACL_STREAM ) +
  1636. requiredLength;
  1637. mirrorAclStream.ChangeTime.QuadPart = ChangeTime.QuadPart;
  1638. mirrorAclStream.ExtendedAttributes = AttributesToStore;
  1639. mirrorAclStream.SecurityDescriptorLength = requiredLength;
  1640. if ((WriteFile( hAclFile,
  1641. &mirrorAclStream,
  1642. sizeof( MIRROR_ACL_STREAM ),
  1643. &BytesWritten,
  1644. NULL // No overlap.
  1645. ) == FALSE) ||
  1646. (BytesWritten < sizeof( MIRROR_ACL_STREAM ))) {
  1647. deleteAclFile = TRUE;
  1648. err = GetLastError();
  1649. errorCase = ReportCopyError( copyContext,
  1650. Source,
  1651. COPY_ERROR_ACTION_SETACL,
  1652. err );
  1653. goto IMCEExit;
  1654. }
  1655. if (ThreadContext->IsNTFS) {
  1656. if ((WriteFile( hAclFile,
  1657. SourceSD,
  1658. requiredLength,
  1659. &BytesWritten,
  1660. NULL // No overlap.
  1661. ) == FALSE) ||
  1662. (BytesWritten < requiredLength )) {
  1663. deleteAclFile = TRUE;
  1664. err = GetLastError();
  1665. errorCase = ReportCopyError( copyContext,
  1666. Source,
  1667. COPY_ERROR_ACTION_SETACL,
  1668. err );
  1669. goto IMCEExit;
  1670. }
  1671. InterlockedIncrement( (PLONG)&copyContext->SecurityDescriptorsWritten ); // this is really a ULONG
  1672. }
  1673. IMCEExit:
  1674. if (hAclFile != INVALID_HANDLE_VALUE) {
  1675. CloseHandle( hAclFile );
  1676. if (deleteAclFile) {
  1677. // the file didn't get written properly, let's delete
  1678. aclFileName = (PWCHAR) ThreadContext->SDBuffer;
  1679. lstrcpyW( aclFileName, Dest );
  1680. lstrcatW( aclFileName, IMIRROR_ACL_STREAM_NAME );
  1681. DeleteFile( aclFileName );
  1682. }
  1683. }
  1684. if (errorCase == STATUS_RETRY) {
  1685. goto retryWriteStream;
  1686. }
  1687. if (errorCase == ERROR_SUCCESS) {
  1688. err = ERROR_SUCCESS;
  1689. }
  1690. return err;
  1691. }
  1692. DWORD
  1693. StoreOurSFNStream (
  1694. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1695. PWCHAR Source,
  1696. PWCHAR Dest,
  1697. PWCHAR ShortFileName
  1698. )
  1699. //
  1700. // This routine stores off the short file name from the master into a named
  1701. // alternate data stream on the destination.
  1702. //
  1703. {
  1704. PCOPY_TREE_CONTEXT copyContext;
  1705. DWORD err;
  1706. DWORD requiredLength;
  1707. DWORD ShortFileNameLength;
  1708. HANDLE hSFNFile;
  1709. PWCHAR SFNFileName;
  1710. ULONG action;
  1711. MIRROR_SFN_STREAM mirrorSFNStream;
  1712. DWORD BytesWritten;
  1713. BOOL deleteSFNFile;
  1714. DWORD errorCase;
  1715. retryWriteStream:
  1716. errorCase = STATUS_SUCCESS;
  1717. copyContext = ThreadContext->CopyContext;
  1718. err = ERROR_SUCCESS;
  1719. requiredLength = 0;
  1720. hSFNFile = INVALID_HANDLE_VALUE;
  1721. action = COPY_ERROR_ACTION_GETSFN;
  1722. deleteSFNFile = FALSE;
  1723. ShortFileNameLength = ((DWORD)wcslen(ShortFileName)+1)*sizeof(WCHAR);
  1724. //
  1725. // We use the SFNBuffer on the thread context to store the file name of the
  1726. // alternate data stream.
  1727. //
  1728. requiredLength = (lstrlenW( Dest ) + lstrlenW( IMIRROR_SFN_STREAM_NAME ) + 1) * sizeof(WCHAR);
  1729. if (requiredLength < ShortFileNameLength) {
  1730. requiredLength = ShortFileNameLength;
  1731. }
  1732. if (ThreadContext->SFNBuffer == NULL || (requiredLength > ThreadContext->SFNBufferLength)) {
  1733. if (ThreadContext->SFNBuffer != NULL) {
  1734. IMirrorFreeMem( ThreadContext->SFNBuffer );
  1735. ThreadContext->SFNBuffer = NULL;
  1736. }
  1737. if (requiredLength > ThreadContext->SFNBufferLength) {
  1738. ThreadContext->SFNBufferLength = requiredLength;
  1739. }
  1740. ThreadContext->SFNBuffer = IMirrorAllocMem( ThreadContext->SFNBufferLength );
  1741. if (ThreadContext->SFNBuffer == NULL) {
  1742. err = GetLastError();
  1743. errorCase = ReportCopyError( copyContext,
  1744. Source,
  1745. COPY_ERROR_ACTION_MALLOC,
  1746. err );
  1747. goto IMCEExit;
  1748. }
  1749. }
  1750. SFNFileName = (PWCHAR) ThreadContext->SFNBuffer;
  1751. lstrcpyW( SFNFileName, Dest );
  1752. lstrcatW( SFNFileName, IMIRROR_SFN_STREAM_NAME );
  1753. hSFNFile = CreateFile( SFNFileName,
  1754. GENERIC_WRITE,
  1755. 0, // Exclusive access.
  1756. NULL, // Default security descriptor.
  1757. CREATE_ALWAYS, // Overrides if file exists.
  1758. FILE_FLAG_BACKUP_SEMANTICS, // Open directories too.
  1759. NULL
  1760. );
  1761. if (hSFNFile == INVALID_HANDLE_VALUE) {
  1762. err = GetLastError();
  1763. errorCase = ReportCopyError( copyContext,
  1764. Source,
  1765. COPY_ERROR_ACTION_CREATE_FILE,
  1766. err );
  1767. goto IMCEExit;
  1768. }
  1769. mirrorSFNStream.StreamVersion = IMIRROR_SFN_STREAM_VERSION;
  1770. mirrorSFNStream.StreamLength = sizeof( MIRROR_SFN_STREAM ) + ShortFileNameLength;
  1771. if ((WriteFile( hSFNFile,
  1772. &mirrorSFNStream,
  1773. sizeof( MIRROR_SFN_STREAM ),
  1774. &BytesWritten,
  1775. NULL // No overlap.
  1776. ) == FALSE) ||
  1777. (BytesWritten < sizeof( MIRROR_SFN_STREAM ))) {
  1778. deleteSFNFile = TRUE;
  1779. err = GetLastError();
  1780. errorCase = ReportCopyError( copyContext,
  1781. Source,
  1782. COPY_ERROR_ACTION_SETSFN,
  1783. err );
  1784. goto IMCEExit;
  1785. }
  1786. if ((WriteFile( hSFNFile,
  1787. ShortFileName,
  1788. ShortFileNameLength,
  1789. &BytesWritten,
  1790. NULL // No overlap.
  1791. ) == FALSE) ||
  1792. (BytesWritten < ShortFileNameLength )) {
  1793. deleteSFNFile = TRUE;
  1794. err = GetLastError();
  1795. errorCase = ReportCopyError( copyContext,
  1796. Source,
  1797. COPY_ERROR_ACTION_SETSFN,
  1798. err );
  1799. goto IMCEExit;
  1800. }
  1801. InterlockedIncrement( (PLONG)&copyContext->SFNWritten );// this is really a ULONG
  1802. IMCEExit:
  1803. if (hSFNFile != INVALID_HANDLE_VALUE) {
  1804. CloseHandle( hSFNFile );
  1805. if (deleteSFNFile) {
  1806. // the file didn't get written properly, let's delete
  1807. SFNFileName = (PWCHAR) ThreadContext->SFNBuffer;
  1808. lstrcpyW( SFNFileName, Dest );
  1809. lstrcatW( SFNFileName, IMIRROR_SFN_STREAM_NAME );
  1810. DeleteFile( SFNFileName );
  1811. }
  1812. }
  1813. if (errorCase == STATUS_RETRY) {
  1814. goto retryWriteStream;
  1815. }
  1816. if (errorCase == ERROR_SUCCESS) {
  1817. err = ERROR_SUCCESS;
  1818. }
  1819. return err;
  1820. }
  1821. DWORD
  1822. GetOurSFNStream (
  1823. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1824. PWCHAR Dest,
  1825. PMIRROR_SFN_STREAM MirrorSFNStream,
  1826. PWCHAR SFNBuffer,
  1827. DWORD SFNBufferSize
  1828. )
  1829. //
  1830. // This routine reads the short filename stream header from the destination. We do this
  1831. // to get the fields out of it so that we can determine if it needs updating.
  1832. //
  1833. {
  1834. DWORD err = ERROR_SUCCESS;
  1835. DWORD requiredLength = 0;
  1836. HANDLE hSFNFile = INVALID_HANDLE_VALUE;
  1837. PWCHAR SFNFileName;
  1838. DWORD BytesRead;
  1839. //
  1840. // We use the SFNBuffer on the thread context to store not only the
  1841. // security descriptor but also the file name of the alternate data stream.
  1842. //
  1843. if (!Dest || *Dest == L'\0') {
  1844. err = ERROR_INVALID_PARAMETER;
  1845. goto IMCEExit;
  1846. }
  1847. requiredLength = (lstrlenW( Dest ) + lstrlenW( IMIRROR_SFN_STREAM_NAME ) + 1) * sizeof(WCHAR);
  1848. if (ThreadContext->SFNBuffer == NULL || requiredLength > ThreadContext->SFNBufferLength) {
  1849. if (ThreadContext->SFNBuffer != NULL) {
  1850. IMirrorFreeMem( ThreadContext->SFNBuffer );
  1851. ThreadContext->SFNBuffer = NULL;
  1852. ThreadContext->SFNBufferLength = requiredLength;
  1853. }
  1854. if (requiredLength > ThreadContext->SFNBufferLength) {
  1855. ThreadContext->SFNBufferLength = requiredLength;
  1856. }
  1857. ThreadContext->SFNBuffer = IMirrorAllocMem( ThreadContext->SFNBufferLength );
  1858. if (ThreadContext->SFNBuffer == NULL) {
  1859. err = GetLastError();
  1860. goto IMCEExit;
  1861. }
  1862. }
  1863. SFNFileName = (PWCHAR) ThreadContext->SFNBuffer;
  1864. lstrcpyW( SFNFileName, Dest );
  1865. lstrcatW( SFNFileName, IMIRROR_SFN_STREAM_NAME );
  1866. hSFNFile = CreateFile( SFNFileName,
  1867. GENERIC_READ,
  1868. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1869. NULL, // Default security descriptor.
  1870. OPEN_EXISTING,
  1871. 0, // no special attributes
  1872. NULL
  1873. );
  1874. if (hSFNFile == INVALID_HANDLE_VALUE) {
  1875. err = GetLastError();
  1876. goto IMCEExit;
  1877. }
  1878. if ((ReadFile( hSFNFile,
  1879. MirrorSFNStream,
  1880. sizeof( MIRROR_SFN_STREAM ),
  1881. &BytesRead,
  1882. NULL // No overlap.
  1883. ) == FALSE) ||
  1884. (BytesRead < sizeof( MIRROR_SFN_STREAM )) ||
  1885. (MirrorSFNStream->StreamVersion != IMIRROR_SFN_STREAM_VERSION) ||
  1886. (MirrorSFNStream->StreamLength < sizeof( MIRROR_SFN_STREAM ))) {
  1887. err = ERROR_INVALID_DATA;
  1888. }
  1889. if ((MirrorSFNStream->StreamLength - sizeof(MIRROR_SFN_STREAM)) > SFNBufferSize) {
  1890. err = ERROR_INSUFFICIENT_BUFFER;
  1891. } else {
  1892. if ((ReadFile( hSFNFile,
  1893. SFNBuffer,
  1894. MirrorSFNStream->StreamLength - sizeof(MIRROR_SFN_STREAM),
  1895. &BytesRead,
  1896. NULL ) == FALSE) ||
  1897. (BytesRead != (MirrorSFNStream->StreamLength - sizeof(MIRROR_SFN_STREAM)))) {
  1898. err = ERROR_INVALID_DATA;
  1899. }
  1900. }
  1901. IMCEExit:
  1902. if (hSFNFile != INVALID_HANDLE_VALUE) {
  1903. CloseHandle( hSFNFile );
  1904. }
  1905. return err;
  1906. }
  1907. DWORD
  1908. GetOurSecurityStream (
  1909. PIMIRROR_THREAD_CONTEXT ThreadContext,
  1910. PWCHAR Dest,
  1911. PMIRROR_ACL_STREAM MirrorAclStream
  1912. )
  1913. //
  1914. // This routine reads the stream header from the destination. We do this
  1915. // to get the fields out of it so that we can determine if it needs updating.
  1916. //
  1917. {
  1918. DWORD err = ERROR_SUCCESS;
  1919. DWORD requiredLength = 0;
  1920. HANDLE hAclFile = INVALID_HANDLE_VALUE;
  1921. PWCHAR aclFileName;
  1922. DWORD BytesRead;
  1923. //
  1924. // We use the SDuffer on the thread context to store not only the
  1925. // security descriptor but also the file name of the alternate data stream.
  1926. //
  1927. if (!Dest || *Dest == L'\0') {
  1928. err = ERROR_INVALID_PARAMETER;
  1929. goto IMCEExit;
  1930. }
  1931. requiredLength = (lstrlenW( Dest ) + lstrlenW( IMIRROR_ACL_STREAM_NAME ) + 1) * sizeof(WCHAR);
  1932. if (ThreadContext->SDBuffer == NULL || requiredLength > ThreadContext->SDBufferLength) {
  1933. if (ThreadContext->SDBuffer != NULL) {
  1934. IMirrorFreeMem( ThreadContext->SDBuffer );
  1935. ThreadContext->SDBuffer = NULL;
  1936. ThreadContext->SDBufferLength = requiredLength;
  1937. }
  1938. if (requiredLength > ThreadContext->SDBufferLength) {
  1939. ThreadContext->SDBufferLength = requiredLength;
  1940. }
  1941. ThreadContext->SDBuffer = IMirrorAllocMem( ThreadContext->SDBufferLength );
  1942. if (ThreadContext->SDBuffer == NULL) {
  1943. err = GetLastError();
  1944. goto IMCEExit;
  1945. }
  1946. }
  1947. aclFileName = (PWCHAR) ThreadContext->SDBuffer;
  1948. lstrcpyW( aclFileName, Dest );
  1949. lstrcatW( aclFileName, IMIRROR_ACL_STREAM_NAME );
  1950. hAclFile = CreateFile( aclFileName,
  1951. GENERIC_READ,
  1952. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1953. NULL, // Default security descriptor.
  1954. OPEN_EXISTING,
  1955. 0, // no special attributes
  1956. NULL
  1957. );
  1958. if (hAclFile == INVALID_HANDLE_VALUE) {
  1959. err = GetLastError();
  1960. goto IMCEExit;
  1961. }
  1962. //
  1963. // read the header of the stream. We don't bother reading the security
  1964. // descriptor because all we need is the ChangeTime (which changes with
  1965. // the security descriptor).
  1966. //
  1967. if ((ReadFile( hAclFile,
  1968. MirrorAclStream,
  1969. sizeof( MIRROR_ACL_STREAM ),
  1970. &BytesRead,
  1971. NULL // No overlap.
  1972. ) == FALSE) ||
  1973. (BytesRead < sizeof( MIRROR_ACL_STREAM )) ||
  1974. (MirrorAclStream->StreamVersion != IMIRROR_ACL_STREAM_VERSION) ||
  1975. (MirrorAclStream->StreamLength < sizeof( MIRROR_ACL_STREAM ))) {
  1976. err = ERROR_INVALID_DATA;
  1977. }
  1978. IMCEExit:
  1979. if (hAclFile != INVALID_HANDLE_VALUE) {
  1980. CloseHandle( hAclFile );
  1981. }
  1982. return err;
  1983. }
  1984. ULONG
  1985. ReportCopyError (
  1986. PCOPY_TREE_CONTEXT CopyContext OPTIONAL,
  1987. PWCHAR File,
  1988. DWORD ActionCode,
  1989. DWORD Err
  1990. )
  1991. //
  1992. // This returns either ERROR_SUCCESS, STATUS_RETRY, or STATUS_REQUEST_ABORTED
  1993. //
  1994. // ERROR_SUCCESS means we just continue on and ignore the error.
  1995. // STATUS_RETRY means we retry the operation
  1996. // STATUS_REQUEST_ABORTED means we bail.
  1997. //
  1998. {
  1999. NTSTATUS Status = STATUS_SUCCESS;
  2000. ULONG ReturnCode = ERROR_SUCCESS;
  2001. if (CopyContext != NULL) {
  2002. if (Err != ERROR_SUCCESS) {
  2003. InterlockedIncrement( (PLONG)&CopyContext->ErrorsEncountered );// this is really a ULONG
  2004. }
  2005. }
  2006. if (Callbacks.FileCreateFn == NULL) {
  2007. if (Err != ERROR_SUCCESS) {
  2008. if (ActionCode == COPY_ERROR_ACTION_DELETE) {
  2009. printf( "error %u while deleting %S\n", Err, File );
  2010. } else {
  2011. printf( "error %u while copying %S\n", Err, File );
  2012. }
  2013. } else {
  2014. if (ActionCode == COPY_ERROR_ACTION_DELETE) {
  2015. printf( "deleted %S\n", File );
  2016. } else {
  2017. printf( "copied %S\n", File );
  2018. }
  2019. }
  2020. }
  2021. if (Err != STATUS_SUCCESS) {
  2022. Status = IMirrorFileCreate(File, ActionCode, Err);
  2023. if (Status == STATUS_REQUEST_ABORTED) {
  2024. CopyContext->Cancelled = TRUE;
  2025. } else if ((Status != STATUS_RETRY) &&
  2026. (Status != STATUS_SUCCESS)) {
  2027. Status = STATUS_SUCCESS;
  2028. }
  2029. ReturnCode = (ULONG) Status;
  2030. } else {
  2031. ReturnCode = ERROR_SUCCESS;
  2032. }
  2033. return(ReturnCode);
  2034. }
  2035. NTSTATUS
  2036. SetPrivs(
  2037. IN HANDLE TokenHandle,
  2038. IN LPTSTR lpszPriv
  2039. )
  2040. /*++
  2041. Routine Description:
  2042. This routine enables the given privilege in the given token.
  2043. Arguments:
  2044. Return Value:
  2045. FALSE - Failure.
  2046. TRUE - Success.
  2047. --*/
  2048. {
  2049. LUID SetPrivilegeValue;
  2050. TOKEN_PRIVILEGES TokenPrivileges;
  2051. //
  2052. // First, find out the value of the privilege
  2053. //
  2054. if (!LookupPrivilegeValue(NULL, lpszPriv, &SetPrivilegeValue)) {
  2055. return GetLastError();
  2056. }
  2057. //
  2058. // Set up the privilege set we will need
  2059. //
  2060. TokenPrivileges.PrivilegeCount = 1;
  2061. TokenPrivileges.Privileges[0].Luid = SetPrivilegeValue;
  2062. TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  2063. if (!AdjustTokenPrivileges( TokenHandle,
  2064. FALSE,
  2065. &TokenPrivileges,
  2066. sizeof(TOKEN_PRIVILEGES),
  2067. NULL,
  2068. NULL)) {
  2069. return GetLastError();
  2070. }
  2071. return ERROR_SUCCESS;
  2072. }
  2073. NTSTATUS
  2074. GetTokenHandle(
  2075. IN OUT PHANDLE TokenHandle
  2076. )
  2077. /*++
  2078. Routine Description:
  2079. This routine opens the current process object and returns a
  2080. handle to its token.
  2081. Arguments:
  2082. Return Value:
  2083. NTSTATUS
  2084. --*/
  2085. {
  2086. HANDLE ProcessHandle;
  2087. NTSTATUS Result;
  2088. ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION,
  2089. FALSE,
  2090. GetCurrentProcessId());
  2091. if (ProcessHandle == NULL) {
  2092. return GetLastError();
  2093. }
  2094. Result = OpenProcessToken(ProcessHandle,
  2095. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  2096. TokenHandle);
  2097. CloseHandle(ProcessHandle);
  2098. if (Result) {
  2099. Result = ERROR_SUCCESS;
  2100. } else {
  2101. Result = GetLastError();
  2102. }
  2103. return Result;
  2104. }
  2105. NTSTATUS
  2106. CanHandleReparsePoint (
  2107. PIMIRROR_THREAD_CONTEXT ThreadContext,
  2108. PWCHAR SourceFileName,
  2109. DWORD FileAttributes
  2110. )
  2111. //
  2112. // This routine checks the type of reparse point a file is. If it is a
  2113. // reparse point we can handle (e.g. a structured storage document) then
  2114. // return success. Otherwise we return the appropriate error.
  2115. //
  2116. {
  2117. UNREFERENCED_PARAMETER(ThreadContext);
  2118. UNREFERENCED_PARAMETER(SourceFileName);
  2119. UNREFERENCED_PARAMETER(FileAttributes);
  2120. return(ERROR_REPARSE_ATTRIBUTE_CONFLICT);
  2121. }