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.

1136 lines
38 KiB

  1. #include "stdinc.h"
  2. #include "FusionBuffer.h"
  3. #include "Util.h"
  4. #include "FusionHandle.h"
  5. #define SXSP_MOVE_FILE_FLAG_COMPRESSION_AWARE 1
  6. BOOL
  7. SxspDoesFileExist(
  8. DWORD dwFlags,
  9. PCWSTR pszFileName,
  10. bool &rfExists
  11. )
  12. {
  13. BOOL fSuccess = FALSE;
  14. FN_TRACE_WIN32(fSuccess);
  15. const bool fCheckFileOnly = ((dwFlags & SXSP_DOES_FILE_EXIST_FLAG_CHECK_FILE_ONLY) != 0);
  16. const bool fCheckDirectoryOnly = ((dwFlags & SXSP_DOES_FILE_EXIST_FLAG_CHECK_DIRECTORY_ONLY) != 0);
  17. DWORD dwFileOrDirectoryExists = 0;
  18. DWORD dwFlags2 = 0;
  19. if (&rfExists != NULL)
  20. {
  21. rfExists = false;
  22. }
  23. PARAMETER_CHECK(pszFileName != NULL);
  24. PARAMETER_CHECK(&rfExists != NULL);
  25. PARAMETER_CHECK((dwFlags & ~(SXSP_DOES_FILE_EXIST_FLAG_COMPRESSION_AWARE | SXSP_DOES_FILE_EXIST_FLAG_INCLUDE_NETWORK_ERRORS | SXSP_DOES_FILE_EXIST_FLAG_CHECK_DIRECTORY_ONLY | SXSP_DOES_FILE_EXIST_FLAG_CHECK_FILE_ONLY)) == 0);
  26. //
  27. // one or neither of these can be set, but not both
  28. //
  29. PARAMETER_CHECK(!(fCheckFileOnly && fCheckDirectoryOnly));
  30. if ((dwFlags & SXSP_DOES_FILE_EXIST_FLAG_COMPRESSION_AWARE) != 0)
  31. dwFlags2 |= SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_COMPRESSION_AWARE;
  32. if ((dwFlags & SXSP_DOES_FILE_EXIST_FLAG_INCLUDE_NETWORK_ERRORS) != 0)
  33. dwFlags2 |= SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_INCLUDE_NETWORK_ERRORS;
  34. IFW32FALSE_EXIT(SxspDoesFileOrDirectoryExist(dwFlags2, pszFileName, dwFileOrDirectoryExists));
  35. if (fCheckFileOnly)
  36. {
  37. rfExists = (dwFileOrDirectoryExists == SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_FILE_EXISTS);
  38. }
  39. else if (fCheckDirectoryOnly)
  40. {
  41. rfExists = (dwFileOrDirectoryExists == SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_DIRECTORY_EXISTS);
  42. }
  43. else
  44. {
  45. rfExists = (dwFileOrDirectoryExists != SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_NEITHER_EXISTS);
  46. }
  47. fSuccess = TRUE;
  48. Exit:
  49. return fSuccess;
  50. }
  51. BOOL
  52. SxspDoesFileOrDirectoryExist(
  53. DWORD dwFlags,
  54. PCWSTR pszFileName,
  55. OUT DWORD &rdwDisposition
  56. )
  57. {
  58. BOOL fSuccess = FALSE;
  59. FN_TRACE_WIN32(fSuccess);
  60. PWSTR pszActualSource = NULL;
  61. DWORD dwFileOrDirectoryExists = 0;
  62. if (&rdwDisposition != NULL)
  63. {
  64. rdwDisposition = SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_NEITHER_EXISTS;
  65. }
  66. PARAMETER_CHECK(&rdwDisposition != NULL);
  67. PARAMETER_CHECK(pszFileName != NULL);
  68. PARAMETER_CHECK((dwFlags & ~(SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_COMPRESSION_AWARE | SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_INCLUDE_NETWORK_ERRORS)) == 0);
  69. if (dwFlags & SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_COMPRESSION_AWARE)
  70. {
  71. DWORD dwTemp = 0;
  72. DWORD dwSourceFileSize = 0;
  73. DWORD dwTargetFileSize = 0;
  74. UINT uiCompressionType = 0;
  75. dwTemp = ::SetupGetFileCompressionInfoW(
  76. pszFileName,
  77. &pszActualSource,
  78. &dwSourceFileSize,
  79. &dwTargetFileSize,
  80. &uiCompressionType);
  81. if (pszActualSource != NULL)
  82. {
  83. ::LocalFree((HLOCAL) pszActualSource);
  84. pszActualSource = NULL;
  85. }
  86. //
  87. // don't care about ERROR_PATH_NOT_FOUND or network errors here?
  88. //
  89. if (dwTemp == ERROR_FILE_NOT_FOUND)
  90. {
  91. // This case is OK. No error to return...
  92. }
  93. else if (dwTemp != ERROR_SUCCESS)
  94. {
  95. ORIGINATE_WIN32_FAILURE_AND_EXIT(SetupGetFileCompressionInfoW, dwTemp);
  96. }
  97. else
  98. {
  99. rdwDisposition = SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_FILE_EXISTS;
  100. }
  101. }
  102. else
  103. {
  104. const DWORD dwAttribute = ::GetFileAttributesW(pszFileName);
  105. if (dwAttribute == INVALID_FILE_ATTRIBUTES)
  106. {
  107. const DWORD dwLastError = ::FusionpGetLastWin32Error();
  108. const bool fUseNetwork = ((dwFlags & SXSP_DOES_FILE_OR_DIRECTORY_EXIST_FLAG_INCLUDE_NETWORK_ERRORS) != 0);
  109. //
  110. // Apologies for the wierd logic, but this was simpler to write.
  111. //
  112. if ((dwLastError == ERROR_SUCCESS) ||
  113. (dwLastError == ERROR_FILE_NOT_FOUND) ||
  114. (dwLastError == ERROR_PATH_NOT_FOUND) ||
  115. (fUseNetwork && (dwLastError == ERROR_BAD_NETPATH)) ||
  116. (fUseNetwork && (dwLastError == ERROR_BAD_NET_NAME)))
  117. {
  118. //
  119. // ok, do nothing
  120. //
  121. }
  122. else
  123. {
  124. ORIGINATE_WIN32_FAILURE_AND_EXIT(GetFileAttributesW, dwLastError);
  125. }
  126. }
  127. else
  128. {
  129. if ((dwAttribute & FILE_ATTRIBUTE_DIRECTORY) != 0)
  130. {
  131. rdwDisposition = SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_DIRECTORY_EXISTS;
  132. }
  133. else
  134. {
  135. rdwDisposition = SXSP_DOES_FILE_OR_DIRECTORY_EXIST_DISPOSITION_FILE_EXISTS;
  136. }
  137. }
  138. }
  139. fSuccess = TRUE;
  140. Exit:
  141. if (pszActualSource != NULL)
  142. {
  143. CSxsPreserveLastError ple;
  144. ::LocalFree((HLOCAL) pszActualSource);
  145. ple.Restore();
  146. }
  147. return fSuccess;
  148. }
  149. // NTRAID#NTBUG9 - 589828 - 2002/03/26 - xiaoyuw:
  150. // the current implementation assumes that the input path always begins with "c:\" or "\\machinename"
  151. // So if we want to support path beginning with "\\?\", more code need added....
  152. BOOL
  153. FusionpCreateDirectories(
  154. PCWSTR pszDirectory,
  155. SIZE_T cchDirectory
  156. )
  157. /*-----------------------------------------------------------------------------
  158. like ::CreateDirectoryW, but will create the parent directories as needed;
  159. origin of this code
  160. \\lang5\V5.PRO\src\ide5\shell\path.cpp ("MakeDirectory")
  161. \\kingbird\vseedev\src\vsee98\vsee\pkgs\scc\path.cpp ("MakeDirectory")
  162. then ported to \\kingbird\vseedev\src\vsee70\pkgs\scc\path.cpp ("MakeDirectory")
  163. then moved to \vsee\lib\io\io.cpp, converted to use exceptions ("NVseeLibIo::FCreateDirectories")
  164. then copied to fusion\dll\whistler\util.cpp, exceptions converted to BOOL/LastError ("SxspCreateDirectories")
  165. -----------------------------------------------------------------------------*/
  166. {
  167. BOOL fSuccess = FALSE;
  168. FN_TRACE_WIN32(fSuccess);
  169. CStringBuffer strBuffer;
  170. DWORD dwAttribs = 0;
  171. PARAMETER_CHECK(pszDirectory != NULL);
  172. PARAMETER_CHECK(cchDirectory != 0);
  173. IFW32FALSE_EXIT(strBuffer.Win32Assign(pszDirectory, cchDirectory));
  174. //::CreateDirectoryW will do the wrong thing if strBuffer has a trailing slash,
  175. //so we'll strip it off if it's there. (see bug VS7:31319) [MSantoro]
  176. IFW32FALSE_EXIT(strBuffer.Win32RemoveTrailingPathSeparators());
  177. // cover the two common cases of its parent exists or it exists
  178. if ((!::CreateDirectoryW(strBuffer, NULL)) && (::FusionpGetLastWin32Error() != ERROR_ALREADY_EXISTS))
  179. {
  180. CStringBufferAccessor sbaBuffer;
  181. // now the slow path
  182. //
  183. // Try to create the subdirectories (if any) named in the path.
  184. //
  185. sbaBuffer.Attach(&strBuffer);
  186. WCHAR* pStart = sbaBuffer.GetBufferPtr();
  187. WCHAR* pCurr = pStart;
  188. // skip the leading drive or \\computer\share
  189. // this way we don't try to create C: in trying to create C:\
  190. // or \\computer\share in trying to create \\computer\share\dir
  191. // FUTURE This is not ideal.. (need NVseeLibPath)
  192. if (pCurr[0] != 0)
  193. {
  194. const static WCHAR rgchAZaz[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  195. C_ASSERT(NUMBER_OF(rgchAZaz) == 53);
  196. if ((pCurr[1] == L':') &&
  197. CUnicodeCharTraits::IsPathSeparator(pCurr[2]) &&
  198. (wcschr(rgchAZaz, pCurr[0]) != NULL))
  199. {
  200. pCurr += 3;
  201. }
  202. else if (CUnicodeCharTraits::IsPathSeparator(pCurr[0]) &&
  203. CUnicodeCharTraits::IsPathSeparator(pCurr[1]))
  204. {
  205. // skip to after the share, since we presumably can't create shares with CreateDirectory
  206. pCurr += wcsspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip leading two slashes
  207. pCurr += wcscspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip computer name
  208. pCurr += wcsspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip slashes after computer name
  209. pCurr += wcscspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip share name
  210. pCurr += wcsspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip slashes after share name
  211. }
  212. }
  213. while (*pCurr != L'\0')
  214. {
  215. pCurr += wcscspn(pCurr, CUnicodeCharTraits::PathSeparators()); // skip to next slash
  216. if (*pCurr != 0)
  217. {
  218. // [a-JayK, JayKrell April 2000] Why not just assume it's a backslash?
  219. WCHAR chSaved = *pCurr;
  220. *pCurr = 0;
  221. if (!::CreateDirectoryW(pStart, NULL))
  222. {
  223. // In trying to create c:\foo\bar,
  224. // we try to create c:\foo, which fails, but is ok.
  225. const DWORD dwLastError = ::FusionpGetLastWin32Error();
  226. bool fExist;
  227. IFW32FALSE_EXIT(::SxspDoesFileExist(SXSP_DOES_FILE_EXIST_FLAG_CHECK_DIRECTORY_ONLY, pStart, fExist));
  228. if (!fExist)
  229. {
  230. ::SetLastError(ERROR_PATH_NOT_FOUND);
  231. goto Exit;
  232. }
  233. }
  234. *pCurr = chSaved;
  235. pCurr += 1;
  236. }
  237. }
  238. IFW32FALSE_ORIGINATE_AND_EXIT(::CreateDirectoryW(pStart, NULL));
  239. }
  240. //
  241. // Try again to see if the given directory exists and
  242. // return true if successful.
  243. //
  244. bool fExist;
  245. IFW32FALSE_EXIT(::SxspDoesFileExist(SXSP_DOES_FILE_EXIST_FLAG_CHECK_DIRECTORY_ONLY, strBuffer, fExist));
  246. if (!fExist)
  247. {
  248. ::SetLastError(ERROR_PATH_NOT_FOUND);
  249. goto Exit;
  250. }
  251. fSuccess = TRUE;
  252. Exit:
  253. return fSuccess;
  254. }
  255. VOID
  256. CFusionDirectoryDifference::DbgPrint(
  257. PCWSTR dir1,
  258. PCWSTR dir2
  259. )
  260. {
  261. #if DBG // { {
  262. switch (m_e)
  263. {
  264. case eEqual:
  265. ::FusionpDbgPrintEx(
  266. FUSION_DBG_LEVEL_ERROR,
  267. "SXS.DLL: The directories %ls and %ls match size-wise recursively\n",
  268. dir1,
  269. dir2);
  270. break;
  271. case eExtraOrMissingFile:
  272. ::FusionpDbgPrintEx(
  273. FUSION_DBG_LEVEL_ERROR,
  274. "SXS.DLL: The directories %ls and %ls mismatch, the file %ls is only in one of them.\n",
  275. dir1,
  276. dir2,
  277. static_cast<PCWSTR>(*m_pstrExtraOrMissingFile));
  278. break;
  279. case eMismatchedFileSize:
  280. ::FusionpDbgPrintEx(
  281. FUSION_DBG_LEVEL_ERROR,
  282. "SXS.DLL: The directories %ls and %ls mismatch, file:%ls, size:%I64d, file:%ls, size:%I64d.\n",
  283. dir1,
  284. dir2,
  285. static_cast<PCWSTR>(*m_pstrMismatchedSizeFile1),
  286. m_nMismatchedFileSize1,
  287. static_cast<PCWSTR>(*m_pstrMismatchedSizeFile2),
  288. m_nMismatchedFileSize2);
  289. break;
  290. case eMismatchedFileCount:
  291. ::FusionpDbgPrintEx(
  292. FUSION_DBG_LEVEL_ERROR,
  293. "SXS.DLL: The directories %ls and %ls mismatch in number of files,"
  294. "subdirectory %ls has %I64d files, subdirectory %ls has %I64d files\n",
  295. dir1,
  296. dir2,
  297. static_cast<PCWSTR>(*m_pstrMismatchedCountDir1),
  298. m_nMismatchedFileCount1,
  299. static_cast<PCWSTR>(*m_pstrMismatchedCountDir2),
  300. m_nMismatchedFileCount2);
  301. break;
  302. case eFileDirectoryMismatch:
  303. ::FusionpDbgPrintEx(
  304. FUSION_DBG_LEVEL_ERROR,
  305. "SXS.DLL: The directories %ls and %ls mismatch, "
  306. "%ls is a file, %ls is a directory.\n",
  307. dir1,
  308. dir2,
  309. static_cast<PCWSTR>(*m_pstrFile),
  310. static_cast<PCWSTR>(*m_pstrDirectory));
  311. break;
  312. }
  313. #endif // } }
  314. }
  315. /*-----------------------------------------------------------------------------*/
  316. // NTRAID#NTBUG9 - 589828 - 2002/03/26 - xiaoyuw:
  317. // if an input path containing both "\" and "/", the implementation would take it as
  318. // a valid path;
  319. int __cdecl
  320. CFusionFilePathAndSize::QsortComparePath(
  321. const void* pvx,
  322. const void* pvy
  323. )
  324. {
  325. const CFusionFilePathAndSize* px = reinterpret_cast<const CFusionFilePathAndSize*>(pvx);
  326. const CFusionFilePathAndSize* py = reinterpret_cast<const CFusionFilePathAndSize*>(pvy);
  327. int i =
  328. ::FusionpCompareStrings(
  329. px->m_path,
  330. px->m_path.Cch(),
  331. py->m_path,
  332. py->m_path.Cch(),
  333. TRUE);
  334. return i;
  335. }
  336. int __cdecl
  337. CFusionFilePathAndSize::QsortIndirectComparePath(
  338. const void* ppvx,
  339. const void* ppvy
  340. )
  341. {
  342. const void* pv = *reinterpret_cast<void const* const*>(ppvx);
  343. const void* py = *reinterpret_cast<void const* const*>(ppvy);
  344. int i = QsortComparePath(pv, py);
  345. return i;
  346. }
  347. /*-----------------------------------------------------------------------------
  348. See FusionpCompareDirectoriesSizewiseRecursively for what this does;
  349. this function exists to reduce the stack usage of
  350. FusionpCompareDirectoriesSizewiseRecursively.
  351. -----------------------------------------------------------------------------*/
  352. static BOOL
  353. FusionpCompareDirectoriesSizewiseRecursivelyHelper(
  354. CFusionDirectoryDifference *pResult,
  355. CBaseStringBuffer &rdir1,
  356. CBaseStringBuffer &rdir2,
  357. WIN32_FIND_DATAW &rwfd
  358. )
  359. {
  360. BOOL fSuccess = FALSE;
  361. FN_TRACE_WIN32(fSuccess);
  362. // either or both directories can be on FAT, we can't assume that FindFirstFile
  363. // returns entries in any particular order, so we first enumerate one directory
  364. // entirely, storing the leaf names in an array, sort the array, then
  365. // walk the second directory doing a binary search in the first array
  366. // if the file is not in the array, we have an extra on one side
  367. // we count the elements in both directories, if the counts don't match,
  368. // we have a mismatch
  369. typedef CFusionArray<CFusionFilePathAndSize> CDirEntries;
  370. CDirEntries dir1Entries;
  371. typedef CFusionArray<CFusionFilePathAndSize*> CIndirectDirEntries;
  372. CIndirectDirEntries indirectDir1Entries;
  373. CFusionFilePathAndSize* pFoundDirEntry = NULL;
  374. CFusionFilePathAndSize** ppFoundDirEntry = NULL;
  375. CFindFile findFile;
  376. const SIZE_T dirSlash1Length = rdir1.Cch();
  377. const SIZE_T dirSlash2Length = rdir2.Cch();
  378. CFusionFilePathAndSize pathAndSize;
  379. CFusionFilePathAndSize* pPathAndSize = &pathAndSize;
  380. INT count1 = 0; // seperate from the array, because this includes directories, and the array does not
  381. INT count2 = 0;
  382. DWORD dwAttributes = 0;
  383. IFW32FALSE_EXIT(rdir1.Win32Append(L"*", 1));
  384. IFW32FALSE_EXIT(findFile.Win32FindFirstFile(rdir1, &rwfd));
  385. do
  386. {
  387. if (FusionpIsDotOrDotDot(rwfd.cFileName))
  388. continue;
  389. ++count1;
  390. if ((rwfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  391. {
  392. rdir1.Left(dirSlash1Length);
  393. rdir2.Left(dirSlash2Length);
  394. IFW32FALSE_EXIT(rdir1.Win32Append(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  395. IFW32FALSE_EXIT(rdir1.Win32EnsureTrailingPathSeparator());
  396. IFW32FALSE_EXIT(rdir2.Win32Append(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  397. bool fExist;
  398. IFW32FALSE_EXIT(SxspDoesFileExist(0, rdir2, fExist));
  399. if (!fExist)
  400. {
  401. IFW32FALSE_EXIT(pResult->m_str1.Win32Assign(rdir1, dirSlash1Length));
  402. IFW32FALSE_EXIT(pResult->m_str1.Win32Append(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  403. pResult->m_e = CFusionDirectoryDifference::eExtraOrMissingFile;
  404. fSuccess = TRUE;
  405. goto Exit;
  406. }
  407. IFW32FALSE_EXIT(SxspGetFileAttributesW(rdir2, dwAttributes));
  408. if ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  409. {
  410. SIZE_T cchTemp = ::wcslen(rwfd.cFileName);
  411. IFW32FALSE_EXIT(pResult->m_pstrDirectory->Win32Assign(rdir1, dirSlash1Length));
  412. IFW32FALSE_EXIT(pResult->m_pstrDirectory->Win32Append(rwfd.cFileName, cchTemp));
  413. IFW32FALSE_EXIT(pResult->m_pstrFile->Win32Assign(rdir2, dirSlash2Length));
  414. IFW32FALSE_EXIT(pResult->m_pstrFile->Win32Append(rwfd.cFileName, cchTemp));
  415. pResult->m_e = CFusionDirectoryDifference::eFileDirectoryMismatch;
  416. fSuccess = TRUE;
  417. goto Exit;
  418. }
  419. IFW32FALSE_EXIT(rdir2.Win32EnsureTrailingPathSeparator());
  420. IFW32FALSE_EXIT(
  421. ::FusionpCompareDirectoriesSizewiseRecursivelyHelper(
  422. pResult,
  423. rdir1,
  424. rdir2,
  425. rwfd));
  426. if (pResult->m_e != CFusionDirectoryDifference::eEqual)
  427. {
  428. fSuccess = TRUE;
  429. goto Exit;
  430. }
  431. }
  432. else
  433. {
  434. IFW32FALSE_EXIT(pathAndSize.m_path.Win32Assign(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  435. pathAndSize.m_size = ::FusionpFileSizeFromFindData(rwfd);
  436. IFW32FALSE_EXIT(dir1Entries.Win32Append(pathAndSize));
  437. }
  438. } while (FindNextFileW(findFile, &rwfd));
  439. if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
  440. {
  441. goto Exit;
  442. }
  443. // dir1Entries cannot be sorted directly because it contains CStringBuffers.
  444. // first initialize the index to be an identity
  445. IFW32FALSE_EXIT(indirectDir1Entries.Win32SetSize(dir1Entries.GetSize()));
  446. ULONG i;
  447. for (i = 0 ; i != dir1Entries.GetSize() ; ++i)
  448. {
  449. indirectDir1Entries[i] = &dir1Entries[i];
  450. }
  451. qsort(
  452. &*indirectDir1Entries.Begin(),
  453. indirectDir1Entries.GetSize(),
  454. sizeof(CIndirectDirEntries::ValueType),
  455. CFusionFilePathAndSize::QsortIndirectComparePath);
  456. IFW32FALSE_EXIT(findFile.Win32Close());
  457. rdir2.Left(dirSlash2Length);
  458. IFW32FALSE_EXIT(rdir2.Win32Append(L"*", 1));
  459. IFW32FALSE_EXIT(findFile.Win32FindFirstFile(rdir2, &rwfd));
  460. do
  461. {
  462. if (::FusionpIsDotOrDotDot(rwfd.cFileName))
  463. continue;
  464. ++count2;
  465. if ((rwfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  466. continue;
  467. IFW32FALSE_EXIT(pathAndSize.m_path.Win32Assign(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  468. pathAndSize.m_size = ::FusionpFileSizeFromFindData(rwfd);
  469. ppFoundDirEntry = reinterpret_cast<CFusionFilePathAndSize**>(::bsearch(
  470. &pPathAndSize,
  471. &*indirectDir1Entries.Begin(),
  472. indirectDir1Entries.GetSize(),
  473. sizeof(CIndirectDirEntries::ValueType),
  474. CFusionFilePathAndSize::QsortIndirectComparePath));
  475. pFoundDirEntry = (ppFoundDirEntry != NULL) ? *ppFoundDirEntry : NULL;
  476. if (pFoundDirEntry == NULL)
  477. {
  478. IFW32FALSE_EXIT(pResult->m_str1.Win32Assign(rdir2, dirSlash2Length));
  479. IFW32FALSE_EXIT(pResult->m_str1.Win32Append(rwfd.cFileName, ::wcslen(rwfd.cFileName)));
  480. pResult->m_e = CFusionDirectoryDifference::eExtraOrMissingFile;
  481. fSuccess = TRUE;
  482. goto Exit;
  483. }
  484. if (pFoundDirEntry->m_size != pathAndSize.m_size)
  485. {
  486. SIZE_T cchTemp = ::wcslen(rwfd.cFileName);
  487. IFW32FALSE_EXIT(pResult->m_str1.Win32Assign(rdir1, dirSlash1Length));
  488. IFW32FALSE_EXIT(pResult->m_str1.Win32Append(rwfd.cFileName, cchTemp));
  489. pResult->m_nMismatchedFileSize1 = pFoundDirEntry->m_size;
  490. IFW32FALSE_EXIT(pResult->m_str2.Win32Assign(rdir2, dirSlash2Length));
  491. IFW32FALSE_EXIT(pResult->m_str2.Win32Append(rwfd.cFileName, cchTemp));
  492. pResult->m_nMismatchedFileSize2 = pathAndSize.m_size;
  493. pResult->m_e = CFusionDirectoryDifference::eMismatchedFileSize;
  494. fSuccess = TRUE;
  495. goto Exit;
  496. }
  497. } while (::FindNextFileW(findFile, &rwfd));
  498. if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
  499. goto Exit;
  500. if (count1 != count2)
  501. {
  502. IFW32FALSE_EXIT(pResult->m_str1.Win32Assign(rdir1, dirSlash1Length - 1));
  503. IFW32FALSE_EXIT(pResult->m_str2.Win32Assign(rdir2, dirSlash2Length - 1));
  504. pResult->m_nMismatchedFileCount1 = count1;
  505. pResult->m_nMismatchedFileCount2 = count2;
  506. pResult->m_e = CFusionDirectoryDifference::eMismatchedFileCount;
  507. fSuccess = TRUE;
  508. goto Exit;
  509. }
  510. IFW32FALSE_EXIT(findFile.Win32Close());
  511. pResult->m_e = CFusionDirectoryDifference::eEqual;
  512. fSuccess = TRUE;
  513. Exit:
  514. // restore the paths for our caller
  515. rdir1.Left(dirSlash1Length);
  516. rdir2.Left(dirSlash2Length);
  517. return fSuccess;
  518. }
  519. /*-----------------------------------------------------------------------------
  520. walk dirSlash1 and dirSlash2 recursively
  521. for each file in either tree, see if it is in the other tree
  522. at the same analogous position, and has the same size
  523. if all files are present in both trees, no extra in either tree,
  524. all with same size, return true
  525. if any files are in one tree but not the other, or vice versa, or any
  526. sizes mis match, return false
  527. the algorithm short circuits
  528. but it also does a depth first recursion
  529. -----------------------------------------------------------------------------*/
  530. BOOL
  531. FusionpCompareDirectoriesSizewiseRecursively(
  532. CFusionDirectoryDifference* pResult,
  533. const CBaseStringBuffer &rdir1,
  534. const CBaseStringBuffer &rdir2
  535. )
  536. {
  537. /*
  538. security issue marker
  539. large frame -- over 1500 bytes
  540. and worse than that, indefinite recursion
  541. */
  542. BOOL fSuccess = FALSE;
  543. FN_TRACE_WIN32(fSuccess);
  544. // only hog one stack frame with these large variables, rather than
  545. // putting them in the recursive function
  546. WIN32_FIND_DATAW wfd = {0};
  547. CStringBuffer mutableDir1;
  548. CStringBuffer mutableDir2;
  549. pResult->m_e = pResult->eEqual;
  550. IFW32FALSE_EXIT(mutableDir1.Win32Assign(rdir1, rdir1.Cch()));
  551. IFW32FALSE_EXIT(mutableDir1.Win32EnsureTrailingPathSeparator());
  552. IFW32FALSE_EXIT(mutableDir2.Win32Assign(rdir2, rdir2.Cch()));
  553. IFW32FALSE_EXIT(mutableDir2.Win32EnsureTrailingPathSeparator());
  554. // if either directory is a subdirectory of the other,
  555. // (or a subdir of a subdir, any generation descendant)
  556. // return an error; we could also interpret this as unequal,
  557. // since they can't be equal, or we could do the comparison
  558. // but not recurse on the subdir that is also a root;
  559. //
  560. // must do this check after the slashes are in place, because
  561. // "c:\food" is not a subdir of "c:\foo", but "c:\foo\d" is a subdir of "c:\foo\"
  562. // (quotes avoid backslash line continuation)
  563. PARAMETER_CHECK(_wcsnicmp(mutableDir1, mutableDir2, mutableDir1.Cch()) != 0);
  564. PARAMETER_CHECK(_wcsnicmp(mutableDir1, mutableDir2, mutableDir2.Cch()) != 0);
  565. IFW32FALSE_EXIT(
  566. ::FusionpCompareDirectoriesSizewiseRecursivelyHelper(
  567. pResult,
  568. mutableDir1,
  569. mutableDir2,
  570. wfd));
  571. fSuccess = TRUE;
  572. Exit:
  573. return fSuccess;
  574. }
  575. static BOOL
  576. IsStarOrStarDotStar(
  577. PCWSTR str
  578. )
  579. {
  580. // NTRAID#NTBUG9 - 589828 - 2002/03/26 - xiaoyuw:
  581. // better use WCHAR instead of CHAR for the following char constant.
  582. return (str[0] == '*'
  583. && (str[1] == 0 || (str[1] == '.' && str[2] == '*' && str[3] == 0)));
  584. }
  585. CDirWalk::ECallbackResult
  586. CDirWalk::WalkHelper(
  587. )
  588. {
  589. #if DBG
  590. #define SET_LINE() Line = __LINE__
  591. ULONG Line = 0;
  592. #else
  593. #define SET_LINE() /* nothing */
  594. #endif
  595. const PCWSTR* fileFilter = NULL;
  596. BOOL fGotAll = FALSE;
  597. BOOL fThisIsAll = FALSE;
  598. CFindFile hFind;
  599. SIZE_T directoryLength = m_strParent.Cch();
  600. ECallbackResult result = eKeepWalking;
  601. DWORD dwWalkDirFlags = 0;
  602. ::ZeroMemory(&m_fileData, sizeof(m_fileData));
  603. result |= m_callback(eBeginDirectory, this, dwWalkDirFlags);
  604. if (result & (eError | eSuccess))
  605. {
  606. SET_LINE();
  607. goto Exit;
  608. }
  609. if ((result & eStopWalkingFiles) == 0)
  610. {
  611. for (fileFilter = m_fileFiltersBegin ; fileFilter != m_fileFiltersEnd ; ++fileFilter)
  612. {
  613. //
  614. // FindFirstFile equates *.* with *, so we do too.
  615. //
  616. fThisIsAll = ::IsStarOrStarDotStar(*fileFilter);
  617. fGotAll = fGotAll || fThisIsAll;
  618. if (!m_strParent.Win32EnsureTrailingPathSeparator())
  619. goto Error;
  620. if (!m_strParent.Win32Append(*fileFilter, (*fileFilter != NULL) ? ::wcslen(*fileFilter) : 0))
  621. goto Error;
  622. hFind = ::FindFirstFileW(m_strParent, &m_fileData);
  623. m_strParent.Left(directoryLength);
  624. if (hFind != INVALID_HANDLE_VALUE)
  625. {
  626. do
  627. {
  628. if (::FusionpIsDotOrDotDot(m_fileData.cFileName))
  629. continue;
  630. if (!m_strLastObjectFound.Win32Assign(m_fileData.cFileName, ::wcslen(m_fileData.cFileName)))
  631. {
  632. SET_LINE();
  633. goto Error;
  634. }
  635. //
  636. // we recurse on directories only if we are getting all of them
  637. // otherwise we do them afterward
  638. //
  639. // the order directories are visited is therefore inconsistent, but
  640. // most applications should be happy enough with the eEndDirectory
  641. // notification (to implement rd /q/s)
  642. //
  643. if (m_fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  644. {
  645. if (fThisIsAll && (result & eStopWalkingDirectories) == 0)
  646. {
  647. if (!m_strParent.Win32Append("\\", 1))
  648. {
  649. SET_LINE();
  650. goto Error;
  651. }
  652. if (!m_strParent.Win32Append(m_fileData.cFileName, ::wcslen(m_fileData.cFileName)))
  653. {
  654. SET_LINE();
  655. goto Error;
  656. }
  657. result |= WalkHelper();
  658. }
  659. }
  660. else
  661. {
  662. if ((result & eStopWalkingFiles) == 0)
  663. {
  664. dwWalkDirFlags |= SXSP_DIR_WALK_FLAGS_FIND_AT_LEAST_ONE_FILEUNDER_CURRENTDIR;
  665. result |= m_callback(eFile, this, dwWalkDirFlags);
  666. if(result == (eStopWalkingFiles | eStopWalkingDirectories))
  667. dwWalkDirFlags |= SXSP_DIR_WALK_FLAGS_INSTALL_ASSEMBLY_UNDER_CURRECTDIR_SUCCEED;
  668. }
  669. }
  670. m_strParent.Left(directoryLength);
  671. if (result & (eError | eSuccess))
  672. {
  673. SET_LINE();
  674. goto Exit;
  675. }
  676. if (fThisIsAll)
  677. {
  678. if ((result & eStopWalkingDirectories) &&
  679. (result & eStopWalkingFiles))
  680. {
  681. if (!hFind.Win32Close())
  682. {
  683. SET_LINE();
  684. goto Error;
  685. }
  686. SET_LINE();
  687. goto StopWalking;
  688. }
  689. }
  690. else
  691. {
  692. if (result & eStopWalkingFiles)
  693. {
  694. if (!hFind.Win32Close())
  695. {
  696. SET_LINE();
  697. goto Error;
  698. }
  699. SET_LINE();
  700. goto StopWalking;
  701. }
  702. }
  703. } while(::FindNextFileW(hFind, &m_fileData));
  704. if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
  705. {
  706. SET_LINE();
  707. goto Error;
  708. }
  709. if (!hFind.Win32Close())
  710. {
  711. SET_LINE();
  712. goto Error;
  713. }
  714. }
  715. }
  716. }
  717. StopWalking:;
  718. //
  719. // make another pass with * to get all directories, if we haven't already
  720. //
  721. if (!fGotAll && (result & eStopWalkingDirectories) == 0)
  722. {
  723. if (!m_strParent.Win32Append("\\*", 2))
  724. {
  725. SET_LINE();
  726. goto Error;
  727. }
  728. hFind = ::FindFirstFileW(m_strParent, &m_fileData);
  729. m_strParent.Left(directoryLength);
  730. if (hFind != INVALID_HANDLE_VALUE)
  731. {
  732. do
  733. {
  734. if (::FusionpIsDotOrDotDot(m_fileData.cFileName))
  735. continue;
  736. if (!m_strLastObjectFound.Win32Assign(m_fileData.cFileName, ::wcslen(m_fileData.cFileName)))
  737. {
  738. SET_LINE();
  739. goto Error;
  740. }
  741. if ((m_fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  742. continue;
  743. if (!m_strParent.Win32Append("\\", 1))
  744. {
  745. SET_LINE();
  746. goto Error;
  747. }
  748. if (!m_strParent.Win32Append(m_fileData.cFileName, ::wcslen(m_fileData.cFileName)))
  749. {
  750. SET_LINE();
  751. goto Error;
  752. }
  753. result |= WalkHelper();
  754. m_strParent.Left(directoryLength);
  755. if (result & (eError | eSuccess))
  756. {
  757. SET_LINE();
  758. goto Exit;
  759. }
  760. if (result & eStopWalkingDirectories)
  761. {
  762. SET_LINE();
  763. goto StopWalkingDirs;
  764. }
  765. } while(::FindNextFileW(hFind, &m_fileData));
  766. if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
  767. {
  768. SET_LINE();
  769. goto Error;
  770. }
  771. StopWalkingDirs:
  772. if (!hFind.Win32Close())
  773. {
  774. SET_LINE();
  775. goto Error;
  776. }
  777. }
  778. }
  779. ::ZeroMemory(&m_fileData, sizeof(m_fileData));
  780. result |= m_callback(eEndDirectory, this, dwWalkDirFlags);
  781. if (result & (eError | eSuccess))
  782. {
  783. SET_LINE();
  784. goto Exit;
  785. }
  786. result = eKeepWalking;
  787. Exit:
  788. if ((result & eStopWalkingDeep) == 0)
  789. {
  790. result &= ~(eStopWalkingFiles | eStopWalkingDirectories);
  791. }
  792. if (result & eError)
  793. {
  794. result |= (eStopWalkingFiles | eStopWalkingDirectories | eStopWalkingDeep);
  795. #if DBG
  796. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%s(%lu): %s\n", __FILE__, Line, __FUNCTION__);
  797. #endif
  798. }
  799. return result;
  800. Error:
  801. result |= eError;
  802. goto Exit;
  803. #undef SET_LINE
  804. }
  805. CDirWalk::CDirWalk()
  806. {
  807. const static PCWSTR defaultFileFilter[] = { L"*" };
  808. m_fileFiltersBegin = defaultFileFilter;
  809. m_fileFiltersEnd = defaultFileFilter + NUMBER_OF(defaultFileFilter);
  810. }
  811. BOOL
  812. CDirWalk::Walk()
  813. {
  814. BOOL fSuccess = FALSE;
  815. //
  816. // Save off the original path length before we go twiddling m_strParent
  817. //
  818. m_cchOriginalPath = m_strParent.Cch();
  819. ECallbackResult result = WalkHelper();
  820. if (result & eError)
  821. {
  822. if (::FusionpGetLastWin32Error() == ERROR_SUCCESS) // forget to set lasterror ?
  823. ::SetLastError(ERROR_INSTALL_FAILURE);
  824. goto Exit;
  825. }
  826. fSuccess = TRUE;
  827. Exit:
  828. return fSuccess;
  829. }
  830. /*-----------------------------------------------------------------------------
  831. helper function to reduce recursive stack size
  832. -----------------------------------------------------------------------------*/
  833. static VOID
  834. SxspDeleteDirectoryHelper(
  835. CBaseStringBuffer &dir,
  836. WIN32_FIND_DATAW &wfd,
  837. DWORD &dwFirstError
  838. )
  839. {
  840. //
  841. // the reason to add this call here is that if installation ends successfully, the directory
  842. // would be
  843. // C:\WINDOWS\WINSXS\INSTALLTEMP\15349016
  844. // +---Manifests
  845. //
  846. // and they are "empty" directories (no files). Manifests is a SH dir so set it to be
  847. // FILE_ATTRIBUTE_NORMAL be more efficient.
  848. //
  849. //
  850. ::SetFileAttributesW(dir, FILE_ATTRIBUTE_NORMAL);
  851. if (RemoveDirectoryW(dir)) // empty dir
  852. return;
  853. //
  854. // this is the *only* "valid" reason for DeleteDirectory fail
  855. // but I am not sure about "only"
  856. //
  857. DWORD dwLastError = ::FusionpGetLastWin32Error();
  858. if ( dwLastError != ERROR_DIR_NOT_EMPTY)
  859. {
  860. if (dwFirstError == 0)
  861. dwFirstError = dwLastError;
  862. return;
  863. }
  864. const static WCHAR SlashStar[] = L"\\*";
  865. SIZE_T length = dir.Cch();
  866. CFindFile findFile;
  867. if (!dir.Win32Append(SlashStar, NUMBER_OF(SlashStar) - 1))
  868. {
  869. if (dwFirstError == NO_ERROR)
  870. dwFirstError = ::FusionpGetLastWin32Error();
  871. goto Exit;
  872. }
  873. if (!findFile.Win32FindFirstFile(dir, &wfd))
  874. {
  875. if (dwFirstError == NO_ERROR)
  876. dwFirstError = ::FusionpGetLastWin32Error();
  877. goto Exit;
  878. }
  879. do
  880. {
  881. if (::FusionpIsDotOrDotDot(wfd.cFileName))
  882. continue;
  883. DWORD dwFileAttributes = wfd.dwFileAttributes;
  884. // Trim back to the slash...
  885. dir.Left(length + 1);
  886. if (dir.Win32Append(wfd.cFileName, ::wcslen(wfd.cFileName)))
  887. {
  888. if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  889. {
  890. // recurse
  891. ::SxspDeleteDirectoryHelper(dir, wfd, dwFirstError);
  892. }
  893. else
  894. {
  895. if (!DeleteFileW(dir))
  896. {
  897. ::SetFileAttributesW(dir, FILE_ATTRIBUTE_NORMAL);
  898. if (!DeleteFileW(dir))
  899. {
  900. if (dwFirstError == NO_ERROR)
  901. {
  902. //
  903. // continue even in delete file ( delete files as much as possible)
  904. // and record the errorCode for first failure
  905. //
  906. dwFirstError = ::FusionpGetLastWin32Error();
  907. }
  908. }
  909. }
  910. }
  911. }
  912. } while (::FindNextFileW(findFile, &wfd));
  913. if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
  914. {
  915. if (dwFirstError == NO_ERROR)
  916. dwFirstError = ::FusionpGetLastWin32Error();
  917. }
  918. Exit:
  919. if (!findFile.Win32Close()) // otherwise RemoveDirectory fails
  920. if (dwFirstError == NO_ERROR)
  921. dwFirstError = ::FusionpGetLastWin32Error();
  922. dir.Left(length);
  923. if (!RemoveDirectoryW(dir)) // the dir must be empty and NORMAL_ATTRIBUTE : ready to delete
  924. {
  925. if (dwFirstError == NO_ERROR)
  926. dwFirstError = ::FusionpGetLastWin32Error();
  927. }
  928. }
  929. /*-----------------------------------------------------------------------------
  930. delete a directory recursively, continues upon errors, but returns
  931. FALSE if there were any.
  932. -----------------------------------------------------------------------------*/
  933. BOOL
  934. SxspDeleteDirectory(
  935. const CBaseStringBuffer &dir
  936. )
  937. {
  938. BOOL fSuccess = FALSE;
  939. FN_TRACE_WIN32(fSuccess);
  940. CStringBuffer mutableDir;
  941. WIN32_FIND_DATAW wfd = {0};
  942. DWORD dwFirstError = ERROR_SUCCESS;
  943. IFW32FALSE_EXIT(mutableDir.Win32Assign(dir));
  944. IFW32FALSE_EXIT(mutableDir.Win32RemoveTrailingPathSeparators());
  945. ::SxspDeleteDirectoryHelper(
  946. mutableDir,
  947. wfd,
  948. dwFirstError);
  949. //
  950. // Set wFirstError to Teb->LastWin32Error
  951. //
  952. if (dwFirstError != ERROR_SUCCESS)
  953. goto Exit;
  954. fSuccess = TRUE;
  955. //
  956. // Oops, the walker will end up out here with ERROR_NO_MORE_FILES,
  957. // which is a plainly 'good' error. Mask it.
  958. //
  959. FusionpSetLastWin32Error(ERROR_SUCCESS);
  960. Exit:
  961. return fSuccess;
  962. }
  963. BOOL
  964. SxspGetFileAttributesW(
  965. PCWSTR lpFileName,
  966. DWORD &rdwFileAttributes,
  967. DWORD &rdwWin32Error,
  968. SIZE_T cExceptionalWin32Errors,
  969. ...
  970. )
  971. {
  972. FN_PROLOG_WIN32
  973. rdwWin32Error = ERROR_SUCCESS;
  974. if ((rdwFileAttributes = ::GetFileAttributesW(lpFileName)) == ((DWORD) -1))
  975. {
  976. SIZE_T i = 0;
  977. va_list ap;
  978. const DWORD dwLastError = ::FusionpGetLastWin32Error();
  979. va_start(ap, cExceptionalWin32Errors);
  980. for (i=0; i<cExceptionalWin32Errors; i++)
  981. {
  982. if (dwLastError == va_arg(ap, DWORD))
  983. {
  984. rdwWin32Error = dwLastError;
  985. break;
  986. }
  987. }
  988. va_end(ap);
  989. if (i == cExceptionalWin32Errors)
  990. {
  991. ORIGINATE_WIN32_FAILURE_AND_EXIT_EX(dwLastError, ("%s(%ls)", "GetFileAttributesW", lpFileName));
  992. }
  993. }
  994. FN_EPILOG
  995. }
  996. BOOL
  997. SxspGetFileAttributesW(
  998. PCWSTR lpFileName,
  999. DWORD &rdwFileAttributes
  1000. )
  1001. {
  1002. DWORD dw = 0;
  1003. return ::SxspGetFileAttributesW(lpFileName, rdwFileAttributes, dw, 0);
  1004. }