Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1693 lines
49 KiB

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <direct.h>
  5. #include <windows.h>
  6. #include "patchapi.h"
  7. #include "const.h"
  8. #include "ansparse.h"
  9. #include "dirwak2a.h"
  10. #include "filetree.h"
  11. #include "crc32.h"
  12. #ifdef __BOUNDSCHECKER__
  13. #include "nmevtrpt.h"
  14. #endif
  15. // noise about 'this'
  16. #pragma warning(disable:4355)
  17. // multi-thread support
  18. static MULTI_THREAD_STRUCT* myThreadStruct = NULL;
  19. static HANDLE* myThreadHandle = NULL;
  20. static ULONG g_iThreads = 0;
  21. // patch options
  22. static DWORD g_iBestMethod;
  23. static BOOL g_blnCollectStat;
  24. static BOOL g_blnFullLog;
  25. ///////////////////////////////////////////////////////////////////////////////
  26. //
  27. // class FileTree
  28. //
  29. ///////////////////////////////////////////////////////////////////////////////
  30. ///////////////////////////////////////////////////////////////////////////////
  31. //
  32. // FileTree, the constructor for the object FileTree, works in conjunction with
  33. // Create so that the constructor is guanranteed to return
  34. //
  35. // Parameters:
  36. //
  37. // pwszLocalRoot, the destination directory that the tree will work on
  38. //
  39. // Return:
  40. //
  41. // none, constructor must return
  42. //
  43. ///////////////////////////////////////////////////////////////////////////////
  44. FileTree::FileTree(IN CONST WCHAR* pwszLocalRoot)
  45. : m_Root(this, NULL, EMPTY)
  46. {
  47. m_cDirectories = 0;
  48. m_cFiles = 0;
  49. m_cFilesDetermined = 0;
  50. m_cbTotalFileSize = 0;
  51. memset(m_aHashTable, 0, sizeof(m_aHashTable));
  52. memset(m_aNameTable, 0, sizeof(m_aNameTable));
  53. m_cchLocalRoot = wcslen(pwszLocalRoot);
  54. wcscpy(m_wszLocalRoot, pwszLocalRoot);
  55. m_cFilesExisting = 0;
  56. m_cFilesZeroLength = 0;
  57. m_cFilesRenamed = 0;
  58. m_cFilesCopied = 0;
  59. m_cFilesChanged = 0;
  60. m_cbChangedFileSize = 0;
  61. m_cbChangedFilePatchedSize = 0;
  62. m_cbChangedFileNotPatchedSize = 0;
  63. m_cFilesNoMatch = 0;
  64. m_cbNoMatchFileSize = 0;
  65. m_pLanguage = NULL;
  66. m_pAnsParse = NULL;
  67. m_blnBase = FALSE;
  68. m_iSize = 0;
  69. }
  70. ///////////////////////////////////////////////////////////////////////////////
  71. //
  72. // ~FileTree, the destructor for the object FileTree
  73. //
  74. // Parameters:
  75. //
  76. // none
  77. //
  78. // Return:
  79. //
  80. // none
  81. //
  82. ///////////////////////////////////////////////////////////////////////////////
  83. FileTree::~FileTree(VOID)
  84. {
  85. // clear the filenodes for the base tree, all other tree should have no
  86. // file node
  87. if(m_blnBase)
  88. {
  89. for(UINT i = 0; i < HASH_SIZE; i++)
  90. {
  91. FileNode* pFile = m_aNameTable[i];
  92. while(pFile)
  93. {
  94. m_aNameTable[i] = m_aNameTable[i]->m_pNextNameHash;
  95. delete pFile;
  96. pFile = m_aNameTable[i];
  97. }
  98. }
  99. }
  100. // tell the scriptfile to remove the directories after apply patch
  101. ToScriptFile(this,
  102. m_pLanguage->s_hScriptFile,
  103. ACTION_DELETE_DIRECTORY,
  104. m_pAnsParse->m_wszBaseDirectory + DRIVE_LETTER_LENGTH,
  105. NULL,
  106. FALSE);
  107. ToScriptFile(this,
  108. m_pLanguage->s_hScriptFile,
  109. ACTION_DELETE_DIRECTORY,
  110. PATCH_SUB_PATCH,
  111. NULL,
  112. FALSE);
  113. // flush the script file
  114. ToScriptFile(this, m_pLanguage->s_hScriptFile, NULL, NULL, NULL, TRUE);
  115. // remove the critical section used by this FileTree object
  116. DeleteCriticalSection(&CSScriptFile);
  117. }
  118. ///////////////////////////////////////////////////////////////////////////////
  119. //
  120. // CreateMultiThreadStruct, allocate and zero memory for the multi-thread
  121. // supporting structures, this function is static
  122. // because the structures are static, intended to be
  123. // used by all instances of FileTree, so need to be
  124. // called only once
  125. //
  126. // Parameters:
  127. //
  128. // iNumber, the number of threads
  129. //
  130. // Return:
  131. //
  132. // TRUE for successfully allocating memory
  133. // FALSE if the memory allocation failed
  134. //
  135. ///////////////////////////////////////////////////////////////////////////////
  136. BOOL FileTree::CreateMultiThreadStruct(IN ULONG iNumber)
  137. {
  138. // allocate the memory for the structures
  139. if(myThreadStruct == NULL && myThreadHandle == NULL)
  140. {
  141. myThreadStruct = new MULTI_THREAD_STRUCT[iNumber];
  142. myThreadHandle = new HANDLE[iNumber];
  143. g_iThreads = iNumber;
  144. }
  145. // check for failed allocation
  146. if(myThreadStruct == NULL && myThreadHandle != NULL)
  147. {
  148. delete [] myThreadHandle;
  149. myThreadHandle = NULL;
  150. g_iThreads = 0;
  151. }
  152. if(myThreadHandle == NULL && myThreadStruct != NULL)
  153. {
  154. delete [] myThreadStruct;
  155. myThreadStruct = NULL;
  156. g_iThreads = 0;
  157. }
  158. // zero out the memory, if the structures are really statically linked
  159. // then we don't have to do this
  160. if(myThreadStruct != NULL && myThreadHandle != NULL)
  161. {
  162. ZeroMemory(myThreadStruct, iNumber * sizeof(MULTI_THREAD_STRUCT));
  163. ZeroMemory(myThreadHandle, iNumber * sizeof(HANDLE));
  164. }
  165. return(myThreadStruct != NULL && myThreadHandle != NULL);
  166. }
  167. ///////////////////////////////////////////////////////////////////////////////
  168. //
  169. // DeleteMultiThreadStruct, deallocate meory used by the multi-thread support
  170. // structures, also a static function, need to be
  171. // called only once
  172. //
  173. // Parameters:
  174. //
  175. // none
  176. //
  177. // Return:
  178. //
  179. // none, the memory are guanrateed to be de-allocated, and set to NULL
  180. //
  181. ///////////////////////////////////////////////////////////////////////////////
  182. VOID FileTree::DeleteMultiThreadStruct(VOID)
  183. {
  184. if(myThreadStruct != NULL)
  185. {
  186. delete [] myThreadStruct;
  187. myThreadStruct = NULL;
  188. }
  189. if(myThreadHandle != NULL)
  190. {
  191. delete [] myThreadHandle;
  192. myThreadHandle = NULL;
  193. }
  194. }
  195. ///////////////////////////////////////////////////////////////////////////////
  196. //
  197. // Create, the true function for initializing a FileTree object
  198. //
  199. // Parameters:
  200. //
  201. // pInLanguage, the language struct, contains information about directories
  202. // and others specified in the answer file
  203. // pInAnsParse, the parser for the answer file, contains information about
  204. // the base directory and so on
  205. // ppTree, pTree is the would be pointer to the FileTree
  206. // blnBase, whether or not this FileTree is a base tree
  207. //
  208. // Return:
  209. //
  210. // PREP_BAD_PATH_ERROR, invalid directory encountered
  211. // PREP_NO_MEMORY, memory allocation failed
  212. // PREP_DIRECTORY_ERROR, cannot create some directory
  213. // PREP_SCRIPT_FILE_ERROR, cannot create or write to the scriptfile
  214. // PREP_INPUT_FILE_ERROR, pInLanguage is NULL, no input
  215. // PREP_NO_ERROR, no error
  216. //
  217. ///////////////////////////////////////////////////////////////////////////////
  218. INT FileTree::Create(IN PPATCH_LANGUAGE pInLanguage,
  219. IN AnswerParser* pInAnsParse,
  220. OUT FileTree** ppTree,
  221. IN BOOL blnBase,
  222. IN DWORD iInBestMethod,
  223. IN BOOL blnInCollectStat,
  224. IN BOOL blnInFullLog)
  225. {
  226. DWORD cch;
  227. WCHAR wszBasePath[STRING_LENGTH];
  228. WCHAR* pwszJunk;
  229. FileTree* pTree;
  230. *ppTree = NULL;
  231. g_iBestMethod = iInBestMethod;
  232. g_blnCollectStat = blnInCollectStat;
  233. g_blnFullLog = blnInFullLog;
  234. if(pInLanguage)
  235. {
  236. cch = GetFullPathNameW(pInLanguage->s_wszDirectory,
  237. countof(wszBasePath), wszBasePath, &pwszJunk);
  238. if((cch == 0) || (cch >= countof(wszBasePath)))
  239. {
  240. return(PREP_BAD_PATH_ERROR);
  241. }
  242. if(wszBasePath[cch - 1] != L'\\')
  243. {
  244. wszBasePath[cch++] = L'\\';
  245. wszBasePath[cch] = L'\0';
  246. }
  247. DWORD dwAttributes = GetFileAttributesW(wszBasePath);
  248. if((dwAttributes == 0xFFFFFFFF) ||
  249. ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
  250. {
  251. return(PREP_BAD_PATH_ERROR);
  252. }
  253. pTree = new FileTree(wszBasePath);
  254. if(pTree == NULL)
  255. {
  256. return(PREP_NO_MEMORY);
  257. }
  258. pTree->m_pLanguage = pInLanguage;
  259. pTree->m_pAnsParse = pInAnsParse;
  260. pTree->m_blnBase = blnBase;
  261. // create some directories
  262. if(!(pTree->CreateNewDirectory(pInLanguage->s_wszPatchDirectory,
  263. EMPTY) &&
  264. pTree->CreateNewDirectory(pInLanguage->s_wszSubPatchDirectory,
  265. EMPTY) &&
  266. pTree->CreateNewDirectory(pInLanguage->s_wszSubExceptDirectory,
  267. EMPTY)))
  268. {
  269. return(PREP_DIRECTORY_ERROR);
  270. }
  271. if(blnBase)
  272. {
  273. if(!pTree->CreateNewDirectory(pInAnsParse->m_wszBaseDirectory,
  274. EMPTY))
  275. {
  276. return(PREP_DIRECTORY_ERROR);
  277. }
  278. }
  279. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  280. L"Directory created for patching.");
  281. // create the script file
  282. pTree->m_pLanguage->s_hScriptFile = CreateFileW(pTree->m_pLanguage->s_wszScriptFile,
  283. GENERIC_WRITE,
  284. 0,
  285. NULL,
  286. CREATE_ALWAYS,
  287. FILE_ATTRIBUTE_NORMAL,
  288. NULL);
  289. if(pTree->m_pLanguage->s_hScriptFile == INVALID_HANDLE_VALUE)
  290. {
  291. return(PREP_SCRIPT_FILE_ERROR);
  292. }
  293. // write the unicode header to file
  294. WriteFile(pTree->m_pLanguage->s_hScriptFile, &UNICODE_HEAD,
  295. sizeof(WCHAR), &cch, NULL);
  296. ZeroMemory(pTree->m_strWriteBuffer, SUPER_LENGTH * sizeof(WCHAR));
  297. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  298. L"Script file created for patching.");
  299. // initialized the critical section here
  300. InitializeCriticalSection(&(pTree->CSScriptFile));
  301. *ppTree = pTree;
  302. }
  303. else
  304. {
  305. return(PREP_INPUT_FILE_ERROR);
  306. }
  307. return(PREP_NO_ERROR);
  308. }
  309. ///////////////////////////////////////////////////////////////////////////////
  310. //
  311. // Load, the entry point for processing files,
  312. // for a base tree, pTree should be NULL,
  313. // otherwise, use the pointer to base tree to process files
  314. //
  315. // Parameters:
  316. //
  317. // pTree, a FileTree object, used to as a match target
  318. //
  319. // Return:
  320. //
  321. // PREP_BAD_PATH_ERROR, invalid directory encountered
  322. // PREP_NO_MEMORY, memory allocation failed
  323. // PREP_DEPTH_ERROR, the directory tree is too deep
  324. // PREP_UNKNOWN_ERROR, the directory tree is too deep
  325. // PREP_NO_ERROR, no error
  326. //
  327. ///////////////////////////////////////////////////////////////////////////////
  328. INT FileTree::Load(IN FileTree* pTree)
  329. {
  330. INT rc = PREP_NO_ERROR;
  331. // do the directory walk, this funtion will use the callback functions
  332. // to process files
  333. rc = DirectoryWalk(this, NULL, m_wszLocalRoot, NotifyDirectory,
  334. NotifyFile,
  335. NotifyDirectoryEnd, pTree);
  336. // at the end of matching, shutdown all threads and close the handles
  337. for(UINT i = 0; i < g_iThreads; i++)
  338. {
  339. if(WaitForSingleObject(myThreadHandle[i], INFINITE) != WAIT_FAILED)
  340. {
  341. CloseHandle(myThreadHandle[i]);
  342. }
  343. myThreadHandle[i] = NULL;
  344. }
  345. // determin error from the directory walk errors
  346. switch(rc)
  347. {
  348. case DW_NO_ERROR:
  349. rc = PREP_NO_ERROR;
  350. break;
  351. case DW_MEMORY:
  352. rc = PREP_NO_MEMORY;
  353. break;
  354. case DW_ERROR:
  355. rc = PREP_BAD_PATH_ERROR;
  356. break;
  357. case DW_DEPTH:
  358. rc = PREP_DEPTH_ERROR;
  359. break;
  360. case DW_OTHER_ERROR:
  361. rc = PREP_UNKNOWN_ERROR;
  362. break;
  363. default:
  364. break;
  365. }
  366. // print out the stats
  367. if(!m_pLanguage->s_blnBase)
  368. {
  369. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  370. L"Patch results:");
  371. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  372. L"%12I64u files total, %I64u bytes", m_cFiles,
  373. m_cbTotalFileSize);
  374. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  375. L"%12I64u existing", m_cFilesExisting);
  376. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  377. L"%12I64u zero-length", m_cFilesZeroLength);
  378. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  379. L"%12I64u renamed", m_cFilesRenamed);
  380. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  381. L"%12I64u copied (from another directory)",
  382. m_cFilesCopied);
  383. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  384. L"%12I64u changed, %I64u bytes, %I64u patched + %I64u raw",
  385. m_cFilesChanged, m_cbChangedFileSize,
  386. m_cbChangedFilePatchedSize,
  387. m_cbChangedFileNotPatchedSize);
  388. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  389. L"%12I64u unique unmatched, %I64u bytes",
  390. m_cFilesNoMatch, m_cbNoMatchFileSize);
  391. }
  392. return(rc);
  393. }
  394. ///////////////////////////////////////////////////////////////////////////////
  395. //
  396. // NotifyDirectory, the callback function used when a new directory is opened
  397. // and ready to be processed for files, the directory can be
  398. // any directory that the user has access to, also, directory
  399. // processing is not multi-threaded, only the files
  400. //
  401. // Parameters:
  402. //
  403. // context, a pointer to a FileTree object, this FileTree has called for a
  404. // directory walk
  405. // parentID, a pointer to the parent directory
  406. // directory, the name of the directory
  407. // path, the full path of the directory include then ending "\"
  408. // childID, a would be pointer to the this directory, so this directory is a
  409. // child of the parent directory
  410. // pTree, the matching tree
  411. //
  412. // Return:
  413. //
  414. // PREP_NO_MEMORY, memory allocation failed
  415. // PREP_DIRECTORY_ERROR, unable to recreate the directory for base directory
  416. // PREP_NO_ERROR, no error
  417. //
  418. ///////////////////////////////////////////////////////////////////////////////
  419. INT FileTree::NotifyDirectory(
  420. IN VOID* context,
  421. IN VOID* parentID,
  422. IN CONST WCHAR* directory,
  423. IN CONST WCHAR* path,
  424. OUT VOID** childID,
  425. VOID* pTree
  426. )
  427. {
  428. WCHAR wszDirectoryAttrib[LANGUAGE_LENGTH];
  429. FileTree* pThis = (FileTree*)context;
  430. DirectoryNode* pParent = (DirectoryNode*)parentID;
  431. if(parentID == NULL)
  432. {
  433. *childID = &pThis->m_Root;
  434. }
  435. else
  436. {
  437. // create the new directory node here
  438. DirectoryNode *pNew = new DirectoryNode(pThis,
  439. (DirectoryNode*)parentID, directory);
  440. if(pNew == NULL)
  441. {
  442. return(PREP_NO_MEMORY);
  443. }
  444. wcscpy(pNew->m_wszLongDirectoryName, path);
  445. pNew->m_wszDirectoryName = pNew->m_wszLongDirectoryName +
  446. wcslen(path) - wcslen(directory);
  447. pThis->m_cDirectories++;
  448. // go match this directory if matchable
  449. if(pTree != NULL) pNew->Match((FileTree*)pTree);
  450. *childID = pNew;
  451. }
  452. if(pParent)
  453. {
  454. if(pThis->m_blnBase)
  455. {
  456. // create base directories
  457. if(!pThis->CreateNewDirectory(
  458. pThis->m_pAnsParse->m_wszBaseDirectory,
  459. path + pThis->m_pLanguage->s_iDirectoryCount))
  460. {
  461. return(PREP_DIRECTORY_ERROR);
  462. }
  463. }
  464. // save the directory creation to scriptfile
  465. // most localized trees do contain localized directory names, we need
  466. // to recreate them later with the same directory attributes
  467. DWORD iAttrib = GetFileAttributesW(path);
  468. ZeroMemory(wszDirectoryAttrib, LANGUAGE_LENGTH * sizeof(WCHAR));
  469. if((iAttrib & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN)
  470. {
  471. wcscat(wszDirectoryAttrib, DIR_HIDDEN);
  472. }
  473. if((iAttrib & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
  474. {
  475. wcscat(wszDirectoryAttrib, DIR_READONLY);
  476. }
  477. if((iAttrib & FILE_ATTRIBUTE_SYSTEM) == FILE_ATTRIBUTE_SYSTEM)
  478. {
  479. wcscat(wszDirectoryAttrib, DIR_SYSTEM);
  480. }
  481. if((iAttrib & FILE_ATTRIBUTE_COMPRESSED) == FILE_ATTRIBUTE_COMPRESSED)
  482. {
  483. wcscat(wszDirectoryAttrib, DIR_COMPRESSED);
  484. }
  485. if((iAttrib & FILE_ATTRIBUTE_ENCRYPTED) == FILE_ATTRIBUTE_ENCRYPTED)
  486. {
  487. wcscat(wszDirectoryAttrib, DIR_ENCRYPTED);
  488. }
  489. pThis->ToScriptFile(pThis,
  490. pThis->m_pLanguage->s_hScriptFile,
  491. ACTION_NEW_DIRECTORY,
  492. path + pThis->m_pLanguage->s_iDirectoryCount,
  493. wszDirectoryAttrib,
  494. FALSE);
  495. }
  496. return(DW_NO_ERROR);
  497. }
  498. ///////////////////////////////////////////////////////////////////////////////
  499. //
  500. // NotifyFile, this is a callback function used when a new file is being
  501. // processed, also a static function to allow same entry point
  502. // for all files, supports multi-threaded file processing
  503. //
  504. // Parameters:
  505. //
  506. // context, a pointer to a FileTree object, this FileTree has called for a
  507. // directory walk
  508. // parentID, a pointer to the parent directory
  509. // filename, the filename
  510. // path, the full path of the directory include then ending "\"
  511. // attributes, the file attribute
  512. // filetime, the last modified time, not used
  513. // creation, the creation time, not used
  514. // filesize, the size of the file in bytes
  515. // pTree, the matching tree
  516. //
  517. // Return:
  518. //
  519. // DW_OTHER_ERROR, thread creation failed
  520. // DW_NO_ERROR, no error
  521. //
  522. ///////////////////////////////////////////////////////////////////////////////
  523. INT FileTree::NotifyFile(
  524. IN VOID* context,
  525. IN VOID* parentID,
  526. IN CONST WCHAR* filename,
  527. IN CONST WCHAR* path,
  528. IN DWORD attributes,
  529. IN FILETIME filetime,
  530. IN FILETIME creation,
  531. IN __int64 filesize,
  532. IN VOID* pTree
  533. )
  534. {
  535. UINT i = 0;
  536. DWORD iThread = 0;
  537. // find an empty thread slot for file processing
  538. for(i = 0; i < g_iThreads; i++)
  539. {
  540. if(!myThreadStruct[i].blnInUse) break;
  541. }
  542. if(i < g_iThreads && !myThreadStruct[i].blnInUse)
  543. {
  544. // empty slot is found
  545. // make sure the thread is done and close the handle
  546. if(myThreadHandle[i] &&
  547. WaitForSingleObject(myThreadHandle[i], INFINITE) != WAIT_FAILED)
  548. {
  549. CloseHandle(myThreadHandle[i]);
  550. }
  551. }
  552. else
  553. {
  554. // empty slot is not found
  555. // wait for a thread to finish
  556. DWORD dwEvent = WaitForMultipleObjects(g_iThreads,
  557. myThreadHandle, FALSE, INFINITE);
  558. // there is a struct not in use, so find it and close the handle
  559. i = dwEvent - WAIT_OBJECT_0;
  560. CloseHandle(myThreadHandle[i]);
  561. }
  562. // reset the handle to null for insurance
  563. myThreadHandle[i] = NULL;
  564. // ready the multi-thread support struct to pass onto the actual processing
  565. // function, should try not to pass pointers here unless absolutely
  566. // necessary
  567. myThreadStruct[i].blnInUse = TRUE;
  568. myThreadStruct[i].pThis = (FileTree*)context;
  569. myThreadStruct[i].pParent = (DirectoryNode*)parentID;
  570. // the reason for copy the filenames is that once the thread is running,
  571. // the pointers are changed by the directory walk thread
  572. wcscpy(myThreadStruct[i].filename, filename);
  573. wcscpy(myThreadStruct[i].path, path);
  574. myThreadStruct[i].attributes = attributes;
  575. myThreadStruct[i].filetime = filetime;
  576. myThreadStruct[i].creation = creation;
  577. myThreadStruct[i].filesize = filesize;
  578. myThreadStruct[i].pTree = (FileTree*)pTree;
  579. // create the thread and give the struct
  580. myThreadHandle[i] = CreateThread(NULL,
  581. 0,
  582. (LPTHREAD_START_ROUTINE)StartFileThread,
  583. (LPVOID)&myThreadStruct[i],
  584. 0,
  585. &iThread);
  586. if(myThreadHandle[i] == NULL)
  587. {
  588. // thread creation failed
  589. return(DW_OTHER_ERROR);
  590. }
  591. return(DW_NO_ERROR);
  592. }
  593. ///////////////////////////////////////////////////////////////////////////////
  594. //
  595. // StartFileThread, this is the original thread entry point, I did some debug
  596. // work here before, but now it is just a wrapper to jump to
  597. // the next function
  598. //
  599. // Parameters:
  600. //
  601. // lpParam, the multi-thread struct, everything about this file ready to be
  602. // processed
  603. //
  604. // Return:
  605. //
  606. // Whatever from ProcessFile
  607. //
  608. ///////////////////////////////////////////////////////////////////////////////
  609. DWORD WINAPI FileTree::StartFileThread(IN LPVOID lpParam)
  610. {
  611. MULTI_THREAD_STRUCT* pStruct = (MULTI_THREAD_STRUCT*)lpParam;
  612. // translate the parameters and call the processing function
  613. return(ProcessFile(pStruct->pThis,
  614. pStruct->pParent,
  615. pStruct->filename,
  616. pStruct->path,
  617. pStruct->attributes,
  618. pStruct->filetime,
  619. pStruct->creation,
  620. pStruct->filesize,
  621. pStruct->pTree,
  622. pStruct));
  623. }
  624. ///////////////////////////////////////////////////////////////////////////////
  625. //
  626. // ProcessFile, do actual file processing, determines what to do with the file,
  627. // computes filehash, try to match it, every file node is created
  628. // here, but deleted after usage to save memory
  629. //
  630. // Parameters:
  631. //
  632. // same as in NotifyFile
  633. //
  634. // Return:
  635. //
  636. // PREP_NO_MEMORY, allocation failed
  637. // DW_NO_ERROR, no error
  638. //
  639. ///////////////////////////////////////////////////////////////////////////////
  640. INT FileTree::ProcessFile(
  641. IN FileTree* pThis,
  642. IN DirectoryNode* pParent,
  643. IN CONST WCHAR* filename,
  644. IN CONST WCHAR* path,
  645. IN DWORD attributes,
  646. IN FILETIME filetime,
  647. IN FILETIME creation,
  648. IN __int64 filesize,
  649. IN FileTree* pTree,
  650. IN VOID* pStruct
  651. )
  652. {
  653. WCHAR wszFullPathFileName[STRING_LENGTH];
  654. INT iLength = 0;
  655. // is the file an exception?
  656. if(!pThis->m_pAnsParse->IsFileExceptHash(filename))
  657. {
  658. // create the file node here
  659. FileNode* pNew = new FileNode(pParent, filename, filetime, filesize);
  660. if(pNew == NULL)
  661. {
  662. return(PREP_NO_MEMORY);
  663. }
  664. pThis->m_cFiles++;
  665. pThis->m_cbTotalFileSize += filesize;
  666. // path includes the ending '\'
  667. // process filenames to get fullpath name
  668. iLength = wcslen(path);
  669. wcscpy(pNew->m_wszLongFileName, path);
  670. wcscat(pNew->m_wszLongFileName + iLength, filename);
  671. pNew->m_wszFileName = pNew->m_wszLongFileName + iLength;
  672. // computes the file content hash
  673. // file name hash is computed in (new FileNode)
  674. pNew->ComputeHash();
  675. if(!pThis->m_pLanguage->s_blnBase)
  676. {
  677. // match the file if the file is not in the base tree
  678. // the directory node should already be matched
  679. pNew->Match((FileTree*)pTree);
  680. // remove the filenode after use
  681. delete pNew;
  682. pNew = NULL;
  683. }
  684. else
  685. {
  686. // this language is the base language, need to copy files to base directory
  687. if(!pThis->CopyFileTo(pThis,
  688. ACTION_MOVE_FILE,
  689. pThis->m_pAnsParse->m_wszBaseDirectory,
  690. pNew->m_wszLongFileName +
  691. pThis->m_pLanguage->s_iDirectoryCount,
  692. pNew->m_wszLongFileName,
  693. attributes))
  694. {
  695. // copy file failed, a warning
  696. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  697. L"warning, file copy %ls failed, e=%d",
  698. filename, GetLastError());
  699. }
  700. }
  701. }
  702. else
  703. {
  704. // get full path, then move the file to patch\except directory
  705. wcscpy(wszFullPathFileName, path);
  706. wcscat(wszFullPathFileName, filename);
  707. if(!pThis->CopyFileTo(pThis,
  708. ACTION_EXCEPT_FILE,
  709. pThis->m_pLanguage->s_wszSubExceptDirectory,
  710. filename,
  711. wszFullPathFileName,
  712. attributes))
  713. {
  714. // copy file failed, a warning
  715. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  716. L"warning, except file copy %ls failed, e=%d",
  717. filename, GetLastError());
  718. }
  719. }
  720. // reset the thread status so other threads can close the thread and
  721. // takes its place
  722. ((MULTI_THREAD_STRUCT*)pStruct)->blnInUse = FALSE;
  723. return(DW_NO_ERROR);
  724. }
  725. ///////////////////////////////////////////////////////////////////////////////
  726. //
  727. // NotifyDirectoryEnd, this callback function is used when the end of directory
  728. // is encountered
  729. //
  730. // Parameters:
  731. //
  732. // not used, just there for compiling
  733. //
  734. // Return:
  735. //
  736. // DW_NO_ERROR, no error
  737. //
  738. ///////////////////////////////////////////////////////////////////////////////
  739. INT FileTree::NotifyDirectoryEnd(
  740. IN VOID* pContext,
  741. IN VOID* pParentID,
  742. IN CONST WCHAR* pwszDirectory,
  743. IN CONST WCHAR* pwszPath,
  744. IN VOID* pChildID)
  745. {
  746. // when the end of the directory is reached, all file processig thread
  747. // must be terminated, the reason is that each file rely on its parent,
  748. // a directory node, to know where it is at and how to match itself, but
  749. // the directory node pointer is changed when a new directory is
  750. // encountered, to avoid errors, all threads must exit.
  751. // the thread handles are not closed here, but they will be close either
  752. // through a new file thread creation or the end of Load.
  753. WaitForMultipleObjects(g_iThreads, myThreadHandle, TRUE, INFINITE);
  754. return(DW_NO_ERROR);
  755. }
  756. ///////////////////////////////////////////////////////////////////////////////
  757. //
  758. // CreateNewDirectory, creates a directory with no attributes set, if the
  759. // directory already exist, no new directory is created and
  760. // attributes are not changed
  761. //
  762. // Parameters:
  763. //
  764. // pwszLocal, the first part of the directory
  765. // pwszBuffer, the second part of the directory,
  766. // pwszLocal + pwszBuffer = full path of the new directory
  767. //
  768. // Return:
  769. //
  770. // DW_NO_ERROR, no error
  771. //
  772. ///////////////////////////////////////////////////////////////////////////////
  773. BOOL FileTree::CreateNewDirectory(IN WCHAR* pwszLocal,
  774. IN CONST WCHAR* pwszBuffer)
  775. {
  776. BOOL blnReturn = FALSE;
  777. ULONG iLength = 0;
  778. ULONG iEntireLength = 0;
  779. iLength = wcslen(pwszLocal);
  780. wcscat(pwszLocal, pwszBuffer);
  781. // uses _wmdir instead of CreateDirectory so that error is easy to check,
  782. // errno is defined by CRT stdlib.h, and 17 is the EEXIST error code
  783. blnReturn = (_wmkdir(pwszLocal) == 0 || errno == 17 /*EEXIST*/);
  784. pwszLocal[iLength] = 0;
  785. return(blnReturn);
  786. }
  787. ///////////////////////////////////////////////////////////////////////////////
  788. //
  789. // CopyFileTo, physically copy a file along with its attributes to another
  790. // location, used to save files that are not patched
  791. //
  792. // Parameters:
  793. //
  794. // pThis, pointer to this filetree
  795. // pwszWhat, the action
  796. // pwszLocal, the destination directory
  797. // pwszFileName, the full path filename minus the directory specified in the
  798. // answerfile, pwszLocal + pwszFileName = new filename
  799. // pwszOldFile, the full path oldfile
  800. // attributes, this file's attributes
  801. //
  802. // Return:
  803. //
  804. // TRUE for file copied and logged into the scriptfile
  805. // FALSE otherwise, should not stop processing files, but logs the entries
  806. //
  807. ///////////////////////////////////////////////////////////////////////////////
  808. BOOL FileTree::CopyFileTo(IN FileTree* pThis,
  809. IN CONST WCHAR* pwszWhat,
  810. IN WCHAR* pwszLocal,
  811. IN CONST WCHAR* pwszFileName,
  812. IN WCHAR* pwszOldFile,
  813. IN DWORD attributes)
  814. {
  815. BOOL blnReturn = FALSE;
  816. ULONG iLength = 0;
  817. ULONG iEntireLength = 0;
  818. WCHAR wszRandom[10];
  819. WCHAR wszTempString[STRING_LENGTH];
  820. iLength = wcslen(pwszLocal);
  821. iEntireLength = iLength + wcslen(pwszFileName);
  822. wcscpy(wszTempString, pwszLocal);
  823. wcscpy(wszTempString + iLength, pwszFileName);
  824. if(pwszWhat != ACTION_EXCEPT_FILE &&
  825. pwszWhat != ACTION_NOT_PATCH_FILE &&
  826. pwszWhat != ACTION_SAVED_FILE)
  827. {
  828. // error can be caused by file attributes
  829. blnReturn = CopyFileW(pwszOldFile, wszTempString, FALSE);
  830. if(!blnReturn)
  831. {
  832. SetFileAttributesW(wszTempString, FILE_ATTRIBUTE_ARCHIVE);
  833. blnReturn = CopyFileW(pwszOldFile, wszTempString, FALSE);
  834. SetFileAttributesW(wszTempString, attributes);
  835. }
  836. }
  837. else
  838. {
  839. // choose a different name if possible
  840. while((blnReturn = CopyFileW(pwszOldFile, wszTempString, TRUE)) ==
  841. FALSE)
  842. {
  843. ZeroMemory(wszRandom, 10 * sizeof(WCHAR));
  844. // randomly picks a number, total number of possible choice is 100,
  845. // there shouldn't be 100 same name file in a file tree
  846. _itow(rand() % FILE_LIMIT, wszRandom, 10);
  847. if(wcslen(wszRandom) + iEntireLength < STRING_LENGTH)
  848. {
  849. wcscpy(wszTempString + iEntireLength, wszRandom);
  850. }
  851. else
  852. {
  853. break;
  854. }
  855. }
  856. }
  857. if(blnReturn)
  858. {
  859. // remember what to do later
  860. if(pwszWhat != ACTION_MOVE_FILE)
  861. {
  862. pThis->ToScriptFile(pThis,
  863. pThis->m_pLanguage->s_hScriptFile,
  864. pwszWhat,
  865. wszTempString + pThis->m_pLanguage->s_iPatchDirectoryCount,
  866. pwszOldFile + pThis->m_pLanguage->s_iDirectoryCount,
  867. FALSE);
  868. }
  869. else
  870. {
  871. pThis->ToScriptFile(pThis,
  872. pThis->m_pLanguage->s_hScriptFile,
  873. pwszWhat,
  874. wszTempString + DRIVE_LETTER_LENGTH,
  875. pwszOldFile + pThis->m_pLanguage->s_iDirectoryCount,
  876. FALSE);
  877. }
  878. }
  879. return(blnReturn);
  880. }
  881. ///////////////////////////////////////////////////////////////////////////////
  882. //
  883. // ToScriptFile, save actions into the scriptfile so that apply patch can be
  884. // done later on the client side
  885. //
  886. // Parameters:
  887. //
  888. // pThis, pointer to this filetree
  889. // hFile, the handle of the script file
  890. // pwszWhat, the action to be saved
  891. // pwszFirst, first string to be saved
  892. // pwszSecond, the second string to be saved
  893. // blnFlush, TRUE to flush the saved buffer to write, FALSE = do not flush
  894. //
  895. // Note:
  896. //
  897. // where blnFlush is set to TRUE, all other parameters are ignored
  898. //
  899. // Return:
  900. //
  901. // none
  902. //
  903. ///////////////////////////////////////////////////////////////////////////////
  904. VOID FileTree::ToScriptFile(IN FileTree* pThis,
  905. IN HANDLE hFile,
  906. IN CONST WCHAR* pwszWhat,
  907. IN CONST WCHAR* pwszFirst,
  908. IN WCHAR* pwszSecond,
  909. IN BOOL blnFlush)
  910. {
  911. ULONG iFirstLength = 0;
  912. ULONG iLength = 0;
  913. ULONG iBegin = 0;
  914. ULONG iSecondLength = 0;
  915. ULONG iWriteBytes = 0;
  916. __try
  917. {
  918. // critical section lock on the scriptfile
  919. EnterCriticalSection(&(pThis->CSScriptFile));
  920. if(!blnFlush && pwszWhat && pwszFirst &&
  921. (iFirstLength = wcslen(pwszFirst)) > 0)
  922. {
  923. // 1: the action
  924. // 1: the * separator
  925. // 1: the end of line
  926. // 1: the carriage return
  927. iLength = 4 + iFirstLength;
  928. if(pwszSecond && (iSecondLength = wcslen(pwszSecond)) > 0)
  929. {
  930. // 1: the * separator
  931. iLength += 1 + iSecondLength;
  932. }
  933. iBegin = pThis->m_iSize;
  934. pThis->m_iSize += iLength;
  935. if(pThis->m_iSize + 1 < SUPER_LENGTH)
  936. {
  937. // buffer the message up
  938. wcscpy(pThis->m_strWriteBuffer + iBegin, pwszWhat);
  939. wcscpy(pThis->m_strWriteBuffer + iBegin + 1, SEPARATOR);
  940. wcscpy(pThis->m_strWriteBuffer + iBegin + 2, pwszFirst);
  941. if(pwszSecond && iSecondLength > 0)
  942. {
  943. wcscpy(pThis->m_strWriteBuffer + iBegin + 2 + iFirstLength,
  944. SEPARATOR);
  945. wcscpy(pThis->m_strWriteBuffer + iBegin + 3 + iFirstLength,
  946. pwszSecond);
  947. }
  948. wcscpy(pThis->m_strWriteBuffer + pThis->m_iSize - 2, ENDOFLINE);
  949. wcscpy(pThis->m_strWriteBuffer + pThis->m_iSize - 1, CRETURN);
  950. }
  951. else
  952. {
  953. // overflow, write the buffer out
  954. WriteFile(hFile, pThis->m_strWriteBuffer,
  955. iBegin * sizeof(WCHAR), &iWriteBytes, NULL);
  956. ZeroMemory(pThis->m_strWriteBuffer,
  957. SUPER_LENGTH * sizeof(WCHAR));
  958. wcscpy(pThis->m_strWriteBuffer, pwszWhat);
  959. wcscpy(pThis->m_strWriteBuffer + 1, SEPARATOR);
  960. wcscpy(pThis->m_strWriteBuffer + 2, pwszFirst);
  961. if(iSecondLength > 0)
  962. {
  963. wcscpy(pThis->m_strWriteBuffer + 2 + iFirstLength,
  964. SEPARATOR);
  965. wcscpy(pThis->m_strWriteBuffer + 3 + iFirstLength,
  966. pwszSecond);
  967. }
  968. wcscpy(pThis->m_strWriteBuffer + iLength - 2, ENDOFLINE);
  969. wcscpy(pThis->m_strWriteBuffer + iLength - 1, CRETURN);
  970. pThis->m_iSize = iLength;
  971. }
  972. }
  973. else if(blnFlush && pThis->m_iSize > 0)
  974. {
  975. // flush the buffer to file
  976. WriteFile(hFile, pThis->m_strWriteBuffer,
  977. pThis->m_iSize * sizeof(WCHAR), &iWriteBytes, NULL);
  978. pThis->m_iSize = 0;
  979. }
  980. }
  981. __finally
  982. {
  983. LeaveCriticalSection(&(pThis->CSScriptFile));
  984. }
  985. }
  986. ////////////////////////////////////////////////////////////////////////////////////////////////////
  987. //
  988. // class DirectoryNode
  989. //
  990. ////////////////////////////////////////////////////////////////////////////////////////////////////
  991. ///////////////////////////////////////////////////////////////////////////////
  992. //
  993. // DirectoryNode, constructor for the DirectoryNode object, directory name hash
  994. // is created here
  995. //
  996. // Parameters:
  997. //
  998. // pRoot, the filetree object that contains this directory
  999. // pParent, the parent directory node
  1000. // pwszDirectoryName, the name of the directory
  1001. //
  1002. // Return:
  1003. //
  1004. // none
  1005. //
  1006. ///////////////////////////////////////////////////////////////////////////////
  1007. DirectoryNode::DirectoryNode(IN FileTree* pRoot,
  1008. IN DirectoryNode* pParent,
  1009. IN CONST WCHAR* pwszDirectoryName)
  1010. {
  1011. m_pParent = pParent;
  1012. m_pFirstChild = NULL;
  1013. m_pNext = NULL;
  1014. m_pRoot = pRoot;
  1015. m_pMatchingDirectory = NULL;
  1016. m_cchDirectoryName = wcslen(pwszDirectoryName);
  1017. m_wszDirectoryName = NULL;
  1018. if(pParent != NULL)
  1019. {
  1020. m_pNext = pParent->m_pFirstChild;
  1021. pParent->m_pFirstChild = this;
  1022. }
  1023. m_DirectoryNameHash = CRC32Update(0, (UCHAR*)pwszDirectoryName,
  1024. sizeof(WCHAR) * m_cchDirectoryName);
  1025. }
  1026. ///////////////////////////////////////////////////////////////////////////////
  1027. //
  1028. // ~DirectoryNode, destructor, responsible for clean up its own child
  1029. // directories, file nodes are not removed here, but in the
  1030. // destructor of the filetree
  1031. //
  1032. // Parameters:
  1033. //
  1034. // none
  1035. //
  1036. // Return:
  1037. //
  1038. // none
  1039. //
  1040. ///////////////////////////////////////////////////////////////////////////////
  1041. DirectoryNode::~DirectoryNode(VOID)
  1042. {
  1043. // remove the directory nodes
  1044. DirectoryNode* pDirectory = m_pFirstChild;
  1045. while(pDirectory)
  1046. {
  1047. DirectoryNode* pNext = pDirectory->m_pNext;
  1048. delete pDirectory;
  1049. pDirectory = NULL;
  1050. pDirectory = pNext;
  1051. }
  1052. }
  1053. ///////////////////////////////////////////////////////////////////////////////
  1054. //
  1055. // Match, this function is used to match a directory with another directory in
  1056. // the BaseTree, must be called before processing a file, otherwise,
  1057. // some matches do not make any sense
  1058. //
  1059. // Parameters:
  1060. //
  1061. // pBaseTree, the pointer to the BaseTree
  1062. //
  1063. // Return:
  1064. //
  1065. // PREP_NO_ERROR, no error
  1066. //
  1067. ///////////////////////////////////////////////////////////////////////////////
  1068. INT DirectoryNode::Match(IN FileTree* pBaseTree)
  1069. {
  1070. DirectoryNode* pChoice = NULL;
  1071. // match this directory
  1072. if(m_pParent == NULL)
  1073. {
  1074. // Rule: localized root's mate is base root
  1075. m_pMatchingDirectory = &pBaseTree->m_Root;
  1076. }
  1077. else if(m_pParent->m_pMatchingDirectory != NULL)
  1078. {
  1079. // Rule: choose the cousin with an identical name
  1080. // from this directory's parent's mate's children
  1081. pChoice = m_pParent->m_pMatchingDirectory->m_pFirstChild;
  1082. while(pChoice != NULL)
  1083. {
  1084. if((m_DirectoryNameHash == pChoice->m_DirectoryNameHash) &&
  1085. (wcscmp(m_wszDirectoryName, pChoice->m_wszDirectoryName) == 0))
  1086. {
  1087. // update the matching directory member
  1088. m_pMatchingDirectory = pChoice;
  1089. break;
  1090. }
  1091. pChoice = pChoice->m_pNext;
  1092. }
  1093. }
  1094. else
  1095. {
  1096. // Rule: I have no cousins if my parent has no mate.
  1097. }
  1098. return(PREP_NO_ERROR);
  1099. }
  1100. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1101. //
  1102. // class FileNode
  1103. //
  1104. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1105. ///////////////////////////////////////////////////////////////////////////////
  1106. //
  1107. // FileNode, constructor for the FileNode object, compute for a name hash, file
  1108. // content hash is computed by ComputeHash
  1109. //
  1110. // Parameters:
  1111. //
  1112. // pParent, the parent directory
  1113. // pwszFileName, the name of the file
  1114. // ftLastModified, time the file was touched, not used
  1115. // iFileSize, the size of the file in bytes
  1116. //
  1117. // Return:
  1118. //
  1119. // none
  1120. //
  1121. ///////////////////////////////////////////////////////////////////////////////
  1122. FileNode::FileNode(IN DirectoryNode* pParent,
  1123. IN CONST WCHAR* pwszFileName,
  1124. IN FILETIME ftLastModified,
  1125. IN __int64 iFileSize)
  1126. {
  1127. m_pParent = pParent;
  1128. m_ftLastModified = ftLastModified;
  1129. m_iFileSize = iFileSize;
  1130. m_cchFileName = wcslen(pwszFileName);
  1131. m_wszFileName = NULL;
  1132. m_pNextHashHash = NULL;
  1133. m_pNextNameHash = NULL;
  1134. m_FileNameHash = CRC32Update(0, (UCHAR*)pwszFileName,
  1135. sizeof(WCHAR) * m_cchFileName);
  1136. unsigned iNameHash = m_FileNameHash % HASH_SIZE;
  1137. m_pNextNameHash = pParent->m_pRoot->m_aNameTable[iNameHash];
  1138. pParent->m_pRoot->m_aNameTable[iNameHash] = this;
  1139. m_fPostCopySource = 0;
  1140. }
  1141. ///////////////////////////////////////////////////////////////////////////////
  1142. //
  1143. // ~FileNode, the destructor for a file node, does not
  1144. //
  1145. // Parameters:
  1146. //
  1147. // none
  1148. //
  1149. // Return:
  1150. //
  1151. // none
  1152. //
  1153. ///////////////////////////////////////////////////////////////////////////////
  1154. FileNode::~FileNode(VOID)
  1155. {
  1156. }
  1157. ///////////////////////////////////////////////////////////////////////////////
  1158. //
  1159. // Nibble, an inline function used to calculate a value for file content hash
  1160. //
  1161. // Parameters:
  1162. //
  1163. // wch, a single unicoded character
  1164. //
  1165. // Return:
  1166. //
  1167. // a BYTE that is intended for hashing
  1168. //
  1169. ///////////////////////////////////////////////////////////////////////////////
  1170. __inline BYTE Nibble(WCHAR wch)
  1171. {
  1172. if((wch >= L'0') && (wch <= L'9'))
  1173. {
  1174. return((BYTE)(wch - L'0'));
  1175. }
  1176. else if((wch >= L'A') && (wch <= L'F'))
  1177. {
  1178. return((BYTE)(wch - L'A' + 10));
  1179. }
  1180. else if((wch >= L'a') && (wch <= L'f'))
  1181. {
  1182. return((BYTE)(wch - L'a' + 10));
  1183. }
  1184. else
  1185. {
  1186. return(99);
  1187. }
  1188. }
  1189. ///////////////////////////////////////////////////////////////////////////////
  1190. //
  1191. // ComputeHash, produces a hash value for the file content, almost unique for
  1192. // all files, the hash value is of MD5, a 16 byte value
  1193. //
  1194. // Parameters:
  1195. //
  1196. // none
  1197. //
  1198. // Return:
  1199. //
  1200. // PREP_NO_ERROR, no error
  1201. // PREP_HASH_ERROR, hash error, wrong hash value
  1202. //
  1203. ///////////////////////////////////////////////////////////////////////////////
  1204. INT FileNode::ComputeHash(VOID)
  1205. {
  1206. INT rc = PREP_NO_ERROR;
  1207. if(m_iFileSize == 0)
  1208. {
  1209. memset(m_Hash, 0, sizeof(m_Hash));
  1210. }
  1211. else
  1212. {
  1213. WCHAR wszBuffer[STRING_LENGTH];
  1214. // filename, then hash
  1215. if(GetFilePatchSignatureW(m_wszLongFileName,
  1216. (PATCH_OPTION_NO_REBASE |
  1217. PATCH_OPTION_NO_BINDFIX |
  1218. PATCH_OPTION_NO_LOCKFIX |
  1219. PATCH_OPTION_NO_RESTIMEFIX |
  1220. PATCH_OPTION_SIGNATURE_MD5),
  1221. NULL,
  1222. 0, NULL, // no ignore
  1223. 0, NULL, // no retain
  1224. sizeof(wszBuffer), // buffer size in bytes
  1225. wszBuffer))
  1226. {
  1227. WCHAR* pwch = wszBuffer;
  1228. for(INT i = 0; i < sizeof(m_Hash); i++)
  1229. {
  1230. BYTE hiNibble = Nibble(*pwch++);
  1231. BYTE loNibble = Nibble(*pwch++);
  1232. m_Hash[i] = (BYTE)((hiNibble << 4) + loNibble);
  1233. if((hiNibble > 0x0F) || (loNibble > 0x0F))
  1234. {
  1235. rc = PREP_HASH_ERROR;
  1236. break;
  1237. }
  1238. }
  1239. if(*pwch != L'\0')
  1240. {
  1241. rc = PREP_HASH_ERROR;
  1242. }
  1243. }
  1244. else
  1245. {
  1246. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  1247. L"warning, GetFilePatchSignatureW() failed, GLE=%08X\n, F=%ls", GetLastError(),
  1248. m_wszLongFileName);
  1249. rc = PREP_HASH_ERROR;
  1250. }
  1251. }
  1252. if(rc == PREP_NO_ERROR)
  1253. {
  1254. // add this file to root's hash table
  1255. unsigned iHashHash = (*(UINT*)(&m_Hash[0])) % HASH_SIZE;
  1256. m_pNextHashHash = m_pParent->m_pRoot->m_aHashTable[iHashHash];
  1257. m_pParent->m_pRoot->m_aHashTable[iHashHash] = this;
  1258. }
  1259. return(rc);
  1260. }
  1261. ///////////////////////////////////////////////////////////////////////////////
  1262. //
  1263. // Match, the file node is attempting to match with another file in the base
  1264. // trees interms of filename and filecontent through hashing, there are
  1265. // a total of 6 rules, in practice, rule 0 and 2 can be ignored
  1266. //
  1267. // Parameters:
  1268. //
  1269. // pBaseTree, the FileTree object that is intended to match with
  1270. //
  1271. // Return:
  1272. //
  1273. // PREP_NO_ERROR, no error
  1274. //
  1275. ///////////////////////////////////////////////////////////////////////////////
  1276. INT FileNode::Match(IN FileTree* pBaseTree)
  1277. {
  1278. WCHAR wszTempName[STRING_LENGTH];
  1279. DETERMINATION eDetermination;
  1280. unsigned iHashHash = (*(UINT*)(&m_Hash[0])) % HASH_SIZE;
  1281. unsigned iNameHash = m_FileNameHash % HASH_SIZE;
  1282. FileTree *pLocalizedTree = m_pParent->m_pRoot;
  1283. FileNode *pChoice = NULL;
  1284. pLocalizedTree->m_cFilesDetermined++;
  1285. fprintf(stderr, "%I64u of %I64u\r",
  1286. pLocalizedTree->m_cFilesDetermined, pLocalizedTree->m_cFiles);
  1287. // Rule 0
  1288. // What: files that are of the same name, same content, same location in base and localized tree
  1289. // What to do here: record the location and name of the files with a move tag in the script file
  1290. // What to do later: the base file needs to be moved from the base to localized directory
  1291. if(m_pParent->m_pMatchingDirectory != NULL)
  1292. {
  1293. pChoice = pBaseTree->m_aHashTable[iHashHash];
  1294. while(pChoice != NULL)
  1295. {
  1296. if((m_FileNameHash == pChoice->m_FileNameHash) &&
  1297. (m_pParent->m_pMatchingDirectory == pChoice->m_pParent) &&
  1298. (memcmp(m_Hash, pChoice->m_Hash, sizeof(m_Hash)) == 0) &&
  1299. (wcscmp(m_wszFileName, pChoice->m_wszFileName) == 0))
  1300. {
  1301. eDetermination = DETERMINATION_EXISTING;
  1302. pLocalizedTree->m_cFilesExisting++;
  1303. wcscpy(wszTempName,
  1304. pLocalizedTree->m_pAnsParse->m_wszBaseDirectory +
  1305. DRIVE_LETTER_LENGTH);
  1306. wcscat(wszTempName,
  1307. pChoice->m_wszLongFileName +
  1308. pBaseTree->m_pLanguage->s_iDirectoryCount);
  1309. pLocalizedTree->ToScriptFile(pLocalizedTree,
  1310. pLocalizedTree->m_pLanguage->s_hScriptFile,
  1311. ACTION_MOVE_FILE,
  1312. wszTempName,
  1313. m_wszLongFileName +
  1314. pLocalizedTree->m_pLanguage->s_iDirectoryCount,
  1315. FALSE);
  1316. goto done;
  1317. }
  1318. pChoice = pChoice->m_pNextHashHash;
  1319. }
  1320. }
  1321. // Rule 1
  1322. // What: files that are of zero length in the localized tree
  1323. // What to do here: record the location and name of the files with a zero tag in the script file
  1324. // What to do later: re-create the zero length file
  1325. if(m_iFileSize == 0)
  1326. {
  1327. eDetermination = DETERMINATION_ZERO_LENGTH;
  1328. pLocalizedTree->ToScriptFile(pLocalizedTree,
  1329. pLocalizedTree->m_pLanguage->s_hScriptFile,
  1330. ACTION_NEW_ZERO_FILE,
  1331. m_wszLongFileName +
  1332. pLocalizedTree->m_pLanguage->s_iDirectoryCount,
  1333. NULL,
  1334. FALSE);
  1335. pLocalizedTree->m_cFilesZeroLength++;
  1336. goto done;
  1337. }
  1338. // Rule 2
  1339. // What: files that are of the different name, same content, same location in base and localized tree
  1340. // What to do here: record the location of the files with a rename tag in the script file
  1341. // What to do later: the base file needs to be renamed(actually moved) from the base to localized directory
  1342. if(m_pParent->m_pMatchingDirectory != NULL)
  1343. {
  1344. pChoice = pBaseTree->m_aHashTable[iHashHash];
  1345. while(pChoice != NULL)
  1346. {
  1347. if((m_pParent->m_pMatchingDirectory == pChoice->m_pParent) &&
  1348. (memcmp(m_Hash, pChoice->m_Hash, sizeof(m_Hash)) == 0))
  1349. {
  1350. eDetermination = DETERMINATION_RENAMED;
  1351. pLocalizedTree->m_cFilesRenamed++;
  1352. wcscpy(wszTempName,
  1353. pLocalizedTree->m_pAnsParse->m_wszBaseDirectory +
  1354. DRIVE_LETTER_LENGTH);
  1355. wcscat(wszTempName,
  1356. pChoice->m_wszLongFileName +
  1357. pBaseTree->m_pLanguage->s_iDirectoryCount);
  1358. pLocalizedTree->ToScriptFile(pLocalizedTree,
  1359. pLocalizedTree->m_pLanguage->s_hScriptFile,
  1360. ACTION_RENAME_FILE,
  1361. wszTempName,
  1362. m_wszLongFileName +
  1363. pLocalizedTree->m_pLanguage->s_iDirectoryCount,
  1364. FALSE);
  1365. goto done;
  1366. }
  1367. pChoice = pChoice->m_pNextHashHash;
  1368. }
  1369. }
  1370. // Rule 3
  1371. // What: files that are of the different name, same content, different location in base and localized tree,
  1372. // different location means that "same" directory but the directory name is localized
  1373. // What to do here: record the location of the files with a copy tag in the script file
  1374. // What to do later: the base file needs to be copied from the base to localized directory
  1375. pChoice = pBaseTree->m_aHashTable[iHashHash];
  1376. while(pChoice != NULL)
  1377. {
  1378. if(memcmp(m_Hash, pChoice->m_Hash, sizeof(m_Hash)) == 0)
  1379. {
  1380. eDetermination = DETERMINATION_COPIED;
  1381. pLocalizedTree->m_cFilesCopied++;
  1382. wcscpy(wszTempName,
  1383. pLocalizedTree->m_pAnsParse->m_wszBaseDirectory +
  1384. DRIVE_LETTER_LENGTH);
  1385. wcscat(wszTempName,
  1386. pChoice->m_wszLongFileName +
  1387. pBaseTree->m_pLanguage->s_iDirectoryCount);
  1388. pLocalizedTree->ToScriptFile(pLocalizedTree,
  1389. pLocalizedTree->m_pLanguage->s_hScriptFile,
  1390. ACTION_COPY_FILE,
  1391. wszTempName,
  1392. m_wszLongFileName +
  1393. pLocalizedTree->m_pLanguage->s_iDirectoryCount,
  1394. FALSE);
  1395. goto done;
  1396. }
  1397. pChoice = pChoice->m_pNextHashHash;
  1398. }
  1399. // Rule 4
  1400. // What: files with the same name, whatever location, different file content
  1401. // What to do here: record the location of the base file, patch file, and destination file with a patch tag in the script file
  1402. // What to do later: the localized file needs to be re-created from the base and patch file
  1403. pChoice = pBaseTree->m_aNameTable[iNameHash];
  1404. {
  1405. while(pChoice != NULL)
  1406. {
  1407. if((m_FileNameHash == pChoice->m_FileNameHash) &&
  1408. (wcscmp(m_wszFileName, pChoice->m_wszFileName) == 0))
  1409. {
  1410. eDetermination = DETERMINATION_PATCHED;
  1411. wcscpy(wszTempName,
  1412. pLocalizedTree->m_pLanguage->s_wszSubPatchDirectory);
  1413. wcscat(wszTempName, m_wszFileName);
  1414. wcscat(wszTempName, PATCH_EXT);
  1415. pLocalizedTree->m_cFilesChanged++;
  1416. pLocalizedTree->m_cbChangedFileSize += m_iFileSize;
  1417. goto done;
  1418. }
  1419. pChoice = pChoice->m_pNextNameHash;
  1420. }
  1421. }
  1422. // Rule 5
  1423. // What: files that are not matched, considered as unique
  1424. // What to do here: copy the file into the except directory for storage,
  1425. // record the location of the localized file with a saved file tag
  1426. // What to do later: move the except file to the localized file location
  1427. eDetermination = DETERMINATION_UNMATCHED;
  1428. pLocalizedTree->CopyFileTo(pLocalizedTree,
  1429. ACTION_SAVED_FILE,
  1430. pLocalizedTree->m_pLanguage->s_wszSubExceptDirectory,
  1431. m_wszFileName,
  1432. m_wszLongFileName,
  1433. 0);
  1434. pLocalizedTree->m_cFilesNoMatch++;
  1435. pLocalizedTree->m_cbNoMatchFileSize += m_iFileSize;
  1436. done:
  1437. // If it's a patching candidate, try it. Might demote to unmatched.
  1438. if(eDetermination == DETERMINATION_PATCHED)
  1439. {
  1440. // try to patch it
  1441. __int64 cbPatchedSize = 0;
  1442. WCHAR wszOldFile[STRING_LENGTH];
  1443. if((m_iFileSize < MAX_PATCH_TARGET_SIZE) &&
  1444. (BuildPatch(pChoice->m_wszLongFileName, m_wszLongFileName,
  1445. wszTempName, &cbPatchedSize) == PREP_NO_ERROR) &&
  1446. (cbPatchedSize < m_iFileSize))
  1447. {
  1448. // the oldfile is now in the base directory
  1449. wcscpy(wszOldFile,
  1450. pLocalizedTree->m_pAnsParse->m_wszBaseDirectory +
  1451. DRIVE_LETTER_LENGTH);
  1452. wcscat(wszOldFile, pChoice->m_wszLongFileName +
  1453. pBaseTree->m_pLanguage->s_iDirectoryCount);
  1454. // the patch file and the newfile
  1455. wcscat(wszTempName, SEPARATOR);
  1456. wcscat(wszTempName,
  1457. m_wszLongFileName +
  1458. pLocalizedTree->m_pLanguage->s_iDirectoryCount);
  1459. // record the information for apply the patch later
  1460. pLocalizedTree->ToScriptFile(pLocalizedTree,
  1461. pLocalizedTree->m_pLanguage->s_hScriptFile,
  1462. ACTION_PATCH_FILE,
  1463. wszOldFile,
  1464. wszTempName +
  1465. pLocalizedTree->m_pLanguage->s_iPatchDirectoryCount,
  1466. FALSE);
  1467. pLocalizedTree->m_cbChangedFilePatchedSize += cbPatchedSize;
  1468. }
  1469. else
  1470. {
  1471. // if un-success, move the file into the except directory and record the information, so the file
  1472. // can be moved to its original position later
  1473. eDetermination = DETERMINATION_UNMATCHED;
  1474. pLocalizedTree->CopyFileTo(pLocalizedTree,
  1475. ACTION_NOT_PATCH_FILE,
  1476. pLocalizedTree->m_pLanguage->s_wszSubExceptDirectory,
  1477. m_wszFileName,
  1478. m_wszLongFileName,
  1479. 0);
  1480. DeleteFileW(wszTempName);
  1481. pLocalizedTree->m_cbChangedFileNotPatchedSize += m_iFileSize;
  1482. }
  1483. }
  1484. if(g_blnFullLog)
  1485. {
  1486. DisplayDebugMessage(TRUE, FALSE, FALSE, FALSE,
  1487. L"N=%05I64u\tF=%ls\tS=%I64u",
  1488. pLocalizedTree->m_cFilesDetermined, m_wszFileName,
  1489. m_iFileSize);
  1490. }
  1491. return(PREP_NO_ERROR);
  1492. }
  1493. ///////////////////////////////////////////////////////////////////////////////
  1494. //
  1495. // BuildPatch, a wrapper function that handles the actual patching
  1496. //
  1497. // Parameters:
  1498. //
  1499. // pwszBaseFileName, the oldfile, the base file
  1500. // pwszLocFileName, the localized file
  1501. // pwszTempPatchFileName, the intended patch filename, can be changed
  1502. //
  1503. // Return:
  1504. //
  1505. // PREP_UNKNOWN_ERROR, something went wrong with the patch file created
  1506. // PREP_NOT_PATCHABLE, not patched
  1507. // PREP_NO_ERROR, no error
  1508. //
  1509. ///////////////////////////////////////////////////////////////////////////////
  1510. INT FileNode::BuildPatch(IN WCHAR* pwszBaseFileName,
  1511. IN WCHAR* pwszLocFileName,
  1512. IN OUT WCHAR* pwszTempPatchFileName,
  1513. OUT __int64* pcbPatchSize)
  1514. {
  1515. INT rc = PREP_NO_ERROR;
  1516. HANDLE hFile = INVALID_HANDLE_VALUE;
  1517. // choose a different name if possible
  1518. hFile = CreateFileW(pwszTempPatchFileName,
  1519. GENERIC_READ,
  1520. 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
  1521. if(hFile == INVALID_HANDLE_VALUE)
  1522. {
  1523. ULONG iLength = wcslen(pwszTempPatchFileName);
  1524. WCHAR wszRandom[10];
  1525. do
  1526. {
  1527. ZeroMemory(wszRandom, 10 * sizeof(WCHAR));
  1528. _itow(rand() % FILE_LIMIT, wszRandom, 10);
  1529. wcscpy(pwszTempPatchFileName + iLength, wszRandom);
  1530. hFile = CreateFileW(pwszTempPatchFileName,
  1531. GENERIC_READ,
  1532. 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
  1533. NULL);
  1534. }
  1535. while(hFile == INVALID_HANDLE_VALUE);
  1536. }
  1537. CloseHandle(hFile);
  1538. // do patch
  1539. if(CreatePatchFileW(pwszBaseFileName,
  1540. pwszLocFileName,
  1541. pwszTempPatchFileName,
  1542. g_iBestMethod,
  1543. NULL))
  1544. {
  1545. // collect the stats, don't do this unless it is necessary, open a file
  1546. // and close a file can take a long time
  1547. if(g_blnCollectStat)
  1548. {
  1549. HANDLE handle = CreateFileW(pwszTempPatchFileName,
  1550. GENERIC_READ,
  1551. 0,
  1552. NULL,
  1553. OPEN_EXISTING,
  1554. 0,
  1555. NULL);
  1556. if(handle != INVALID_HANDLE_VALUE)
  1557. {
  1558. DWORD dwFileSizeLow = 0;
  1559. DWORD dwFileSizeHigh = 0;
  1560. DWORD dwError = 0;
  1561. dwFileSizeLow = GetFileSize(handle, &dwFileSizeHigh);
  1562. dwError = GetLastError();
  1563. if((dwFileSizeLow == 0xFFFFFFFF) && (dwError != NO_ERROR))
  1564. {
  1565. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  1566. L"warning, Created a patch, but can't get patch file size. GLE=%08X, F=%ls",
  1567. dwError, pwszTempPatchFileName);
  1568. rc = PREP_UNKNOWN_ERROR;
  1569. }
  1570. else
  1571. {
  1572. *pcbPatchSize = (((__int64) dwFileSizeHigh) << 32) +
  1573. dwFileSizeLow;
  1574. rc = PREP_NO_ERROR;
  1575. }
  1576. CloseHandle(handle);
  1577. }
  1578. else
  1579. {
  1580. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  1581. L"warning, Created a patch, but can't open the patch file. GLE=%08X, F=%ls",
  1582. GetLastError(), pwszTempPatchFileName);
  1583. rc = PREP_UNKNOWN_ERROR;
  1584. }
  1585. }
  1586. }
  1587. else
  1588. {
  1589. DWORD dwError = GetLastError();
  1590. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  1591. L"warning, CreatePatchFileW(\"%ls\") failed, GLE=%08X",
  1592. pwszLocFileName, dwError);
  1593. rc = PREP_NOT_PATCHABLE;
  1594. }
  1595. return(rc);
  1596. }