CopyThread.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Installer *
  23. * *
  24. * $Archive:: /Commando/Code/Installer/CopyThread.cpp $*
  25. * *
  26. * $Author:: Ian_l $*
  27. * *
  28. * $Modtime:: 1/13/02 5:00p $*
  29. * *
  30. * $Revision:: 10 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Includes.
  36. #include "CopyThread.h"
  37. #include "ErrorHandler.h"
  38. #include "FDI.h"
  39. #include "Installer.h"
  40. #include "RegistryManager.h"
  41. #include "Resource.h"
  42. #include "Translator.h"
  43. #include "Verchk.h"
  44. #include <io.h>
  45. #include <fcntl.h>
  46. #include <sys/stat.h>
  47. #include <share.h>
  48. // Defines.
  49. #define COPY_MESSAGE_FORMAT_STRING L"%s %s"
  50. // Static data.
  51. CopyThreadClass *CopyThreadClass::_ActiveCopyThread = NULL;
  52. /***************************************************************************
  53. * Memory allocation function
  54. * HUGE * FAR DIAMONDAPI fdi_mem_alloc( ULONG cb )
  55. *=========================================================================*/
  56. FNALLOC( fdi_mem_alloc )
  57. {
  58. return (malloc (cb));
  59. }
  60. /***************************************************************************
  61. * Memory free function
  62. * FAR DIAMONDAPI fdi_mem_free( void HUGE *pv )
  63. *=========================================================================*/
  64. FNFREE( fdi_mem_free )
  65. {
  66. free (pv);
  67. }
  68. /***************************************************************************
  69. * int FAR DIAMONDAPI file_open( char FAR *pszFile, int oflag, int pmode )
  70. *=========================================================================*/
  71. FNOPEN( file_open )
  72. {
  73. HANDLE handle;
  74. WWASSERT ((oflag & (_O_APPEND | _O_TEMPORARY)) == 0x0);
  75. DWORD desiredaccess, creationdisposition, flagsandattributes;
  76. if (oflag & _O_WRONLY) {
  77. desiredaccess = GENERIC_WRITE;
  78. } else {
  79. if (oflag & _O_RDWR) {
  80. desiredaccess = GENERIC_READ | GENERIC_WRITE;
  81. } else {
  82. desiredaccess = GENERIC_READ;
  83. }
  84. }
  85. if (oflag & (_O_CREAT | _O_TRUNC)) {
  86. creationdisposition = OPEN_ALWAYS;
  87. } else {
  88. if (oflag & _O_CREAT) {
  89. creationdisposition = CREATE_NEW;
  90. } else {
  91. if (oflag & _O_TRUNC) {
  92. creationdisposition = TRUNCATE_EXISTING;
  93. } else {
  94. creationdisposition = OPEN_EXISTING;
  95. }
  96. }
  97. }
  98. if (pmode & _S_IREAD) {
  99. flagsandattributes = FILE_ATTRIBUTE_READONLY;
  100. } else {
  101. flagsandattributes = FILE_ATTRIBUTE_NORMAL;
  102. }
  103. while ((handle = CreateFile (pszFile, desiredaccess, FILE_SHARE_READ, NULL, creationdisposition, flagsandattributes | FILE_FLAG_WRITE_THROUGH, NULL)) == INVALID_HANDLE_VALUE) {
  104. if (!CopyThreadClass::_ActiveCopyThread->Retry()) break;
  105. }
  106. return ((int) handle);
  107. }
  108. /***************************************************************************
  109. * UINT FAR DIAMONDAPI file_read( int hf, void FAR *pv, UINT cb )
  110. *=========================================================================*/
  111. FNREAD( file_read )
  112. {
  113. // Abort the copying process?
  114. if (!CopyThreadClass::_ActiveCopyThread->Get_Abort (true)) {
  115. unsigned long bytecount;
  116. while (!ReadFile ((void*) hf, pv, cb, &bytecount, NULL)) {
  117. if (!CopyThreadClass::_ActiveCopyThread->Retry()) break;
  118. }
  119. return (bytecount);
  120. } else {
  121. // NOTE: Returning -1 will cause the calling cabinet process to close all
  122. // files, release its resources, and return an error code to the caller of
  123. // FDICopy().
  124. return (-1);
  125. }
  126. }
  127. /***************************************************************************
  128. * UINT FAR DIAMONDAPI file_write( int hf, void FAR *pv, UINT cb )
  129. *=========================================================================*/
  130. FNWRITE( file_write )
  131. {
  132. // Abort the copying process?
  133. if (!CopyThreadClass::_ActiveCopyThread->Get_Abort (true)) {
  134. unsigned long bytecount;
  135. while (!WriteFile ((void*) hf, pv, cb, &bytecount, NULL)) {
  136. if (!CopyThreadClass::_ActiveCopyThread->Retry()) break;
  137. }
  138. if (bytecount >= 0) {
  139. CopyThreadClass::_ActiveCopyThread->Add_Bytes_Copied ((unsigned)bytecount);
  140. }
  141. return (bytecount);
  142. } else {
  143. // NOTE: Returning -1 will cause the calling cabinet process to close all
  144. // files, release its resources, and return an error code to the caller of
  145. // FDICopy().
  146. return (-1);
  147. }
  148. }
  149. /***************************************************************************
  150. * int FAR DIAMONDAPI file_close( int hf )
  151. *=========================================================================*/
  152. FNCLOSE( file_close )
  153. {
  154. int result;
  155. while (!(result = CloseHandle ((void*) hf))) {
  156. if (!CopyThreadClass::_ActiveCopyThread->Retry()) break;
  157. }
  158. return (result ? 0 : -1);
  159. }
  160. /***************************************************************************
  161. * long FAR DIAMONDAPI file_seek( int hf, long dist, int seektype )
  162. *=========================================================================*/
  163. FNSEEK( file_seek )
  164. {
  165. long result;
  166. DWORD movemethod;
  167. // Map seek type to Win32.
  168. switch (seektype) {
  169. case SEEK_SET:
  170. movemethod = FILE_BEGIN;
  171. break;
  172. case SEEK_CUR:
  173. movemethod = FILE_CURRENT;
  174. break;
  175. case SEEK_END:
  176. movemethod = FILE_END;
  177. break;
  178. }
  179. while ((result = SetFilePointer ((void*) hf, dist, NULL, movemethod)) == 0xffffffff) {
  180. if (!CopyThreadClass::_ActiveCopyThread->Retry()) break;
  181. }
  182. return (result);
  183. }
  184. /******************************************************************************
  185. * int FAR DIAMONDAPI notification_function( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin )
  186. *=============================================================================*/
  187. FNFDINOTIFY( notification_function )
  188. {
  189. static WideStringClass _targetpathname;
  190. static StringClass _multibytetemporarypathname;
  191. static FILETIME _sourcefiletime;
  192. switch (fdint) {
  193. case fdintCOPY_FILE:
  194. {
  195. FILETIME localfiletime;
  196. // Build target pathname.
  197. // NOTE: If the relative path does not contain subdirectories it will not contain a backslash.
  198. CopyThreadClass::_ActiveCopyThread->Get_Target_Path (_targetpathname);
  199. if (pfdin->psz1 [0] != '\\') _targetpathname += L"\\";
  200. _targetpathname += WideStringClass (pfdin->psz1);
  201. _Installer.Log (_targetpathname, pfdin->cb);
  202. // If target should be overwritten...
  203. if (!DosDateTimeToFileTime (pfdin->date, pfdin->time, &localfiletime)) return (-1);
  204. if (!LocalFileTimeToFileTime (&localfiletime, &_sourcefiletime)) return (-1);
  205. if (CopyThreadClass::Replace_File (_sourcefiletime, pfdin->cb, _targetpathname)) {
  206. WideStringClass statusmessage;
  207. long handle;
  208. WideStringClass targetpath;
  209. WideStringClass filename;
  210. // Update the name of the current file being copied.
  211. filename = WideStringClass (pfdin->psz1);
  212. Extract_Trailing_Name (filename);
  213. statusmessage.Format (COPY_MESSAGE_FORMAT_STRING, TxWideStringClass (IDS_COPYING), filename);
  214. CopyThreadClass::_ActiveCopyThread->Set_Status_Message (statusmessage);
  215. // Create temporary filename.
  216. targetpath = _targetpathname;
  217. Remove_Trailing_Name (targetpath);
  218. if (!Generate_Temporary_Pathname (targetpath, _multibytetemporarypathname)) return (-1);
  219. // Open the temporary file for writing.
  220. handle = file_open (_multibytetemporarypathname.Peek_Buffer(), _O_BINARY | _O_TRUNC | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE);
  221. // Add temporary filename to log.
  222. CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Add (_multibytetemporarypathname);
  223. return (handle);
  224. } else {
  225. // Do not copy file.
  226. CopyThreadClass::_ActiveCopyThread->Add_Bytes_Copied ((unsigned) pfdin->cb);
  227. return (0);
  228. }
  229. }
  230. case fdintCLOSE_FILE_INFO:
  231. {
  232. StringClass multibytetargetpathname;
  233. HANDLE targetfile;
  234. WIN32_FIND_DATA targetfiledata;
  235. int count;
  236. // Close the temporary file.
  237. if (file_close (pfdin->hf) != 0) return (-1);
  238. // Delete the original target file (if it exists).
  239. multibytetargetpathname = _targetpathname;
  240. targetfile = FindFirstFile (multibytetargetpathname, &targetfiledata);
  241. if (targetfile != INVALID_HANDLE_VALUE) {
  242. if (!FindClose (targetfile)) return (-1);
  243. if (targetfiledata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  244. if (!SetFileAttributes (multibytetargetpathname, FILE_ATTRIBUTE_NORMAL)) return (-1);
  245. }
  246. if (!DeleteFile (multibytetargetpathname)) return (-1);
  247. }
  248. // Rename the temporary file the target file.
  249. if (!MoveFile (_multibytetemporarypathname, multibytetargetpathname)) return (-1);
  250. // Remove the temporary file from the file log (the last item added).
  251. count = CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Count();
  252. WWASSERT (count > 0);
  253. CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Delete (count - 1);
  254. // Add the target file to the file log.
  255. CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Add (multibytetargetpathname);
  256. // Stamp the target file with write time of source.
  257. targetfile = CreateFile (multibytetargetpathname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  258. if (targetfile == INVALID_HANDLE_VALUE) return (-1);
  259. if (!SetFileTime (targetfile, NULL, NULL, &_sourcefiletime)) return (-1);
  260. if (!CloseHandle (targetfile)) return (-1);
  261. // Stamp the target file with attributes of source.
  262. if (!SetFileAttributes (multibytetargetpathname, pfdin->attribs & (~FILE_ATTRIBUTE_READONLY))) return (-1);
  263. // Success.
  264. return (1);
  265. }
  266. case fdintNEXT_CABINET:
  267. {
  268. // Nothing to do. Assume that the next cabinet file is in the same directory as the current
  269. // cabinet file.
  270. return (1);
  271. }
  272. default:
  273. return (1);
  274. }
  275. }
  276. /***********************************************************************************************
  277. * CopyThreadClass::CopyThreadClass -- *
  278. * *
  279. * INPUT: *
  280. * *
  281. * OUTPUT: *
  282. * *
  283. * WARNINGS: *
  284. * *
  285. * HISTORY: *
  286. * 08/22/01 IML : Created. *
  287. *=============================================================================================*/
  288. CopyThreadClass::CopyThreadClass (__int64 bytestocopy)
  289. : ThreadClass(),
  290. Status (STATUS_OK),
  291. Abort (false),
  292. CanAbort (true),
  293. AbortLock (NULL),
  294. IsAborting (false),
  295. BytesToCopy (bytestocopy),
  296. BytesCopied (0)
  297. {
  298. // Only one instance can be active.
  299. WWASSERT (_ActiveCopyThread == NULL);
  300. _ActiveCopyThread = this;
  301. }
  302. /***********************************************************************************************
  303. * CopyThreadClass::~CopyThreadClass -- *
  304. * *
  305. * INPUT: *
  306. * *
  307. * OUTPUT: *
  308. * *
  309. * WARNINGS: *
  310. * *
  311. * HISTORY: *
  312. * 08/22/01 IML : Created. *
  313. *=============================================================================================*/
  314. CopyThreadClass::~CopyThreadClass()
  315. {
  316. WWASSERT (_ActiveCopyThread != NULL);
  317. _ActiveCopyThread = NULL;
  318. }
  319. /***********************************************************************************************
  320. * CopyThreadClass::Thread_Function -- *
  321. * *
  322. * INPUT: *
  323. * *
  324. * OUTPUT: *
  325. * *
  326. * WARNINGS: *
  327. * *
  328. * HISTORY: *
  329. * 08/22/01 IML : Created. *
  330. *=============================================================================================*/
  331. void CopyThreadClass::Thread_Function()
  332. {
  333. try {
  334. unsigned i;
  335. WideStringClass sourcepath, targetpath;
  336. int gamesubdirectorycount, gamefilecount;
  337. // Copy the game directory if user requested it.
  338. if (_Installer.Install_Game()) {
  339. // Create game subdirectories on target.
  340. // NOTE 0: This could have been done whilst copying, but is more efficient to do here,
  341. // considering that InstallerClass already has this information.
  342. // NOTE 1: Directory paths should have been validated, so any errors here are fatal.
  343. if (!Create_Directory (_Installer.Get_Target_Game_Path (targetpath), &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR;
  344. i = 0;
  345. while (_Installer.Get_Target_Sub_Path (i, targetpath)) {
  346. _Installer.Log (targetpath);
  347. if (!Create_Directory (targetpath, &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR;
  348. i++;
  349. }
  350. Copy_Directory (_Installer.Get_Source_Game_Path (sourcepath), _Installer.Get_Target_Game_Path (targetpath));
  351. }
  352. gamesubdirectorycount = Get_Subdirectory_Log().Count();
  353. gamefilecount = Get_Filename_Log().Count();
  354. if (Is_Aborting()) goto abort;
  355. // Copy the WOL directory if user requested it.
  356. if (_Installer.Install_WOL()) {
  357. Copy_Directory (_Installer.Get_Source_WOL_Path (sourcepath), _Installer.Get_Target_WOL_Path (targetpath));
  358. }
  359. // Find out if the user wants to abort. At the same time disable the user's ability to abort
  360. // because the operations to follow are irreversible ie. this is the last chance for the
  361. // user to abort.
  362. if (Get_Abort (false)) goto abort;
  363. // Update registry and make start menu items.
  364. Set_Status_Message (TxWideStringClass (IDS_UPDATING_REGISTRY));
  365. _Installer.Update_Registry();
  366. // Make menu items and icons.
  367. Set_Status_Message (TxWideStringClass (IDS_MAKING_MENU_ITEMS));
  368. _Installer.Create_Links();
  369. // Create uninstall logs.
  370. Set_Status_Message (TxWideStringClass (IDS_CREATING_UNINSTALL_LOGS));
  371. _Installer.Create_Uninstall_Logs();
  372. // Indicate completion.
  373. Set_Status_Message (TxWideStringClass (IDS_INSTALLATION_COMPLETE));
  374. Status = STATUS_SUCCESS;
  375. return;
  376. abort:
  377. Set_Status_Message (WideStringClass (L""));
  378. Status = STATUS_ABORTED;
  379. // Remove installed components if game or WOL was freshly installed.
  380. if (_Installer.Is_Fresh_Game_Install() || _Installer.Is_Fresh_WOL_Install()) {
  381. int i;
  382. Set_Status_Message (TxWideStringClass (IDS_REMOVING_INSTALLED_COMPONENTS));
  383. // NOTE: Order of removal must be inverse of order of creation to ensure that leaf
  384. // subdirectories are removed before parent directories. To satisfy this
  385. // requirement, remove in the following order:
  386. // 1. WOL files (in any order).
  387. // 2. Game files (in any order).
  388. // 3. WOL subdirectories (in reverse order).
  389. // 4. Game subdirectories (in reverse order).
  390. if (_Installer.Is_Fresh_WOL_Install()) {
  391. for (i = gamefilecount; i < Get_Filename_Log().Count(); i++) {
  392. DeleteFile (Get_Filename_Log() [i]);
  393. }
  394. }
  395. if (_Installer.Is_Fresh_Game_Install()) {
  396. for (i = 0; i < gamefilecount; i++) {
  397. DeleteFile (Get_Filename_Log() [i]);
  398. }
  399. }
  400. if (_Installer.Is_Fresh_WOL_Install()) {
  401. for (i = Get_Subdirectory_Log().Count() - 1; i >= gamesubdirectorycount; i--) {
  402. RemoveDirectory (Get_Subdirectory_Log() [i]);
  403. }
  404. }
  405. if (_Installer.Is_Fresh_Game_Install()) {
  406. for (i = gamesubdirectorycount - 1; i >= 0; i--) {
  407. RemoveDirectory (Get_Subdirectory_Log() [i]);
  408. }
  409. }
  410. // Give time for user to read status message.
  411. Sleep (5000);
  412. }
  413. } catch (const WideStringClass &errormessage) {
  414. // Copy off the errormessage so that it can be interrogated by the thread creator.
  415. Set_Status_Message (WideStringClass (L""));
  416. Set_Error_Message (errormessage);
  417. Status = STATUS_FAILURE;
  418. }
  419. }
  420. /***********************************************************************************************
  421. * CopyThreadClass::Copy_Directory -- *
  422. * *
  423. * INPUT: *
  424. * *
  425. * OUTPUT: *
  426. * *
  427. * WARNINGS: *
  428. * *
  429. * HISTORY: *
  430. * 08/22/01 IML : Created. *
  431. *=============================================================================================*/
  432. void CopyThreadClass::Copy_Directory (const WideStringClass &sourcepath, const WideStringClass &targetpath)
  433. {
  434. // WARNING: Do not call SetCurrentDirectory() from this thread because it will affect
  435. // file access for the entire process.
  436. const WCHAR *wildcardname = L"*.*";
  437. HFDI hfdi;
  438. ERF erf;
  439. WideStringClass sourcepathname;
  440. StringClass multibytesourcepathname;
  441. WIN32_FIND_DATA finddata;
  442. HANDLE handle;
  443. bool done = false;
  444. hfdi = FDICreate (fdi_mem_alloc, // Memory allocation function.
  445. fdi_mem_free, // Memory free function.
  446. file_open, // File open function.
  447. file_read, // File read function.
  448. file_write, // File write function.
  449. file_close, // File close function.
  450. file_seek, // File seek function.
  451. cpu80386, // Type of CPU.
  452. &erf); // Pointer to error structure.
  453. WWASSERT (hfdi != NULL);
  454. // Create subdirectory (if it doesn't already exist).
  455. if (!Create_Directory (targetpath, &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR;
  456. // Iterate over files in source directory and copy to target directory.
  457. sourcepathname = sourcepath;
  458. sourcepathname += L"\\";
  459. sourcepathname += wildcardname;
  460. multibytesourcepathname = sourcepathname;
  461. // Find a source file.
  462. // NOTE At least one file must be in the directory.
  463. while ((handle = FindFirstFile (multibytesourcepathname, &finddata)) == INVALID_HANDLE_VALUE) {
  464. if (!Retry()) FATAL_SYSTEM_ERROR;
  465. }
  466. while (!done && !Is_Aborting()) {
  467. WideStringClass filename (finddata.cFileName);
  468. // Filter out system files.
  469. if (filename [0] != L'.') {
  470. // Is it a subdirectory?
  471. if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  472. WideStringClass subsourcepath (sourcepath);
  473. WideStringClass subtargetpath (targetpath);
  474. // Recurse.
  475. subsourcepath += L"\\";
  476. subsourcepath += filename;
  477. subtargetpath += L"\\";
  478. subtargetpath += filename;
  479. _Installer.Log (subtargetpath);
  480. Copy_Directory (subsourcepath, subtargetpath);
  481. } else {
  482. const WCHAR *cabextension = L".cab";
  483. WCHAR extension [_MAX_EXT];
  484. // Is it a CAB file?
  485. _wsplitpath (filename, NULL, NULL, NULL, extension);
  486. if (_wcsicmp (cabextension, extension) == 0) {
  487. StringClass sourcepathbackslash (sourcepath);
  488. // NOTE: FDICopy() requires a trailing backslash on the source path.
  489. sourcepathbackslash += "\\";
  490. Set_Target_Path (targetpath);
  491. // Copy the contents of the cabinet file to the target directory.
  492. // If the function fails then throw a 'cabinet error'.
  493. if (!FDICopy (hfdi, // Handle to FDI context (created by FDICreate()).
  494. finddata.cFileName, // Name of cabinet file, excluding path information.
  495. sourcepathbackslash.Peek_Buffer(), // File path to cabinet file.
  496. 0, // Flags to control extract operation.
  497. notification_function, // Ptr to a notification (status update) function.
  498. NULL, // Ptr to a decryption function.
  499. NULL))
  500. // If failure was not due to user aborting then throw an error.
  501. if (!Is_Aborting()) FATAL_CAB_ERROR (erf.erfOper);
  502. } else {
  503. // Update the name of the current file being copied.
  504. WideStringClass statusmessage;
  505. __int64 filesize;
  506. statusmessage.Format (COPY_MESSAGE_FORMAT_STRING, TxWideStringClass (IDS_COPYING), filename);
  507. Set_Status_Message (statusmessage);
  508. // It's a regular file.
  509. WideStringClass sourcepathname (sourcepath);
  510. WideStringClass targetpathname (targetpath);
  511. sourcepathname += L"\\";
  512. sourcepathname += filename;
  513. targetpathname += L"\\";
  514. targetpathname += filename;
  515. filesize = ((((__int64)MAXDWORD) + 1) * ((__int64) finddata.nFileSizeHigh)) + ((__int64) finddata.nFileSizeLow);
  516. _Installer.Log (targetpathname, filesize);
  517. // If target should be overwitten...
  518. if (Replace_File (sourcepathname, targetpathname)) {
  519. Copy_File (sourcepathname, targetpathname);
  520. } else {
  521. BytesCopied += filesize;
  522. }
  523. }
  524. }
  525. }
  526. if (Is_Aborting()) break;
  527. while (done = (FindNextFile (handle, &finddata) == 0)) {
  528. if (GetLastError() == ERROR_NO_MORE_FILES) break;
  529. if (!Retry()) FATAL_SYSTEM_ERROR;
  530. }
  531. }
  532. while (!FindClose (handle)) {
  533. if (!Retry()) FATAL_SYSTEM_ERROR;
  534. }
  535. FDIDestroy (hfdi);
  536. }
  537. /***********************************************************************************************
  538. * CopyThreadClass::Copy_File -- *
  539. * *
  540. * INPUT: *
  541. * *
  542. * OUTPUT: *
  543. * *
  544. * WARNINGS: *
  545. * *
  546. * HISTORY: *
  547. * 08/22/01 IML : Created. *
  548. *=============================================================================================*/
  549. void CopyThreadClass::Copy_File (const WideStringClass &sourcepathname, const WideStringClass &targetpathname)
  550. {
  551. const unsigned buffersize = 32768;
  552. static char _buffer [buffersize];
  553. StringClass multibytesourcepathname, multibytetargetpathname;
  554. WideStringClass targetpath;
  555. StringClass multibytetargetpath, multibytetemporarypathname;
  556. HANDLE sourcefile, temporaryfile, targetfile;
  557. DWORD sourcebytecount, bytecount;
  558. WIN32_FIND_DATA sourcefiledata, targetfiledata;
  559. multibytesourcepathname = sourcepathname;
  560. multibytetargetpathname = targetpathname;
  561. // Open the read file.
  562. while ((sourcefile = CreateFile (multibytesourcepathname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
  563. if (!Retry()) FATAL_SYSTEM_ERROR;
  564. }
  565. // Create temporary file.
  566. targetpath = targetpathname;
  567. Remove_Trailing_Name (targetpath);
  568. if (!Generate_Temporary_Pathname (targetpath, multibytetemporarypathname)) FATAL_SYSTEM_ERROR;
  569. // Open the temporary file for writing.
  570. temporaryfile = CreateFile (multibytetemporarypathname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
  571. if (temporaryfile == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;
  572. do {
  573. // Abort the copying process?
  574. if (Get_Abort (true)) break;
  575. while (!ReadFile (sourcefile, _buffer, buffersize, &sourcebytecount, NULL)) {
  576. if (!Retry()) FATAL_SYSTEM_ERROR;
  577. }
  578. if (!WriteFile (temporaryfile, _buffer, sourcebytecount, &bytecount, NULL)) FATAL_SYSTEM_ERROR;
  579. BytesCopied += sourcebytecount;
  580. } while (sourcebytecount == buffersize);
  581. // Close.
  582. while (!CloseHandle (sourcefile)) {
  583. if (!Retry()) FATAL_SYSTEM_ERROR;
  584. }
  585. while (!CloseHandle (temporaryfile)) {
  586. if (!Retry()) FATAL_SYSTEM_ERROR;
  587. }
  588. while ((sourcefile = FindFirstFile (multibytesourcepathname, &sourcefiledata)) == INVALID_HANDLE_VALUE) {
  589. if (!Retry()) FATAL_SYSTEM_ERROR;
  590. }
  591. while (!FindClose (sourcefile)) {
  592. if (!Retry()) FATAL_SYSTEM_ERROR;
  593. }
  594. // Delete the original target file (if it exists).
  595. targetfile = FindFirstFile (multibytetargetpathname, &targetfiledata);
  596. if (targetfile != INVALID_HANDLE_VALUE) {
  597. if (!FindClose (targetfile)) FATAL_SYSTEM_ERROR;
  598. if (targetfiledata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  599. if (!SetFileAttributes (multibytetargetpathname, FILE_ATTRIBUTE_NORMAL)) FATAL_SYSTEM_ERROR;
  600. }
  601. if (!DeleteFile (multibytetargetpathname)) FATAL_SYSTEM_ERROR;
  602. }
  603. // Rename the temporary file the original target file.
  604. if (!MoveFile (multibytetemporarypathname, multibytetargetpathname)) FATAL_SYSTEM_ERROR;
  605. // Add the name of the file to the file log.
  606. Get_Filename_Log().Add (multibytetargetpathname);
  607. // Stamp the target file with write time of source.
  608. targetfile = CreateFile (multibytetargetpathname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  609. if (targetfile == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;
  610. if (!SetFileTime (targetfile, NULL, NULL, &sourcefiledata.ftLastWriteTime)) FATAL_SYSTEM_ERROR;
  611. if (!CloseHandle (targetfile)) FATAL_SYSTEM_ERROR;
  612. // Stamp the target file with file attributes of source (but clear the read-only flag to ensure that
  613. // the file can be patched at a later date).
  614. if (!SetFileAttributes (multibytetargetpathname, sourcefiledata.dwFileAttributes & (~FILE_ATTRIBUTE_READONLY))) FATAL_SYSTEM_ERROR;
  615. }
  616. /***********************************************************************************************
  617. * Replace_File -- *
  618. * *
  619. * INPUT: *
  620. * *
  621. * OUTPUT: *
  622. * *
  623. * WARNINGS: *
  624. * *
  625. * HISTORY: *
  626. * 08/22/01 IML : Created. *
  627. *=============================================================================================*/
  628. bool CopyThreadClass::Replace_File (const FILETIME &sourcefiletime, DWORD sourcefilesize, const WideStringClass &targetpathname)
  629. {
  630. StringClass multibytetargetpathname (targetpathname);
  631. WIN32_FIND_DATA targetdata;
  632. HANDLE handle;
  633. long t;
  634. // Does the target file exist?
  635. handle = FindFirstFile (multibytetargetpathname, &targetdata);
  636. if (handle == INVALID_HANDLE_VALUE) return (true);
  637. // Test last write times.
  638. t = CompareFileTime (&sourcefiletime, &targetdata.ftLastWriteTime);
  639. if (t > 0) return (true);
  640. if (t < 0) return (false);
  641. // So far files are deemed equal. To err on the side of caution, test file sizes and elect to replace the file if sizes differ.
  642. return ((sourcefilesize != targetdata.nFileSizeLow) || (targetdata.nFileSizeHigh != 0));
  643. }
  644. /***********************************************************************************************
  645. * Replace_File -- *
  646. * *
  647. * INPUT: *
  648. * *
  649. * OUTPUT: *
  650. * *
  651. * WARNINGS: *
  652. * *
  653. * HISTORY: *
  654. * 08/22/01 IML : Created. *
  655. *=============================================================================================*/
  656. bool CopyThreadClass::Replace_File (const WideStringClass &sourcepathname, const WideStringClass &targetpathname)
  657. {
  658. StringClass multibytesourcepathname (sourcepathname);
  659. StringClass multibytetargetpathname (targetpathname);
  660. WIN32_FIND_DATA sourcedata, targetdata;
  661. HANDLE handle;
  662. VS_FIXEDFILEINFO sourceversion, targetversion;
  663. long t;
  664. // Find source file. It must exist.
  665. while ((handle = FindFirstFile (multibytesourcepathname, &sourcedata)) == INVALID_HANDLE_VALUE) {
  666. if (!_ActiveCopyThread->Retry()) FATAL_SYSTEM_ERROR;
  667. }
  668. // Does the target file exist?
  669. handle = FindFirstFile (multibytetargetpathname, &targetdata);
  670. if (handle == INVALID_HANDLE_VALUE) return (true);
  671. // Test version numbers (if they exist).
  672. if (GetVersionInfo (multibytesourcepathname.Peek_Buffer(), &sourceversion) && GetVersionInfo (multibytetargetpathname.Peek_Buffer(), &targetversion)) {
  673. if (sourceversion.dwFileVersionMS > targetversion.dwFileVersionMS) return (true);
  674. if (sourceversion.dwFileVersionMS < targetversion.dwFileVersionMS) return (false);
  675. if (sourceversion.dwFileVersionLS > targetversion.dwFileVersionLS) return (true);
  676. if (sourceversion.dwFileVersionLS < targetversion.dwFileVersionLS) return (false);
  677. }
  678. // Test last write times.
  679. t = CompareFileTime (&sourcedata.ftLastWriteTime, &targetdata.ftLastWriteTime);
  680. if (t > 0) return (true);
  681. if (t < 0) return (false);
  682. // So far files are deemed equal. To err on the side of caution, test file sizes and elect to replace the file if sizes differ.
  683. return ((sourcedata.nFileSizeLow != targetdata.nFileSizeLow) || (sourcedata.nFileSizeHigh != targetdata.nFileSizeHigh));
  684. }
  685. /***********************************************************************************************
  686. * CopyThreadClass::Can_Abort -- *
  687. * *
  688. * INPUT: *
  689. * *
  690. * OUTPUT: *
  691. * *
  692. * WARNINGS: *
  693. * *
  694. * HISTORY: *
  695. * 08/22/01 IML : Created. *
  696. *=============================================================================================*/
  697. bool CopyThreadClass::Can_Abort (bool lock)
  698. {
  699. if (lock) {
  700. if (AbortLock == NULL) {
  701. AbortLock = new FastCriticalSectionClass::LockClass (SectionAbort);
  702. }
  703. return (CanAbort);
  704. } else {
  705. FastCriticalSectionClass::LockClass cs (SectionAbort);
  706. return (CanAbort);
  707. }
  708. }
  709. /***********************************************************************************************
  710. * CopyThreadClass::Set_Abort -- *
  711. * *
  712. * INPUT: *
  713. * *
  714. * OUTPUT: *
  715. * *
  716. * WARNINGS: *
  717. * *
  718. * HISTORY: *
  719. * 08/22/01 IML : Created. *
  720. *=============================================================================================*/
  721. void CopyThreadClass::Set_Abort (bool abort)
  722. {
  723. Abort = abort;
  724. WWASSERT (AbortLock != NULL);
  725. delete AbortLock;
  726. AbortLock = NULL;
  727. }
  728. /***********************************************************************************************
  729. * CopyThreadClass::Get_Abort -- *
  730. * *
  731. * INPUT: *
  732. * *
  733. * OUTPUT: *
  734. * *
  735. * WARNINGS: *
  736. * *
  737. * HISTORY: *
  738. * 08/22/01 IML : Created. *
  739. *=============================================================================================*/
  740. bool CopyThreadClass::Get_Abort (bool canabort)
  741. {
  742. FastCriticalSectionClass::LockClass cs (SectionAbort);
  743. CanAbort = canabort;
  744. if (Abort) IsAborting = true;
  745. return (Abort);
  746. }
  747. /***********************************************************************************************
  748. * CopyThreadClass::Add_Bytes_Copied -- *
  749. * *
  750. * INPUT: *
  751. * *
  752. * OUTPUT: *
  753. * *
  754. * WARNINGS: *
  755. * *
  756. * HISTORY: *
  757. * 08/22/01 IML : Created. *
  758. *=============================================================================================*/
  759. void CopyThreadClass::Add_Bytes_Copied (unsigned bytecount)
  760. {
  761. FastCriticalSectionClass::LockClass cs (SectionBytesCopied);
  762. BytesCopied += bytecount;
  763. }
  764. /***********************************************************************************************
  765. * CopyThreadClass::Get_Fraction_Complete -- *
  766. * *
  767. * INPUT: *
  768. * *
  769. * OUTPUT: *
  770. * *
  771. * WARNINGS: *
  772. * *
  773. * HISTORY: *
  774. * 08/22/01 IML : Created. *
  775. *=============================================================================================*/
  776. float CopyThreadClass::Get_Fraction_Complete()
  777. {
  778. FastCriticalSectionClass::LockClass cs (SectionBytesCopied);
  779. float fraction;
  780. fraction = ((double) BytesCopied) / ((double) BytesToCopy);
  781. // Clamp to valid range.
  782. fraction = MAX (0.0f, MIN (1.0f, fraction));
  783. return (fraction);
  784. }
  785. /***********************************************************************************************
  786. * CopyThreadClass::Set\Get_Target_Path -- *
  787. * *
  788. * INPUT: *
  789. * *
  790. * OUTPUT: *
  791. * *
  792. * WARNINGS: *
  793. * *
  794. * HISTORY: *
  795. * 08/22/01 IML : Created. *
  796. *=============================================================================================*/
  797. void CopyThreadClass::Set_Target_Path (const WideStringClass &targetpath)
  798. {
  799. FastCriticalSectionClass::LockClass cs (SectionTargetPath);
  800. TargetPath = targetpath;
  801. }
  802. WCHAR *CopyThreadClass::Get_Target_Path (WideStringClass &targetpath)
  803. {
  804. FastCriticalSectionClass::LockClass cs (SectionTargetPath);
  805. targetpath = TargetPath;
  806. return (targetpath.Peek_Buffer());
  807. }
  808. /***********************************************************************************************
  809. * CopyThreadClass::Set\Get_Status_Message -- *
  810. * *
  811. * INPUT: *
  812. * *
  813. * OUTPUT: *
  814. * *
  815. * WARNINGS: *
  816. * *
  817. * HISTORY: *
  818. * 08/22/01 IML : Created. *
  819. *=============================================================================================*/
  820. void CopyThreadClass::Set_Status_Message (const WideStringClass &statusmessage)
  821. {
  822. FastCriticalSectionClass::LockClass cs (SectionStatusMessage);
  823. StatusMessage = statusmessage;
  824. }
  825. WCHAR *CopyThreadClass::Get_Status_Message (WideStringClass &statusmessage)
  826. {
  827. FastCriticalSectionClass::LockClass cs (SectionStatusMessage);
  828. statusmessage = StatusMessage;
  829. return (statusmessage.Peek_Buffer());
  830. }
  831. /***********************************************************************************************
  832. * CopyThreadClass::Set\Get_Error_Message -- *
  833. * *
  834. * INPUT: *
  835. * *
  836. * OUTPUT: *
  837. * *
  838. * WARNINGS: *
  839. * *
  840. * HISTORY: *
  841. * 08/22/01 IML : Created. *
  842. *=============================================================================================*/
  843. void CopyThreadClass::Set_Error_Message (const WideStringClass &errormessage)
  844. {
  845. FastCriticalSectionClass::LockClass cs (SectionErrorMessage);
  846. ErrorMessage = errormessage;
  847. }
  848. WCHAR *CopyThreadClass::Get_Error_Message (WideStringClass &errormessage)
  849. {
  850. FastCriticalSectionClass::LockClass cs (SectionErrorMessage);
  851. errormessage = ErrorMessage;
  852. return (errormessage.Peek_Buffer());
  853. }
  854. /***********************************************************************************************
  855. * CopyThreadClass::Set\Get_Status -- *
  856. * *
  857. * INPUT: *
  858. * *
  859. * OUTPUT: *
  860. * *
  861. * WARNINGS: *
  862. * *
  863. * HISTORY: *
  864. * 08/22/01 IML : Created. *
  865. *=============================================================================================*/
  866. void CopyThreadClass::Set_Status (StatusEnum status)
  867. {
  868. FastCriticalSectionClass::LockClass cs (SectionStatus);
  869. Status = status;
  870. }
  871. CopyThreadClass::StatusEnum CopyThreadClass::Get_Status()
  872. {
  873. FastCriticalSectionClass::LockClass cs (SectionStatus);
  874. return (Status);
  875. }
  876. /***********************************************************************************************
  877. * CopyThreadClass::Retry -- *
  878. * *
  879. * INPUT: *
  880. * *
  881. * OUTPUT: *
  882. * *
  883. * WARNINGS: *
  884. * *
  885. * HISTORY: *
  886. * 08/22/01 IML : Created. *
  887. *=============================================================================================*/
  888. bool CopyThreadClass::Retry()
  889. {
  890. CopyThreadClass::StatusEnum status;
  891. Set_Status (STATUS_ERROR);
  892. while ((status = Get_Status()) == STATUS_ERROR) {
  893. Sleep (50);
  894. }
  895. return (status == STATUS_RETRY);
  896. }