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.

1556 lines
38 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. decomp.c
  5. Abstract:
  6. File decompression support routines.
  7. Author:
  8. Ted Miller (tedm) 1-Feb-1995
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #include <pshpack1.h>
  14. struct LZINFO;
  15. typedef struct LZINFO *PLZINFO;
  16. #include <lz_header.h>
  17. #include <poppack.h>
  18. typedef struct _SFD_INFO {
  19. unsigned FileCount;
  20. PCTSTR TargetFile;
  21. BOOL GotTimestamp;
  22. FILETIME FileTime;
  23. } SFD_INFO, *PSFD_INFO;
  24. UINT
  25. pGetCompressInfoCB(
  26. IN PVOID Context,
  27. IN UINT Notification,
  28. IN UINT_PTR Param1,
  29. IN UINT_PTR Param2
  30. );
  31. UINT
  32. pSingleFileDecompCB(
  33. IN PVOID Context,
  34. IN UINT Notification,
  35. IN UINT_PTR Param1,
  36. IN UINT_PTR Param2
  37. );
  38. //
  39. // OldMyMalloc/OldMyFree used by SetupGetFileCompressionInfo
  40. // for app-compat
  41. //
  42. PVOID
  43. OldMyMalloc(
  44. IN DWORD Size
  45. );
  46. VOID
  47. OldMyFree(
  48. IN PVOID Block
  49. );
  50. PTSTR
  51. SetupGenerateCompressedName(
  52. IN PCTSTR Filename
  53. )
  54. /*++
  55. Routine Description:
  56. Given a filename, generate the compressed form of the name.
  57. The compressed form is generated as follows:
  58. Look backwards for a dot. If there is no dot, append "._" to the name.
  59. If there is a dot followed by 0, 1, or 2 charcaters, append "_".
  60. Otherwise there is a 3-character or greater extension and we replace
  61. the last character with "_".
  62. Arguments:
  63. Filename - supplies filename whose compressed form is desired.
  64. Return Value:
  65. Pointer to buffer containing nul-terminated compressed-form filename.
  66. The caller must free this buffer via MyFree().
  67. --*/
  68. {
  69. PTSTR CompressedName,p,q;
  70. UINT u;
  71. //
  72. // The maximum length of the compressed filename is the length of the
  73. // original name plus 2 (for ._).
  74. //
  75. if(CompressedName = MyMalloc((lstrlen(Filename)+3)*sizeof(TCHAR))) {
  76. lstrcpy(CompressedName,Filename);
  77. p = _tcsrchr(CompressedName,TEXT('.'));
  78. q = _tcsrchr(CompressedName,TEXT('\\'));
  79. if(q < p) {
  80. //
  81. // If there are 0, 1, or 2 characters after the dot, just append
  82. // the underscore. p points to the dot so include that in the length.
  83. //
  84. u = lstrlen(p);
  85. if(u < 4) {
  86. lstrcat(CompressedName,TEXT("_"));
  87. } else {
  88. //
  89. // There are at least 3 characters in the extension.
  90. // Replace the final one with an underscore.
  91. //
  92. p[u-1] = TEXT('_');
  93. }
  94. } else {
  95. //
  96. // No dot, just add ._.
  97. //
  98. lstrcat(CompressedName,TEXT("._"));
  99. }
  100. }
  101. return(CompressedName);
  102. }
  103. DWORD
  104. pSetupAttemptLocate(
  105. IN PCTSTR FileName,
  106. OUT PBOOL Found,
  107. OUT PWIN32_FIND_DATA FindData
  108. )
  109. /*++
  110. Routine Description:
  111. Attempt to locate a source file via FindFirstFile().
  112. Errors of the 'file not found' type are not considered errors
  113. and result in NO_ERROR. Any non-NO_ERROR return indicates that
  114. we could not determine whether the file is present or not
  115. because of some hardware or system problem, etc.
  116. Arguments:
  117. FileName - supplies filename of the file to be located.
  118. Found - receives a value indicating whether the file was found.
  119. This value is only valid when the function returns NO_ERROR.
  120. FindData - if found, returns win32 find data for the file.
  121. Return Value:
  122. Win32 error code indicating the outcome. If NO_ERROR, check
  123. the Found return value to see whether the file was found.
  124. --*/
  125. {
  126. DWORD d;
  127. if(*Found = FileExists(FileName,FindData)) {
  128. d = NO_ERROR;
  129. } else {
  130. //
  131. // We didn't find the file. See whether that was because
  132. // the file wasn't there or because some other error occured.
  133. //
  134. d = GetLastError();
  135. if((d == ERROR_NO_MORE_FILES)
  136. || (d == ERROR_FILE_NOT_FOUND)
  137. || (d == ERROR_PATH_NOT_FOUND)
  138. || (d == ERROR_BAD_NETPATH))
  139. {
  140. d = NO_ERROR;
  141. }
  142. }
  143. return(d);
  144. }
  145. DWORD
  146. SetupDetermineSourceFileName(
  147. IN PCTSTR FileName,
  148. OUT PBOOL UsedCompressedName,
  149. OUT PTSTR *FileNameLocated,
  150. OUT PWIN32_FIND_DATA FindData
  151. )
  152. /*++
  153. Routine Description:
  154. Attempt to locate a source file whose name can be compressed
  155. or uncompressed.
  156. The order of attempt is
  157. - the name as given (should be the uncompressed name)
  158. - the compressed form, using _ as the compression char
  159. - the compressed form, using $ as the compression char
  160. Arguments:
  161. FileName - supplies filename of the file to be located.
  162. UsedCompressedName - receives a boolean indicating whether
  163. the filename we located seems to indicate that the file
  164. is compressed.
  165. FileNameLocated - receives a pointer to the filename actually
  166. located. The caller must free with MyFree().
  167. FindData - if found, returns win32 find data for the file.
  168. Return Value:
  169. Win32 error code indicating the outcome.
  170. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  171. but we can't find the file
  172. NO_ERROR - file was located; check UsedCompressedName and FileNameOpened.
  173. Others - something is wrong with the hardware or system.
  174. --*/
  175. {
  176. DWORD d;
  177. PTSTR TryName;
  178. BOOL Found;
  179. TryName = DuplicateString(FileName);
  180. if(!TryName) {
  181. return(ERROR_NOT_ENOUGH_MEMORY);
  182. }
  183. *UsedCompressedName = FALSE;
  184. *FileNameLocated = TryName;
  185. d = pSetupAttemptLocate(TryName,&Found,FindData);
  186. if(d != NO_ERROR) {
  187. MyFree(TryName);
  188. *FileNameLocated = NULL;
  189. return(d);
  190. }
  191. if(Found) {
  192. return(NO_ERROR);
  193. }
  194. MyFree(TryName);
  195. *UsedCompressedName = TRUE;
  196. *FileNameLocated = NULL;
  197. TryName = SetupGenerateCompressedName(FileName);
  198. if(!TryName) {
  199. return(ERROR_NOT_ENOUGH_MEMORY);
  200. }
  201. *FileNameLocated = TryName;
  202. d = pSetupAttemptLocate(TryName,&Found,FindData);
  203. if(d != NO_ERROR) {
  204. MyFree(TryName);
  205. *FileNameLocated = NULL;
  206. return(d);
  207. }
  208. if(Found) {
  209. return(NO_ERROR);
  210. }
  211. MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_'));
  212. TryName[lstrlen(TryName)-1] = TEXT('$');
  213. d = pSetupAttemptLocate(TryName,&Found,FindData);
  214. if((d != NO_ERROR) || !Found) {
  215. *FileNameLocated = NULL;
  216. MyFree(TryName);
  217. }
  218. return(Found ? NO_ERROR : ERROR_FILE_NOT_FOUND);
  219. }
  220. BOOL
  221. pSetupDoesFileMatch(
  222. IN PCTSTR InputName,
  223. IN PCTSTR CompareName,
  224. OUT PBOOL UsedCompressedName,
  225. OUT PTSTR *FileNameLocated
  226. )
  227. /*++
  228. Routine Description:
  229. determine if the specified input file matches the
  230. name to compare it with. We try the undecorated name
  231. as well as the compressed versions of the file name.
  232. The order of attempt is
  233. - the name as given (should be the uncompressed name)
  234. - the compressed form, using _ as the compression char
  235. - the compressed form, using $ as the compression char
  236. Arguments:
  237. FileName - supplies filename we're looking at.
  238. CompareName -supplies the filename we're comparing against
  239. UsedCompressedName - receives a boolean indicating whether
  240. the filename we located seems to indicate that the file
  241. is compressed.
  242. FileNameLocated - receives a pointer to the filename actually
  243. located. The caller must free with MyFree().
  244. Return Value:
  245. Win32 error code indicating the outcome.
  246. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  247. but we can't find the file
  248. NO_ERROR - file was located; check UsedCompressedName and FileNameOpened.
  249. Others - something is wrong with the hardware or system.
  250. --*/
  251. {
  252. DWORD d;
  253. PTSTR TryName,TargetName,src,dst;
  254. BOOL Found;
  255. TryName = DuplicateString(InputName);
  256. if(!TryName) {
  257. return(FALSE);
  258. }
  259. TargetName = DuplicateString(CompareName);
  260. if(!TargetName) {
  261. MyFree(TryName);
  262. return(FALSE);
  263. }
  264. dst = _tcsrchr(TryName,TEXT('.'));
  265. if (dst) {
  266. *dst = 0;
  267. }
  268. src = _tcsrchr(TargetName,TEXT('.'));
  269. if (src) {
  270. *src = 0;
  271. }
  272. if (lstrcmpi(TargetName,TryName)) {
  273. // the "surnames" do not match, so none of the other comparisons will work.
  274. MyFree(TryName);
  275. MyFree(TargetName);
  276. return(FALSE);
  277. }
  278. if (dst) {
  279. *dst = TEXT('.');
  280. }
  281. if (src) {
  282. *src = TEXT('.');
  283. }
  284. *UsedCompressedName = FALSE;
  285. *FileNameLocated = TryName;
  286. if (!lstrcmpi(TryName,TargetName)) {
  287. // we matched
  288. MyFree(TargetName);
  289. return(TRUE);
  290. }
  291. MyFree(TryName);
  292. *UsedCompressedName = TRUE;
  293. TryName = SetupGenerateCompressedName(TargetName);
  294. if(!TryName) {
  295. return(ERROR_NOT_ENOUGH_MEMORY);
  296. }
  297. *FileNameLocated = TryName;
  298. if (!lstrcmpi(TryName,InputName)) {
  299. // we matched
  300. MyFree(TargetName);
  301. return(TRUE);
  302. }
  303. MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_'));
  304. TryName[lstrlen(TryName)-1] = TEXT('$');
  305. if (!lstrcmpi(TryName,InputName)) {
  306. // we matched
  307. MyFree(TargetName);
  308. return(TRUE);
  309. }
  310. //
  311. // no match
  312. //
  313. MyFree(TargetName);
  314. MyFree(TryName);
  315. return(FALSE);
  316. }
  317. DWORD
  318. pSetupDecompressWinLzFile(
  319. IN PTSTR SourceFileName,
  320. IN PTSTR TargetFileName
  321. )
  322. /*++
  323. Routine Description:
  324. Determine whether a file is compressed, and retreive additional
  325. information about it.
  326. Arguments:
  327. SourceFileName - supplies filename of the file to be checked.
  328. This filename is used as a base; if not found then we look
  329. for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
  330. ActualSourceFileName - receives a pointer to the filename
  331. that was actually located. Caller can free with MyFree().
  332. Valid only if the return code from this routine is NO_ERROR.
  333. SourceFileSize - receives the size of the located file in its
  334. current (ie, compressed) form. Valid only if this routine
  335. returns NO_ERROR.
  336. TargetFileSize - receives the uncompressed size of the file.
  337. If the file is not compressed this will be the same as
  338. SourceFileSize. Valid only if this routine returns NO_ERROR.
  339. CompressionType - receives a value indicating the compression type.
  340. Valid only if this routine returns NO_ERROR.
  341. Return Value:
  342. Win32 error code indicating the outcome.
  343. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  344. but we can't find the file
  345. NO_ERROR - file was located and output params are filled in.
  346. Others - something is wrong with the hardware or system.
  347. --*/
  348. {
  349. INT hSrc,hDst;
  350. OFSTRUCT ofSrc,ofDst;
  351. LONG l;
  352. DWORD d;
  353. FILETIME CreateTime,AccessTime,WriteTime;
  354. //
  355. // Get the timestamp of the source.
  356. //
  357. d = GetSetFileTimestamp(
  358. SourceFileName,
  359. &CreateTime,
  360. &AccessTime,
  361. &WriteTime,
  362. FALSE
  363. );
  364. if(d != NO_ERROR) {
  365. return(d);
  366. }
  367. hSrc = LZOpenFile(SourceFileName,&ofSrc,OF_READ|OF_SHARE_DENY_WRITE);
  368. if(hSrc >= 0) {
  369. hDst = LZOpenFile(TargetFileName,&ofSrc,OF_CREATE|OF_WRITE|OF_SHARE_EXCLUSIVE);
  370. if(hDst >= 0) {
  371. l = LZCopy(hSrc,hDst);
  372. if(l >= 0) {
  373. l = 0;
  374. //
  375. // Set the timestamp of the target. The file is already there
  376. // so just ignore errors.
  377. //
  378. GetSetFileTimestamp(
  379. TargetFileName,
  380. &CreateTime,
  381. &AccessTime,
  382. &WriteTime,
  383. TRUE
  384. );
  385. }
  386. LZClose(hDst);
  387. } else {
  388. l = hDst;
  389. }
  390. LZClose(hSrc);
  391. } else {
  392. l = hSrc;
  393. }
  394. //
  395. // lz error to win32 error
  396. //
  397. switch(l) {
  398. case 0:
  399. return(NO_ERROR);
  400. case LZERROR_BADINHANDLE:
  401. case LZERROR_READ:
  402. return(ERROR_READ_FAULT);
  403. case LZERROR_BADOUTHANDLE:
  404. case LZERROR_WRITE:
  405. return(ERROR_WRITE_FAULT);
  406. case LZERROR_GLOBALLOC:
  407. case LZERROR_GLOBLOCK:
  408. return(ERROR_NOT_ENOUGH_MEMORY);
  409. case LZERROR_BADVALUE:
  410. case LZERROR_UNKNOWNALG:
  411. return(ERROR_INVALID_DATA);
  412. default:
  413. return(ERROR_INVALID_FUNCTION);
  414. }
  415. }
  416. DWORD
  417. SetupInternalGetFileCompressionInfo(
  418. IN PCTSTR SourceFileName,
  419. OUT PTSTR *ActualSourceFileName,
  420. OUT PWIN32_FIND_DATA SourceFindData,
  421. OUT PDWORD TargetFileSize,
  422. OUT PUINT CompressionType
  423. )
  424. /*++
  425. Routine Description:
  426. Determine whether a file is compressed, and retreive additional
  427. information about it.
  428. Arguments:
  429. SourceFileName - supplies filename of the file to be checked.
  430. This filename is used as a base; if not found then we look
  431. for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
  432. ActualSourceFileName - receives a pointer to the filename
  433. that was actually located. Caller can free with MyFree().
  434. Valid only if the return code from this routine is NO_ERROR.
  435. SourceFindData - receives win32 find data for the located file in its
  436. current (ie, compressed) form. Valid only if this routine
  437. returns NO_ERROR.
  438. TargetFileSize - receives the uncompressed size of the file.
  439. If the file is not compressed this will be the same as
  440. SourceFileSize. Valid only if this routine returns NO_ERROR.
  441. CompressionType - receives a value indicating the compression type.
  442. Valid only if this routine returns NO_ERROR.
  443. Return Value:
  444. Win32 error code indicating the outcome.
  445. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  446. but we can't find the file
  447. NO_ERROR - file was located and output params are filled in.
  448. Others - something is wrong with the hardware or system.
  449. --*/
  450. {
  451. DWORD d;
  452. DWORD caberr = NO_ERROR;
  453. BOOL b;
  454. HANDLE hFile,hMapping;
  455. DWORD size;
  456. FH UNALIGNED *LZHeader;
  457. d = SetupDetermineSourceFileName(
  458. SourceFileName,
  459. &b,
  460. ActualSourceFileName,
  461. SourceFindData
  462. );
  463. if(d != NO_ERROR) {
  464. return(d);
  465. }
  466. //
  467. // If the file is 0-length it isn't compressed;
  468. // trying to map it in below will fail in this case.
  469. //
  470. if(SourceFindData->nFileSizeLow) {
  471. //
  472. // See if it's a diamond file.
  473. //
  474. d = DiamondProcessCabinet(
  475. *ActualSourceFileName,
  476. 0,
  477. pGetCompressInfoCB,
  478. &size,
  479. TRUE
  480. );
  481. if(d == NO_ERROR) {
  482. *TargetFileSize = size;
  483. *CompressionType = FILE_COMPRESSION_MSZIP;
  484. return(NO_ERROR);
  485. } else if (d != ERROR_INVALID_DATA) {
  486. //
  487. // general problems not specific to the file format itself
  488. // however if this might be a plain file, ignore it
  489. //
  490. size_t len1 = lstrlen(SourceFileName);
  491. size_t len2 = lstrlen(*ActualSourceFileName);
  492. TCHAR c1 = *CharPrev(SourceFileName,SourceFileName+len1);
  493. TCHAR c2 = *CharPrev(*ActualSourceFileName,*ActualSourceFileName+len2);
  494. if(((c2 == TEXT('_')) || (c2 == TEXT('$'))) && ((len1 != len2) || (c1 != c2))) {
  495. //
  496. // ActualSourceFileName ends in '_' or '$' and is a modification of SourceFileName
  497. // don't let us try and parse this as a plain file
  498. //
  499. caberr = d;
  500. }
  501. }
  502. //
  503. // See if it's a WINLZ file.
  504. //
  505. d = pSetupOpenAndMapFileForRead(
  506. *ActualSourceFileName,
  507. &SourceFindData->nFileSizeLow,
  508. &hFile,
  509. &hMapping,
  510. (PVOID *)&LZHeader
  511. );
  512. if(d != NO_ERROR) {
  513. MyFree(*ActualSourceFileName);
  514. return(d);
  515. }
  516. b = FALSE;
  517. try {
  518. if((SourceFindData->nFileSizeLow >= HEADER_LEN)
  519. && !memcmp(LZHeader->rgbyteMagic,COMP_SIG,COMP_SIG_LEN)
  520. && RecognizeCompAlg(LZHeader->byteAlgorithm))
  521. {
  522. *TargetFileSize = LZHeader->cbulUncompSize;
  523. b = TRUE;
  524. }
  525. } except(EXCEPTION_EXECUTE_HANDLER) {
  526. ;
  527. }
  528. pSetupUnmapAndCloseFile(hFile,hMapping,LZHeader);
  529. if(b) {
  530. *CompressionType = FILE_COMPRESSION_WINLZA;
  531. return(NO_ERROR);
  532. }
  533. if(caberr) {
  534. //
  535. // looks like a compressed file and DiamondProcessCabinet
  536. // returned a suspicious error
  537. //
  538. return(caberr);
  539. }
  540. }
  541. //
  542. // File is not compressed.
  543. //
  544. *CompressionType = FILE_COMPRESSION_NONE;
  545. *TargetFileSize = SourceFindData->nFileSizeLow;
  546. return(NO_ERROR);
  547. }
  548. UINT
  549. pGetCompressInfoCB(
  550. IN PVOID Context,
  551. IN UINT Notification,
  552. IN UINT_PTR Param1,
  553. IN UINT_PTR Param2
  554. )
  555. {
  556. PFILE_IN_CABINET_INFO FileInfo;
  557. DWORD rc;
  558. switch(Notification) {
  559. case SPFILENOTIFY_CABINETINFO:
  560. //
  561. // We don't do anything with this.
  562. //
  563. rc = NO_ERROR;
  564. break;
  565. case SPFILENOTIFY_FILEINCABINET:
  566. //
  567. // New file within a cabinet.
  568. //
  569. // We don't ever want to copy the file. Save size info
  570. // and abort.
  571. //
  572. FileInfo = (PFILE_IN_CABINET_INFO)Param1;
  573. *((PDWORD)Context) = FileInfo->FileSize;
  574. FileInfo->Win32Error = NO_ERROR;
  575. rc = FILEOP_ABORT;
  576. SetLastError(NO_ERROR);
  577. break;
  578. //case SPFILENOTIFY_FILEEXTRACTED:
  579. //case SPFILENOTIFY_NEEDNEWCABINET:
  580. default:
  581. //
  582. // We should never get these.
  583. //
  584. MYASSERT(0);
  585. rc = ERROR_INVALID_FUNCTION;
  586. break;
  587. }
  588. return(rc);
  589. }
  590. #ifdef UNICODE
  591. //
  592. // ANSI version
  593. //
  594. DWORD
  595. SetupGetFileCompressionInfoA(
  596. IN PCSTR SourceFileName,
  597. OUT PSTR *ActualSourceFileName,
  598. OUT PDWORD SourceFileSize,
  599. OUT PDWORD TargetFileSize,
  600. OUT PUINT CompressionType
  601. )
  602. {
  603. WIN32_FIND_DATA FindData;
  604. DWORD d;
  605. PCWSTR source;
  606. PWSTR actualsource = NULL;
  607. PSTR actualsourceansi = NULL;
  608. PSTR la_actualsourceansi = NULL;
  609. DWORD targetsize;
  610. UINT type;
  611. d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source);
  612. if(d != NO_ERROR) {
  613. return(d);
  614. }
  615. d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
  616. if(d == NO_ERROR) {
  617. MYASSERT(actualsource);
  618. if((actualsourceansi = pSetupUnicodeToAnsi(actualsource))==NULL) {
  619. d = ERROR_NOT_ENOUGH_MEMORY;
  620. goto clean0;
  621. }
  622. if((la_actualsourceansi = (PSTR)OldMyMalloc(1+strlen(actualsourceansi)))==NULL) {
  623. d = ERROR_NOT_ENOUGH_MEMORY;
  624. goto clean0;
  625. }
  626. strcpy(la_actualsourceansi,actualsourceansi);
  627. try {
  628. *SourceFileSize = FindData.nFileSizeLow;
  629. *ActualSourceFileName = la_actualsourceansi; // free using LocalFree
  630. *TargetFileSize = targetsize;
  631. *CompressionType = type;
  632. la_actualsourceansi = NULL;
  633. } except(EXCEPTION_EXECUTE_HANDLER) {
  634. d = ERROR_INVALID_PARAMETER;
  635. }
  636. }
  637. clean0:
  638. if(actualsource) {
  639. MyFree(actualsource);
  640. }
  641. if(actualsourceansi) {
  642. MyFree(actualsourceansi);
  643. }
  644. if(la_actualsourceansi) {
  645. OldMyFree(la_actualsourceansi);
  646. }
  647. MyFree(source);
  648. return(d);
  649. }
  650. #else
  651. //
  652. // Unicode stub
  653. //
  654. DWORD
  655. SetupGetFileCompressionInfoW(
  656. IN PCWSTR SourceFileName,
  657. OUT PWSTR *ActualSourceFileName,
  658. OUT PDWORD SourceFileSize,
  659. OUT PDWORD TargetFileSize,
  660. OUT PUINT CompressionType
  661. )
  662. {
  663. UNREFERENCED_PARAMETER(SourceFileName);
  664. UNREFERENCED_PARAMETER(ActualSourceFileName);
  665. UNREFERENCED_PARAMETER(SourceFileSize);
  666. UNREFERENCED_PARAMETER(TargetFileSize);
  667. UNREFERENCED_PARAMETER(CompressionType);
  668. return(ERROR_CALL_NOT_IMPLEMENTED);
  669. }
  670. #endif
  671. DWORD
  672. SetupGetFileCompressionInfo(
  673. IN PCTSTR SourceFileName,
  674. OUT PTSTR *ActualSourceFileName,
  675. OUT PDWORD SourceFileSize,
  676. OUT PDWORD TargetFileSize,
  677. OUT PUINT CompressionType
  678. )
  679. /*++
  680. Routine Description:
  681. Here for App-Compat only
  682. Replaced by SetupGetFileCompressionInfoEx
  683. Return pointer is allocated by OldMyMalloc,
  684. it can be freed by (*cough*) OldMyFree (exported as MyFree)
  685. This is because there are apps out there that use this,
  686. and run-time link to setupapi!MyFree to release memory
  687. !!!! DO NOT USE THIS API !!!!
  688. Arguments:
  689. SourceFileName - supplies filename of the file to be checked.
  690. This filename is used as a base; if not found then we look
  691. for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
  692. ActualSourceFileName - receives a pointer to the filename
  693. that was actually located. Caller can free with exported MyFree().
  694. Valid only if the return code from this routine is NO_ERROR.
  695. SourceFileSize - receives the size of the located file in its
  696. current (ie, compressed) form. Valid only if this routine
  697. returns NO_ERROR.
  698. TargetFileSize - receives the uncompressed size of the file.
  699. If the file is not compressed this will be the same as
  700. SourceFileSize. Valid only if this routine returns NO_ERROR.
  701. CompressionType - receives a value indicating the compression type.
  702. Valid only if this routine returns NO_ERROR.
  703. Return Value:
  704. Win32 error code indicating the outcome.
  705. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  706. but we can't find the file
  707. NO_ERROR - file was located and output params are filled in.
  708. Others - something is wrong with the hardware or system.
  709. --*/
  710. {
  711. WIN32_FIND_DATA FindData;
  712. DWORD d;
  713. PCTSTR source;
  714. PTSTR actualsource = NULL;
  715. PTSTR la_actualsource = NULL;
  716. DWORD targetsize;
  717. UINT type;
  718. d = CaptureStringArg(SourceFileName,&source);
  719. if(d != NO_ERROR) {
  720. return(d);
  721. }
  722. d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
  723. if(d == NO_ERROR) {
  724. MYASSERT(actualsource);
  725. la_actualsource = (PTSTR)OldMyMalloc(sizeof(TCHAR)*(1+lstrlen(actualsource)));
  726. if (la_actualsource == NULL) {
  727. MyFree(actualsource);
  728. MyFree(source);
  729. return ERROR_NOT_ENOUGH_MEMORY;
  730. }
  731. lstrcpy(la_actualsource,actualsource);
  732. try {
  733. *SourceFileSize = FindData.nFileSizeLow;
  734. *ActualSourceFileName = la_actualsource; // free using LocalFree
  735. *TargetFileSize = targetsize;
  736. *CompressionType = type;
  737. } except(EXCEPTION_EXECUTE_HANDLER) {
  738. d = ERROR_INVALID_PARAMETER;
  739. }
  740. if(d != NO_ERROR) {
  741. OldMyFree(la_actualsource);
  742. }
  743. MyFree(actualsource);
  744. }
  745. MyFree(source);
  746. return(d);
  747. }
  748. #ifdef UNICODE
  749. //
  750. // ANSI version
  751. //
  752. BOOL
  753. WINAPI
  754. SetupGetFileCompressionInfoExA(
  755. IN PCSTR SourceFileName,
  756. IN PSTR ActualSourceFileNameBuffer,
  757. IN DWORD ActualSourceFileNameBufferLen,
  758. OUT PDWORD RequiredBufferLen, OPTIONAL
  759. OUT PDWORD SourceFileSize,
  760. OUT PDWORD TargetFileSize,
  761. OUT PUINT CompressionType
  762. )
  763. {
  764. WIN32_FIND_DATA FindData;
  765. DWORD d;
  766. PCWSTR source;
  767. PWSTR actualsource = NULL;
  768. PSTR actualsourceansi = NULL;
  769. DWORD targetsize;
  770. DWORD reqbufsize;
  771. UINT type;
  772. d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source);
  773. if(d != NO_ERROR) {
  774. SetLastError(d);
  775. return (d==NO_ERROR);
  776. }
  777. d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
  778. if(d == NO_ERROR) {
  779. MYASSERT(actualsource);
  780. actualsourceansi = pSetupUnicodeToAnsi(actualsource);
  781. if(actualsourceansi != NULL) {
  782. try {
  783. reqbufsize = strlen(actualsourceansi)+1;
  784. if (RequiredBufferLen) {
  785. *RequiredBufferLen = reqbufsize;
  786. }
  787. if(ActualSourceFileNameBuffer) {
  788. if((ActualSourceFileNameBufferLen < reqbufsize)) {
  789. d = ERROR_INSUFFICIENT_BUFFER;
  790. } else {
  791. strcpy(ActualSourceFileNameBuffer,actualsourceansi);
  792. }
  793. } else if(ActualSourceFileNameBufferLen) {
  794. d = ERROR_INVALID_USER_BUFFER;
  795. }
  796. *SourceFileSize = FindData.nFileSizeLow;
  797. *TargetFileSize = targetsize;
  798. *CompressionType = type;
  799. } except(EXCEPTION_EXECUTE_HANDLER) {
  800. d = ERROR_INVALID_PARAMETER;
  801. }
  802. } else {
  803. d = ERROR_NOT_ENOUGH_MEMORY;
  804. }
  805. }
  806. if(actualsource) {
  807. MyFree(actualsource);
  808. }
  809. if(actualsourceansi) {
  810. MyFree(actualsourceansi);
  811. }
  812. MyFree(source);
  813. SetLastError(d);
  814. return (d==NO_ERROR);
  815. }
  816. #else
  817. //
  818. // Unicode stub
  819. //
  820. BOOL
  821. WINAPI
  822. SetupGetFileCompressionInfoExW(
  823. IN PCWSTR SourceFileName,
  824. IN PWSTR ActualSourceFileNameBuffer,
  825. IN DWORD ActualSourceFileNameBufferLen,
  826. OUT PDWORD RequiredBufferLen, OPTIONAL
  827. OUT PDWORD SourceFileSize,
  828. OUT PDWORD TargetFileSize,
  829. OUT PUINT CompressionType
  830. )
  831. {
  832. UNREFERENCED_PARAMETER(SourceFileName);
  833. UNREFERENCED_PARAMETER(ActualSourceFileNameBuffer);
  834. UNREFERENCED_PARAMETER(ActualSourceFileNameBufferLen);
  835. UNREFERENCED_PARAMETER(RequiredBufferLen);
  836. UNREFERENCED_PARAMETER(SourceFileSize);
  837. UNREFERENCED_PARAMETER(TargetFileSize);
  838. UNREFERENCED_PARAMETER(CompressionType);
  839. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  840. return FALSE;
  841. }
  842. #endif
  843. BOOL
  844. WINAPI
  845. SetupGetFileCompressionInfoEx(
  846. IN PCTSTR SourceFileName,
  847. IN PTSTR ActualSourceFileNameBuffer,
  848. IN DWORD ActualSourceFileNameBufferLen,
  849. OUT PDWORD RequiredBufferLen, OPTIONAL
  850. OUT PDWORD SourceFileSize,
  851. OUT PDWORD TargetFileSize,
  852. OUT PUINT CompressionType
  853. )
  854. /*++
  855. Routine Description:
  856. Determine whether a file is compressed, and retreive additional
  857. information about it.
  858. This is the replacement for the very broken SetupGetFileCompressionInfo
  859. Caller must pass in a buffer
  860. If the buffer is NULL, return size
  861. and all other parameters filled out (unless some other error occurred)
  862. note however, you would typically call this with a buffer size of MAX_PATH.
  863. Arguments:
  864. SourceFileName - supplies filename of the file to be checked.
  865. This filename is used as a base; if not found then we look
  866. for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
  867. ActualSourceFileNameBuffer - if not NULL, receives actual filename
  868. Valid only if the return code from this routine is NO_ERROR.
  869. ActualSourceFileNameBufferLen - pass in length (characters) of
  870. ActualSourceFileNameBuffer. must be 0 if ActualSourceFileNameBuffer
  871. is NULL.
  872. RequiredBufferLen - if not NULL, filled with length of actual filename
  873. including terminating NULL.
  874. Valid only if the return code from this routine is NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
  875. SourceFileSize - receives the size of the located file in its
  876. current (ie, compressed) form. Valid only if this routine
  877. returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
  878. TargetFileSize - receives the uncompressed size of the file.
  879. If the file is not compressed this will be the same as
  880. SourceFileSize. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
  881. CompressionType - receives a value indicating the compression type.
  882. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
  883. Return Value:
  884. TRUE indicating success (NO_ERROR)
  885. FALSE indicating failure
  886. GetLastError() provides Win32 error code indicating the outcome.
  887. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
  888. but we can't find the file
  889. NO_ERROR - file was located and all output params are filled in including ActualSourceFileNameBuffer.
  890. also returned if ActualSourceFileNameBuffer is NULL
  891. ERROR_INSUFFICIENT_BUFFER - file was located and output params are filled in, excluding
  892. ActualSourceFileNameBuffer.
  893. Others - something is wrong with the hardware or system.
  894. --*/
  895. {
  896. WIN32_FIND_DATA FindData;
  897. DWORD d;
  898. PCTSTR source;
  899. PTSTR actualsource = NULL;
  900. DWORD targetsize;
  901. UINT type;
  902. DWORD reqbufsize;
  903. d = CaptureStringArg(SourceFileName,&source);
  904. if(d != NO_ERROR) {
  905. SetLastError(d);
  906. return (d==NO_ERROR);
  907. }
  908. d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
  909. if(d == NO_ERROR) {
  910. MYASSERT(actualsource);
  911. try {
  912. reqbufsize = lstrlen(actualsource)+1;
  913. if (RequiredBufferLen) {
  914. *RequiredBufferLen = reqbufsize;
  915. }
  916. if(ActualSourceFileNameBuffer) {
  917. if(ActualSourceFileNameBufferLen < reqbufsize) {
  918. d = ERROR_INSUFFICIENT_BUFFER;
  919. } else {
  920. lstrcpy(ActualSourceFileNameBuffer,actualsource);
  921. }
  922. } else if(ActualSourceFileNameBufferLen) {
  923. d = ERROR_INVALID_USER_BUFFER;
  924. }
  925. *SourceFileSize = FindData.nFileSizeLow;
  926. *TargetFileSize = targetsize;
  927. *CompressionType = type;
  928. } except(EXCEPTION_EXECUTE_HANDLER) {
  929. d = ERROR_INVALID_PARAMETER;
  930. }
  931. MyFree(actualsource);
  932. }
  933. MyFree(source);
  934. SetLastError(d);
  935. return (d==NO_ERROR);
  936. }
  937. #ifdef UNICODE
  938. //
  939. // ANSI version
  940. //
  941. DWORD
  942. SetupDecompressOrCopyFileA(
  943. IN PCSTR SourceFileName,
  944. OUT PCSTR TargetFileName,
  945. OUT PUINT CompressionType OPTIONAL
  946. )
  947. {
  948. DWORD rc;
  949. PCWSTR s,t;
  950. rc = pSetupCaptureAndConvertAnsiArg(SourceFileName,&s);
  951. if(rc == NO_ERROR) {
  952. rc = pSetupCaptureAndConvertAnsiArg(TargetFileName,&t);
  953. if(rc == NO_ERROR) {
  954. rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL);
  955. MyFree(t);
  956. }
  957. MyFree(s);
  958. }
  959. return(rc);
  960. }
  961. #else
  962. //
  963. // Unicode stub
  964. //
  965. DWORD
  966. SetupDecompressOrCopyFileW(
  967. IN PCWSTR SourceFileName,
  968. OUT PCWSTR TargetFileName,
  969. OUT PUINT CompressionType OPTIONAL
  970. )
  971. {
  972. UNREFERENCED_PARAMETER(SourceFileName);
  973. UNREFERENCED_PARAMETER(TargetFileName);
  974. UNREFERENCED_PARAMETER(CompressionType);
  975. return(ERROR_CALL_NOT_IMPLEMENTED);
  976. }
  977. #endif
  978. DWORD
  979. SetupDecompressOrCopyFile(
  980. IN PCTSTR SourceFileName,
  981. IN PCTSTR TargetFileName,
  982. IN PUINT CompressionType OPTIONAL
  983. )
  984. /*++
  985. Routine Description:
  986. Decompress or copy a file.
  987. Arguments:
  988. SourceFileName - supplies filename of the file to be decompressed.
  989. If CompressionType is specified, no additional processing is
  990. performed on this name -- the caller is responsible for determining
  991. the actual file name (ie, foo.ex_ instead of foo.exe) before calling
  992. this routine. If CompressionType is not specified, then this routine
  993. attempts to locate the compressed form of the filename if the file
  994. is not found with the name given.
  995. TargetFileName - supplies filename of target file.
  996. CompressionType - if specified, supplies type of compression in use
  997. on the source. This can be determined by calling
  998. SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE
  999. results in the file being copied and not decompressed,
  1000. regardless of the type of compression that may be in use on the source.
  1001. If this value is not specified then this routine attempts to determine
  1002. the compression type and decompresses/copies accordingly.
  1003. Return Value:
  1004. Win32 error code indicating the outcome.
  1005. --*/
  1006. {
  1007. DWORD rc;
  1008. PCTSTR s,t;
  1009. rc = CaptureStringArg(SourceFileName,&s);
  1010. if(rc == NO_ERROR) {
  1011. rc = CaptureStringArg(TargetFileName,&t);
  1012. if(rc == NO_ERROR) {
  1013. rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL);
  1014. MyFree(t);
  1015. }
  1016. MyFree(s);
  1017. }
  1018. return(rc);
  1019. }
  1020. DWORD
  1021. pSetupDecompressOrCopyFile(
  1022. IN PCTSTR SourceFileName,
  1023. IN PCTSTR TargetFileName,
  1024. IN PUINT CompressionType, OPTIONAL
  1025. IN BOOL AllowMove,
  1026. OUT PBOOL Moved OPTIONAL
  1027. )
  1028. /*++
  1029. Routine Description:
  1030. Decompress or copy a file.
  1031. Arguments:
  1032. SourceFileName - supplies filename of the file to be decompressed.
  1033. If CompressionType is specified, no additional processing is
  1034. performed on this name -- the caller is responsible for determining
  1035. the actual file name (ie, foo.ex_ instead of foo.exe) before calling
  1036. this routine. If CompressionType is not specified, then this routine
  1037. attempts to locate the compressed form of the filename if the file
  1038. is not found with the name given.
  1039. TargetFileName - supplies filename of target file.
  1040. CompressionType - if specified, supplies type of compression in use
  1041. on the source. This can be determined by calling
  1042. SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE
  1043. results in the file being copied and not decompressed,
  1044. regardless of the type of compression that may be in use on the source.
  1045. If this value is not specified then this routine attempts to determine
  1046. the compression type and decompresses/copies accordingly.
  1047. AllowMove - if specified, then files that do not require decompression
  1048. will be moved instead of copied.
  1049. Moved - if specified receives a boolean indicating whether the file was
  1050. moved (as opposed to copied or decompressed).
  1051. Return Value:
  1052. Win32 error code indicating the outcome.
  1053. --*/
  1054. {
  1055. DWORD d;
  1056. UINT ComprType;
  1057. PTSTR ActualName;
  1058. DWORD TargetSize;
  1059. FILETIME CreateTime,AccessTime,WriteTime;
  1060. SFD_INFO CBData;
  1061. BOOL moved;
  1062. WIN32_FIND_DATA FindData;
  1063. if(Moved) {
  1064. *Moved = FALSE;
  1065. }
  1066. if(CompressionType) {
  1067. ComprType = *CompressionType;
  1068. ActualName = (PTSTR)SourceFileName;
  1069. } else {
  1070. //
  1071. // Need to determine compresison type.
  1072. //
  1073. d = SetupInternalGetFileCompressionInfo(
  1074. SourceFileName,
  1075. &ActualName,
  1076. &FindData,
  1077. &TargetSize,
  1078. &ComprType
  1079. );
  1080. if(d != NO_ERROR) {
  1081. return(d);
  1082. }
  1083. }
  1084. //
  1085. // Blast the target file. Ignore if failure -- it'll be caught later.
  1086. //
  1087. SetFileAttributes(TargetFileName,FILE_ATTRIBUTE_NORMAL);
  1088. DeleteFile(TargetFileName);
  1089. switch(ComprType) {
  1090. case FILE_COMPRESSION_NONE:
  1091. moved = (AllowMove ? MoveFile(ActualName,TargetFileName) : FALSE);
  1092. if(moved) {
  1093. d = NO_ERROR;
  1094. if(Moved) {
  1095. *Moved = TRUE;
  1096. }
  1097. } else {
  1098. d = GetSetFileTimestamp(ActualName,&CreateTime,&AccessTime,&WriteTime,FALSE);
  1099. if(d == NO_ERROR) {
  1100. d = CopyFile(ActualName,TargetFileName,FALSE) ? NO_ERROR : GetLastError();
  1101. if(d == NO_ERROR) {
  1102. GetSetFileTimestamp(TargetFileName,&CreateTime,&AccessTime,&WriteTime,TRUE);
  1103. }
  1104. }
  1105. }
  1106. break;
  1107. case FILE_COMPRESSION_WINLZA:
  1108. d = pSetupDecompressWinLzFile(ActualName,(PTSTR)TargetFileName);
  1109. break;
  1110. case FILE_COMPRESSION_MSZIP:
  1111. CBData.FileCount = 0;
  1112. CBData.TargetFile = TargetFileName;
  1113. CBData.GotTimestamp = FALSE;
  1114. d = DiamondProcessCabinet(
  1115. ActualName,
  1116. 0,
  1117. pSingleFileDecompCB,
  1118. &CBData,
  1119. TRUE
  1120. );
  1121. break;
  1122. default:
  1123. d = ERROR_INVALID_PARAMETER;
  1124. break;
  1125. }
  1126. if(!CompressionType) {
  1127. MyFree(ActualName);
  1128. }
  1129. return(d);
  1130. }
  1131. UINT
  1132. pSingleFileDecompCB(
  1133. IN PVOID Context,
  1134. IN UINT Notification,
  1135. IN UINT_PTR Param1,
  1136. IN UINT_PTR Param2
  1137. )
  1138. {
  1139. PSFD_INFO Data;
  1140. PFILE_IN_CABINET_INFO FileInfo;
  1141. PFILEPATHS FilePaths;
  1142. DWORD rc;
  1143. HANDLE h;
  1144. Data = Context;
  1145. switch(Notification) {
  1146. case SPFILENOTIFY_CABINETINFO:
  1147. //
  1148. // We don't do anything with this.
  1149. //
  1150. rc = NO_ERROR;
  1151. break;
  1152. case SPFILENOTIFY_FILEINCABINET:
  1153. //
  1154. // New file within a cabinet.
  1155. //
  1156. FileInfo = (PFILE_IN_CABINET_INFO)Param1;
  1157. FileInfo->Win32Error = NO_ERROR;
  1158. //
  1159. // We only want the first file. If this is a subsequent file,
  1160. // bail out.
  1161. //
  1162. if(Data->FileCount++) {
  1163. rc = FILEOP_ABORT;
  1164. SetLastError(NO_ERROR);
  1165. } else {
  1166. //
  1167. // We want the file. Ignore the names in the cabinet and
  1168. // use the name given to us. Also, we want to preserve
  1169. // the timestamp of the cabinet, not of the file within it.
  1170. //
  1171. lstrcpyn(FileInfo->FullTargetName,Data->TargetFile,MAX_PATH);
  1172. h = CreateFile(
  1173. (PCTSTR)Param2, // cabinet filename
  1174. GENERIC_READ,
  1175. FILE_SHARE_READ,
  1176. NULL,
  1177. OPEN_EXISTING,
  1178. 0,
  1179. NULL
  1180. );
  1181. if(h != INVALID_HANDLE_VALUE) {
  1182. if(GetFileTime(h,NULL,NULL,&Data->FileTime)) {
  1183. Data->GotTimestamp = TRUE;
  1184. }
  1185. CloseHandle(h);
  1186. }
  1187. rc = FILEOP_DOIT;
  1188. }
  1189. break;
  1190. case SPFILENOTIFY_FILEEXTRACTED:
  1191. //
  1192. // File was successfully extracted.
  1193. // Preserve timestamp.
  1194. //
  1195. FilePaths = (PFILEPATHS)Param1;
  1196. if(Data->GotTimestamp) {
  1197. h = CreateFile(
  1198. FilePaths->Target,
  1199. GENERIC_WRITE,
  1200. FILE_SHARE_READ,
  1201. NULL,
  1202. OPEN_EXISTING,
  1203. 0,
  1204. NULL
  1205. );
  1206. if(h != INVALID_HANDLE_VALUE) {
  1207. SetFileTime(h,NULL,NULL,&Data->FileTime);
  1208. CloseHandle(h);
  1209. }
  1210. }
  1211. rc = NO_ERROR;
  1212. break;
  1213. //case SPFILENOTIFY_NEEDNEWCABINET:
  1214. default:
  1215. //
  1216. // We should never get this.
  1217. //
  1218. MYASSERT(0);
  1219. rc = ERROR_INVALID_FUNCTION;
  1220. break;
  1221. }
  1222. return(rc);
  1223. }