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.

1252 lines
36 KiB

  1. #include "pch.h"
  2. #ifndef FileExists
  3. #undef FileExists
  4. #endif
  5. #include <setupntp.h>
  6. #include "winbom.h"
  7. //
  8. // Context for file queues in the offline installer.
  9. //
  10. typedef struct _OFFLINE_QUEUE_CONTEXT {
  11. PVOID DefaultContext;
  12. PWSTR InfPath;
  13. PWSTR OfflineWindowsDirectory;
  14. PWSTR OfflineSourcePath;
  15. PWSTR TemporaryFilePath;
  16. } OFFLINE_QUEUE_CONTEXT, *POFFLINE_QUEUE_CONTEXT;
  17. //
  18. // Context for Cosma's SetupIterateCabinet calls
  19. //
  20. typedef struct _COSMA_CONTEXT
  21. {
  22. TCHAR szSourceFile[MAX_PATH];
  23. TCHAR szDestination[MAX_PATH];
  24. } COSMA_CONTEXT, *PCOSMA_CONTEXT;
  25. //
  26. // Local function declarations
  27. //
  28. static BOOL
  29. ValidateAndChecksumFile(
  30. IN PCWSTR Filename,
  31. OUT PBOOLEAN IsNtImage,
  32. OUT PULONG Checksum,
  33. OUT PBOOLEAN Valid
  34. );
  35. static VOID
  36. MungeNode(
  37. IN PSP_FILE_QUEUE Queue,
  38. IN PSP_FILE_QUEUE_NODE QueueNode,
  39. IN LPTSTR lpWindowsDirectory,
  40. IN LPTSTR lpOfflineWindowsDirectory);
  41. static VOID
  42. MungeQueuePaths(
  43. IN HSPFILEQ hFileQueue,
  44. IN LPTSTR lpWindowsDirectory,
  45. IN LPTSTR lpOfflineWindowsDirectory);
  46. static UINT
  47. CosmaMsgHandler(
  48. IN PVOID Context,
  49. IN UINT Notification,
  50. IN UINT_PTR Param1,
  51. IN UINT_PTR Param2
  52. );
  53. static UINT
  54. FixCopyQueueStuff(
  55. IN POFFLINE_QUEUE_CONTEXT OfflineContext,
  56. IN LPTSTR lpszSourceFile,
  57. IN OUT LPTSTR lpszDestination
  58. );
  59. static UINT
  60. OfflineQueueCallback(
  61. IN PVOID Context,
  62. IN UINT Notification,
  63. IN UINT_PTR Param1,
  64. IN UINT_PTR Param2
  65. );
  66. static VOID
  67. FreeOfflineContext(
  68. IN PVOID Context
  69. );
  70. static PVOID
  71. InitOfflineQueueCallback(
  72. VOID
  73. );
  74. //
  75. // Exported functions:
  76. //
  77. BOOL OfflineCommitFileQueue(HSPFILEQ hFileQueue, LPTSTR lpInfPath, LPTSTR lpSourcePath, LPTSTR lpOfflineWindowsDirectory )
  78. {
  79. POFFLINE_QUEUE_CONTEXT pOfflineContext;
  80. DWORD dwSize;
  81. TCHAR szWindowsDirectory[MAX_PATH] = NULLSTR;
  82. BOOL bRet = FALSE;
  83. if (INVALID_HANDLE_VALUE != hFileQueue && GetWindowsDirectory(szWindowsDirectory, AS(szWindowsDirectory)))
  84. {
  85. DWORD dwResult = 0;
  86. // If we're not doing an offline install this will be NULL so we won't do any funky
  87. // stuff.
  88. //
  89. if ( lpOfflineWindowsDirectory )
  90. {
  91. pSetupSetGlobalFlags(pSetupGetGlobalFlags() | PSPGF_NO_VERIFY_INF | PSPGF_NO_BACKUP);
  92. // Redirect the target directories to the offline image
  93. //
  94. MungeQueuePaths(hFileQueue, szWindowsDirectory, lpOfflineWindowsDirectory);
  95. }
  96. // Init our special Callback and context.
  97. //
  98. if ( pOfflineContext = (POFFLINE_QUEUE_CONTEXT) InitOfflineQueueCallback() )
  99. {
  100. TCHAR szInfPath[MAX_PATH] = NULLSTR;
  101. if ( lpInfPath )
  102. {
  103. lstrcpy(szInfPath, lpInfPath);
  104. }
  105. //
  106. // Set the OfflineWindowsDirectory member of the Context structure
  107. //
  108. pOfflineContext->OfflineWindowsDirectory = lpOfflineWindowsDirectory;
  109. pOfflineContext->InfPath = szInfPath;
  110. pOfflineContext->OfflineSourcePath = lpSourcePath;
  111. //
  112. // Commit the file queue
  113. //
  114. if ( SetupCommitFileQueue(NULL, hFileQueue, OfflineQueueCallback, pOfflineContext))
  115. {
  116. bRet = TRUE;
  117. }
  118. FreeOfflineContext(pOfflineContext);
  119. }
  120. }
  121. return bRet;
  122. }
  123. //
  124. // Internal functions:
  125. //
  126. static BOOL
  127. ValidateAndChecksumFile(
  128. IN PCWSTR Filename,
  129. OUT PBOOLEAN IsNtImage,
  130. OUT PULONG Checksum,
  131. OUT PBOOLEAN Valid
  132. )
  133. /*++
  134. ===============================================================================
  135. Routine Description:
  136. Calculate a checksum value for a file using the standard
  137. nt image checksum method. If the file is an nt image, validate
  138. the image using the partial checksum in the image header. If the
  139. file is not an nt image, it is simply defined as valid.
  140. If we encounter an i/o error while checksumming, then the file
  141. is declared invalid.
  142. Arguments:
  143. Filename - supplies full NT path of file to check.
  144. IsNtImage - Receives flag indicating whether the file is an
  145. NT image file.
  146. Checksum - receives 32-bit checksum value.
  147. Valid - receives flag indicating whether the file is a valid
  148. image (for nt images) and that we can read the image.
  149. Return Value:
  150. BOOL - Returns TRUE if the file was validated, and in this case,
  151. IsNtImage, Checksum, and Valid will contain the result of
  152. the validation.
  153. This function will return FALSE, if the file could not be
  154. validated, and in this case, the caller should call GetLastError()
  155. to find out why this function failed.
  156. ===============================================================================
  157. --*/
  158. {
  159. DWORD Error;
  160. PVOID BaseAddress;
  161. ULONG FileSize;
  162. HANDLE hFile;
  163. HANDLE hSection;
  164. PIMAGE_NT_HEADERS NtHeaders;
  165. ULONG HeaderSum;
  166. //
  167. // Assume not an image and failure.
  168. //
  169. *IsNtImage = FALSE;
  170. *Checksum = 0;
  171. *Valid = FALSE;
  172. //
  173. // Open and map the file for read access.
  174. //
  175. Error = pSetupOpenAndMapFileForRead( Filename,
  176. &FileSize,
  177. &hFile,
  178. &hSection,
  179. &BaseAddress );
  180. if( Error != ERROR_SUCCESS ) {
  181. SetLastError( Error );
  182. return(FALSE);
  183. }
  184. if( FileSize == 0 ) {
  185. *IsNtImage = FALSE;
  186. *Checksum = 0;
  187. *Valid = TRUE;
  188. CloseHandle( hFile );
  189. return(TRUE);
  190. }
  191. try {
  192. NtHeaders = CheckSumMappedFile(BaseAddress,FileSize,&HeaderSum,Checksum);
  193. } except (EXCEPTION_EXECUTE_HANDLER) {
  194. *Checksum = 0;
  195. NtHeaders = NULL;
  196. }
  197. //
  198. // If the file is not an image and we got this far (as opposed to encountering
  199. // an i/o error) then the checksum is declared valid. If the file is an image,
  200. // then its checksum may or may not be valid.
  201. //
  202. if(NtHeaders) {
  203. *IsNtImage = TRUE;
  204. *Valid = HeaderSum ? (*Checksum == HeaderSum) : TRUE;
  205. } else {
  206. *Valid = TRUE;
  207. }
  208. pSetupUnmapAndCloseFile( hFile, hSection, BaseAddress );
  209. return( TRUE );
  210. }
  211. /*
  212. VOID
  213. LogRepairInfo(
  214. IN PWSTR Source,
  215. IN PWSTR Target,
  216. IN PWSTR DirectoryOnSourceDevice,
  217. IN PWSTR DiskDescription,
  218. IN PWSTR DiskTag
  219. )
  220. ++
  221. ===============================================================================
  222. Routine Description:
  223. This function will log the fact that a file was installed into the
  224. machine. This will enable Windows repair functionality to be alerted
  225. that in case of a repair, this file will need to be restored.
  226. Arguments:
  227. Return Value:
  228. ===============================================================================
  229. --
  230. {
  231. WCHAR RepairLog[MAX_PATH];
  232. BOOLEAN IsNtImage;
  233. ULONG Checksum;
  234. BOOLEAN Valid;
  235. WCHAR Filename[MAX_PATH];
  236. WCHAR SourceName[MAX_PATH];
  237. DWORD LastSourceChar, LastTargetChar;
  238. DWORD LastSourcePeriod, LastTargetPeriod;
  239. WCHAR Line[MAX_PATH];
  240. WCHAR tmp[MAX_PATH];
  241. if (!GetWindowsDirectory( RepairLog, MAX_PATH ))
  242. return;
  243. wcscat( RepairLog, L"\\repair\\setup.log" );
  244. if( ValidateAndChecksumFile( Target, &IsNtImage, &Checksum, &Valid )) {
  245. //
  246. // Strip off drive letter.
  247. //
  248. swprintf(
  249. Filename,
  250. L"\"%s\"",
  251. Target+2
  252. );
  253. //
  254. // Convert source name to uncompressed form.
  255. //
  256. wcscpy( SourceName, wcsrchr( Source, (WCHAR)'\\' ) + 1 );
  257. if(!SourceName) {
  258. return;
  259. }
  260. LastSourceChar = wcslen (SourceName) - 1;
  261. if(SourceName[LastSourceChar] == L'_') {
  262. LastSourcePeriod = (DWORD)(wcsrchr( SourceName, (WCHAR)'.' ) - SourceName);
  263. if(LastSourceChar - LastSourcePeriod == 1) {
  264. //
  265. // No extension - just truncate the "._"
  266. //
  267. SourceName[LastSourceChar-1] = NULLCHR;
  268. } else {
  269. //
  270. // Make sure the extensions on source and target match.
  271. // If this fails, we can't log the file copy
  272. //
  273. LastTargetChar = wcslen (Target) - 1;
  274. LastTargetPeriod = (ULONG)(wcsrchr( Target, (WCHAR)'.' ) - Target);
  275. if( _wcsnicmp(
  276. SourceName + LastSourcePeriod,
  277. Target + LastTargetPeriod,
  278. LastSourceChar - LastSourcePeriod - 1 )) {
  279. return;
  280. }
  281. if(LastTargetChar - LastTargetPeriod < 3) {
  282. //
  283. // Short extension - just truncate the "_"
  284. //
  285. SourceName[LastSourceChar] = NULLCHR;
  286. } else {
  287. //
  288. // Need to replace "_" with last character from target
  289. //
  290. SourceName[LastSourceChar] = Target[LastTargetChar];
  291. }
  292. }
  293. }
  294. //
  295. // Write the line.
  296. //
  297. if( (DirectoryOnSourceDevice) &&
  298. (DiskDescription) &&
  299. (DiskTag) ) {
  300. //
  301. // Treat this as an OEM file.
  302. //
  303. swprintf( Line,
  304. L"\"%s\",\"%x\",\"%s\",\"%s\",\"%s\"",
  305. SourceName,
  306. Checksum,
  307. DirectoryOnSourceDevice,
  308. DiskDescription,
  309. DiskTag );
  310. } else {
  311. //
  312. // Treat this as an "in the box" file.
  313. //
  314. swprintf( Line,
  315. L"\"%s\",\"%x\"",
  316. SourceName,
  317. Checksum );
  318. }
  319. if (GetPrivateProfileString(L"Files.WinNt",Filename,L"",tmp,sizeof(tmp)/sizeof(tmp[0]),RepairLog)) {
  320. //
  321. // there is already an entry for this file present (presumably
  322. // from textmode phase of setup.) Favor this entry over what we
  323. // are about to add
  324. //
  325. } else {
  326. WritePrivateProfileString(
  327. L"Files.WinNt",
  328. Filename,
  329. Line,
  330. RepairLog);
  331. }
  332. }
  333. }
  334. */
  335. static VOID
  336. MungeNode(
  337. IN PSP_FILE_QUEUE Queue,
  338. IN PSP_FILE_QUEUE_NODE QueueNode,
  339. IN LPTSTR lpWindowsDirectory,
  340. IN LPTSTR lpOfflineWindowsDirectory)
  341. {
  342. LONG lNewId = 0;
  343. TCHAR szTempTarget[MAX_PATH];
  344. PTSTR pOldTarget = pSetupStringTableStringFromId(Queue->StringTable, QueueNode->TargetDirectory);
  345. #ifdef DBG
  346. // These are here for debugging purposes. We can look at
  347. //
  348. PTSTR pSourcePath = pSetupStringTableStringFromId(Queue->StringTable, QueueNode->SourcePath);
  349. PTSTR pSourceFilename = pSetupStringTableStringFromId(Queue->StringTable, QueueNode->SourceFilename);
  350. PTSTR pTargetFilename = pSetupStringTableStringFromId(Queue->StringTable, QueueNode->TargetFilename);
  351. #endif
  352. if ( pOldTarget )
  353. {
  354. // See if the WindowsDirectory is part of the target. If so replace it
  355. // with the OfflineWindowsDirectory.
  356. //
  357. if ( StrStrI(pOldTarget, lpWindowsDirectory) )
  358. {
  359. // We found the windows directory in the name. Replace it with our own.
  360. //
  361. lstrcpyn(szTempTarget, lpOfflineWindowsDirectory, MAX_PATH);
  362. StrCatBuff(szTempTarget, pOldTarget + lstrlen(lpWindowsDirectory), MAX_PATH);
  363. }
  364. // If the target is not a subdir of the windows directory just redirect the
  365. // drive to the offline drive.
  366. // Look at the first letter to see if it's the same letter as the
  367. // current WindowsDirectory drive letter.
  368. //
  369. else if( *pOldTarget == *lpWindowsDirectory )
  370. {
  371. // Strip off the windows directory from the offline directory name,
  372. // use the buffer and then put the windows directory back on.
  373. // I am assuming here that the windows directory is at the root of
  374. // a drive (this is pretty reasonable).
  375. //
  376. LPTSTR lpWhack = _tcsrchr(lpOfflineWindowsDirectory, _T('\\'));
  377. if ( lpWhack )
  378. {
  379. *lpWhack = NULLCHR;
  380. lstrcpyn(szTempTarget, lpOfflineWindowsDirectory, MAX_PATH);
  381. *lpWhack = _T('\\');
  382. // Now copy every thing past the drive letter and : to the buffer
  383. // and we will have a good path.
  384. //
  385. StrCatBuff(szTempTarget, pOldTarget + 2, MAX_PATH); // Skip the drive letter and the :.
  386. }
  387. }
  388. // Add the new target string to the StringTable and
  389. // set the TargetDirectory to the new string ID.
  390. //
  391. lNewId = pSetupStringTableAddString(Queue->StringTable, szTempTarget, 0);
  392. QueueNode->TargetDirectory = lNewId;
  393. }
  394. }
  395. static VOID
  396. MungeQueuePaths(
  397. IN HSPFILEQ hFileQueue,
  398. IN LPTSTR lpWindowsDirectory,
  399. IN LPTSTR lpOfflineWindowsDirectory)
  400. {
  401. PSP_FILE_QUEUE Queue;
  402. PSOURCE_MEDIA_INFO SourceMedia;
  403. PSP_FILE_QUEUE_NODE QueueNode;
  404. // The queue handle is nothing more than a pointer to the queue.
  405. //
  406. Queue = (PSP_FILE_QUEUE)hFileQueue;
  407. // Lie to setupapi: tell it that the queue catalogs have already been verified succesfuly.
  408. //
  409. Queue->Flags &= ~FQF_DID_CATALOGS_FAILED;
  410. Queue->Flags |= FQF_DID_CATALOGS_OK;
  411. Queue->DriverSigningPolicy = DRIVERSIGN_NONE;
  412. // Go through all SourceMediaLists and through each CopyQueue withing those.
  413. //
  414. for ( SourceMedia=Queue->SourceMediaList; SourceMedia; SourceMedia=SourceMedia->Next )
  415. {
  416. QueueNode = SourceMedia->CopyQueue;
  417. while ( QueueNode )
  418. {
  419. MungeNode(Queue, QueueNode, lpWindowsDirectory, lpOfflineWindowsDirectory);
  420. // Advance to the next node.
  421. //
  422. QueueNode = QueueNode->Next;
  423. }
  424. }
  425. // Go through the backup queue.
  426. //
  427. for ( QueueNode=Queue->BackupQueue; QueueNode; QueueNode=QueueNode->Next )
  428. {
  429. MungeNode(Queue, QueueNode, lpWindowsDirectory, lpOfflineWindowsDirectory);
  430. }
  431. // Go through the delete queue.
  432. //
  433. for ( QueueNode=Queue->DeleteQueue; QueueNode; QueueNode=QueueNode->Next )
  434. {
  435. MungeNode(Queue, QueueNode, lpWindowsDirectory, lpOfflineWindowsDirectory);
  436. }
  437. // Go through the rename queue.
  438. //
  439. for ( QueueNode=Queue->RenameQueue; QueueNode; QueueNode=QueueNode->Next )
  440. {
  441. MungeNode(Queue, QueueNode, lpWindowsDirectory, lpOfflineWindowsDirectory);
  442. }
  443. }
  444. static UINT
  445. CosmaMsgHandler(
  446. IN PVOID Context,
  447. IN UINT Notification,
  448. IN UINT_PTR Param1,
  449. IN UINT_PTR Param2
  450. )
  451. {
  452. UINT uRet = NO_ERROR;
  453. PCOSMA_CONTEXT CosmaContext = (PCOSMA_CONTEXT) Context;
  454. switch (Notification)
  455. {
  456. case SPFILENOTIFY_FILEEXTRACTED:
  457. {
  458. PFILEPATHS FilePaths = (PFILEPATHS) Param1;
  459. if (FilePaths)
  460. {
  461. #if DBG
  462. MessageBox(NULL, FilePaths->Source, TEXT("Extracted: Source"), MB_OK);
  463. MessageBox(NULL, FilePaths->Target, TEXT("Extracted: Target"), MB_OK);
  464. #endif
  465. uRet = NO_ERROR;
  466. }
  467. }
  468. break;
  469. case SPFILENOTIFY_FILEINCABINET:
  470. {
  471. PFILE_IN_CABINET_INFO FileInfo = (PFILE_IN_CABINET_INFO) Param1;
  472. if (FileInfo)
  473. {
  474. //
  475. // If this is the file we want, then we want to extract it!
  476. //
  477. if ( !lstrcmpi(FileInfo->NameInCabinet, CosmaContext->szSourceFile) )
  478. {
  479. lstrcpy(FileInfo->FullTargetName, CosmaContext->szDestination);
  480. #if DBG
  481. MessageBox(NULL, FileInfo->NameInCabinet, TEXT("InCabinet: NameInCabinet"), MB_OK);
  482. MessageBox(NULL, FileInfo->FullTargetName, TEXT("InCabinet: FullTargetName"), MB_OK);
  483. #endif
  484. uRet = FILEOP_DOIT;
  485. }
  486. else
  487. {
  488. uRet = FILEOP_SKIP;
  489. }
  490. }
  491. }
  492. break;
  493. case SPFILENOTIFY_NEEDNEWCABINET:
  494. {
  495. #if DBG
  496. MessageBox(NULL, TEXT("Doh!"), TEXT("Need New Cabinet"), MB_OK);
  497. #endif
  498. uRet = NO_ERROR;
  499. }
  500. break;
  501. default:
  502. break;
  503. }
  504. return uRet;
  505. }
  506. BOOL
  507. ExtractFileFromCabinet(
  508. IN LPTSTR lpszCabinetPath,
  509. IN LPTSTR lpszSourceFile,
  510. IN LPTSTR lpszDestinationPath
  511. )
  512. {
  513. BOOL bRet = FALSE;
  514. COSMA_CONTEXT CosmaContext;
  515. //
  516. // Initialize the CosmaContext structure with the paths we need
  517. //
  518. ZeroMemory(&CosmaContext, sizeof(CosmaContext));
  519. lstrcpy(CosmaContext.szSourceFile, lpszSourceFile);
  520. lstrcpy(CosmaContext.szDestination, lpszDestinationPath);
  521. AddPath(CosmaContext.szDestination, lpszSourceFile);
  522. //
  523. // Create the directory where we will extract the file
  524. //
  525. CreateDirectory(lpszDestinationPath, NULL);
  526. //
  527. // Call SetupIterateCabinet to extract a file from the CAB
  528. //
  529. if ( SetupIterateCabinet(lpszCabinetPath,
  530. 0,
  531. (PSP_FILE_CALLBACK) CosmaMsgHandler,
  532. (LPVOID) &CosmaContext) &&
  533. EXIST(CosmaContext.szDestination) )
  534. {
  535. //
  536. // We only return true if we successfully extracted the file
  537. //
  538. bRet = TRUE;
  539. }
  540. return bRet;
  541. }
  542. static BOOL
  543. IsFileInDrvIndex(
  544. IN POFFLINE_QUEUE_CONTEXT OfflineContext,
  545. IN LPTSTR lpszSourceFile
  546. )
  547. {
  548. LPTSTR lpszDrvIndexFile = TEXT("inf\\drvindex.inf");
  549. HINF hInf = NULL;
  550. UINT uError = 0;
  551. BOOL bFound = FALSE;
  552. TCHAR szDrvIndexPath[MAX_PATH];
  553. //
  554. // Build a path to the offline image's %WINDIR%\\inf\\drvindex.inf
  555. // We're going to search for this file in the drvindex.inf. If it is there
  556. // then look for it in the driver.cab in the offline image driver cache and
  557. // if driver.cab is not found there we look for driver.cab in the sourcepath
  558. // specified in the offline registry.
  559. //
  560. lstrcpy(szDrvIndexPath, OfflineContext->OfflineWindowsDirectory);
  561. AddPath(szDrvIndexPath, lpszDrvIndexFile);
  562. if ( INVALID_HANDLE_VALUE != ( hInf = SetupOpenInfFile(szDrvIndexPath, NULL, INF_STYLE_WIN4|INF_STYLE_OLDNT, &uError) ) )
  563. {
  564. BOOL bRet = FALSE;
  565. INFCONTEXT InfContext;
  566. TCHAR szFileNameBuffer[MAX_PATH];
  567. //
  568. // Find the section appropriate to the passed in service name.
  569. //
  570. bRet = SetupFindFirstLine(hInf, TEXT("driver"), NULL, &InfContext);
  571. while (bRet && !bFound)
  572. {
  573. //
  574. // Initialize the buffer that gets the service name so we can see if it's the one we want
  575. //
  576. szFileNameBuffer[0] = NULLCHR;
  577. //
  578. // Call SetupGetStringField to get the service name for this AddService entry
  579. //
  580. bRet = SetupGetStringField(&InfContext, 0, szFileNameBuffer, AS(szFileNameBuffer), NULL);
  581. if ( bRet && *szFileNameBuffer && !lstrcmpi(szFileNameBuffer, lpszSourceFile) )
  582. {
  583. bFound = TRUE;
  584. }
  585. else
  586. {
  587. bRet = SetupFindNextLine(&InfContext, &InfContext);
  588. }
  589. }
  590. }
  591. return bFound;
  592. }
  593. static UINT
  594. FixCopyQueueStuff(
  595. IN POFFLINE_QUEUE_CONTEXT OfflineContext,
  596. IN LPTSTR lpszSourceFile,
  597. IN OUT LPTSTR lpszDestination
  598. )
  599. {
  600. UINT uRet = NO_ERROR;
  601. TCHAR szNewPath[MAX_PATH];
  602. if ( OfflineContext->InfPath && *OfflineContext->InfPath )
  603. {
  604. LPTSTR lpFilePart = NULL;
  605. // Check wether InfPath is a full path or just an INF name.
  606. //
  607. if ( GetFullPathName(OfflineContext->InfPath, AS(szNewPath), szNewPath, &lpFilePart) &&
  608. lpFilePart && *lpFilePart && lstrcmpi(OfflineContext->InfPath, lpFilePart) )
  609. {
  610. // Build a new path to the file inside InfPath.
  611. //
  612. *lpFilePart = NULLCHR;
  613. AddPath(szNewPath, lpszSourceFile);
  614. //
  615. // Make sure there is a valid string to play with...
  616. //
  617. if ( szNewPath[0] )
  618. {
  619. // If the file exists there, return FILEOP_NEWPATH.
  620. //
  621. if ( EXIST(szNewPath) )
  622. {
  623. uRet = FILEOP_NEWPATH;
  624. }
  625. else
  626. {
  627. // Look for the compressed version of the file as well.
  628. //
  629. szNewPath[lstrlen(szNewPath) - 1] = TEXT('_');
  630. if ( EXIST(szNewPath) )
  631. {
  632. uRet = FILEOP_NEWPATH;
  633. }
  634. }
  635. if ( uRet == FILEOP_NEWPATH )
  636. {
  637. #if DBG
  638. MessageBox(NULL, szNewPath, TEXT("INFPATH: WooHoo!!"), MB_OK);
  639. #endif
  640. if (lpszDestination)
  641. {
  642. // Cut off the filename.
  643. //
  644. *lpFilePart = NULLCHR;
  645. // Save this in the buffer.
  646. //
  647. lstrcpy((LPTSTR)lpszDestination, szNewPath);
  648. }
  649. return uRet;
  650. }
  651. #if DBG
  652. else
  653. {
  654. MessageBox(NULL, szNewPath, TEXT("INFPATH: File Not Found!!"), MB_OK);
  655. }
  656. #endif
  657. }
  658. }
  659. }
  660. //
  661. // Doh! It wasn't found in the InfPath path!
  662. // Let's try the OfflineWindowsDirectory
  663. //
  664. if (OfflineContext->OfflineWindowsDirectory && *OfflineContext->OfflineWindowsDirectory)
  665. {
  666. if ( IsFileInDrvIndex(OfflineContext, lpszSourceFile) )
  667. {
  668. LPTSTR lpszDriverCache = TEXT("Driver Cache");
  669. LPTSTR lpszDriverCabFile = TEXT("DRIVER.CAB");
  670. LPTSTR lpszTempPath = TEXT("TEMP");
  671. //
  672. // Build a path to the offline image's %WINDIR%\\Driver Cache\\i386\\DRIVER.CAB
  673. //
  674. lstrcpyn(szNewPath, OfflineContext->OfflineWindowsDirectory, AS(szNewPath));
  675. AddPath(szNewPath, lpszDriverCache);
  676. AddPath(szNewPath, IsIA64() ? TEXT("ia64") : TEXT("i386"));
  677. AddPath(szNewPath, lpszDriverCabFile);
  678. if ( ( EXIST(szNewPath) ) ||
  679. ( ( lstrcpyn(szNewPath, OfflineContext->OfflineSourcePath, AS(szNewPath)) ) &&
  680. ( AddPath(szNewPath, lpszDriverCabFile) ) &&
  681. ( EXIST(szNewPath) ) ) )
  682. {
  683. TCHAR szOfflineTemp[MAX_PATH];
  684. //
  685. // Build a path to the Offline image's %WINDIR%\\TEMP directory
  686. //
  687. lstrcpyn(szOfflineTemp, OfflineContext->OfflineWindowsDirectory, AS(szOfflineTemp));
  688. AddPath(szOfflineTemp, lpszTempPath);
  689. if ( ExtractFileFromCabinet(szNewPath,
  690. lpszSourceFile,
  691. szOfflineTemp) )
  692. {
  693. #if DBG
  694. MessageBox(NULL, TEXT("Succeeded!"), TEXT("ExtractFileFromCabinet"), MB_OK);
  695. #endif
  696. //
  697. // If the file exists, fill in the caller's buffer with the new location
  698. //
  699. if (lpszDestination)
  700. lstrcpy((LPTSTR)lpszDestination, szOfflineTemp);
  701. //
  702. // Fill in the TemporaryFilePath variable so we delete this file on ENDCOPY event
  703. //
  704. if (OfflineContext->TemporaryFilePath)
  705. {
  706. lstrcpy(OfflineContext->TemporaryFilePath, szOfflineTemp);
  707. AddPath(OfflineContext->TemporaryFilePath, lpszSourceFile);
  708. }
  709. //
  710. // We found the file, so want to return FILEOP_NEWPATH
  711. //
  712. uRet = FILEOP_NEWPATH;
  713. }
  714. #if DBG
  715. else
  716. {
  717. MessageBox(NULL, TEXT("Failed!"), TEXT("ExtractFileFromCabinet"), MB_OK);
  718. }
  719. #endif
  720. }
  721. #if DBG
  722. else
  723. {
  724. MessageBox(NULL, szNewPath, TEXT("DRVCAB: File Not Found!!"), MB_OK);
  725. }
  726. #endif
  727. }
  728. else
  729. {
  730. #if DBG
  731. MessageBox(NULL, lpszSourceFile, TEXT("File is not in drvindex.inf"), MB_OK);
  732. #endif
  733. // Search for it in the OfflineSourcePath
  734. //
  735. if ( OfflineContext->OfflineSourcePath && *OfflineContext->OfflineSourcePath )
  736. {
  737. // Build a new path to the file inside OfflineSourcePath
  738. //
  739. lstrcpyn(szNewPath, OfflineContext->OfflineSourcePath, AS(szNewPath));
  740. AddPath(szNewPath, lpszSourceFile);
  741. // If the file exists there, return FILEOP_NEWPATH.
  742. //
  743. if ( EXIST(szNewPath) )
  744. {
  745. uRet = FILEOP_NEWPATH;
  746. }
  747. else
  748. {
  749. // Look for the compressed version of the file as well.
  750. //
  751. szNewPath[lstrlen(szNewPath) - 1] = TEXT('_');
  752. if ( EXIST(szNewPath) )
  753. {
  754. uRet = FILEOP_NEWPATH;
  755. }
  756. }
  757. if ( uRet == FILEOP_NEWPATH )
  758. {
  759. #if DBG
  760. MessageBox(NULL, szNewPath, TEXT("SOURCEPATH: WooHoo!!"), MB_OK);
  761. #endif
  762. if (lpszDestination)
  763. {
  764. // Save this in the buffer.
  765. //
  766. lstrcpy((LPTSTR)lpszDestination, OfflineContext->OfflineSourcePath);
  767. }
  768. return uRet;
  769. }
  770. #if DBG
  771. else
  772. {
  773. MessageBox(NULL, szNewPath, TEXT("SOURCEPATH: File Not Found!!"), MB_OK);
  774. }
  775. #endif
  776. }
  777. }
  778. }
  779. return uRet;
  780. }
  781. static UINT
  782. OfflineQueueCallback(
  783. IN PVOID Context,
  784. IN UINT Notification,
  785. IN UINT_PTR Param1,
  786. IN UINT_PTR Param2
  787. )
  788. /*++
  789. ===============================================================================
  790. Routine Description:
  791. Callback function for setupapi to use as he's copying files.
  792. We'll use this to ensure that the files we copy get appended to
  793. setup.log, which in turn may get used when/if the user ever tries to
  794. use Windows repair capabilities.
  795. Arguments:
  796. Return Value:
  797. ===============================================================================
  798. --*/
  799. {
  800. UINT Status = FILEOP_ABORT;
  801. POFFLINE_QUEUE_CONTEXT OfflineContext = Context;
  802. //
  803. // Make sure that if we get these notification to check Param1.
  804. //
  805. switch (Notification) {
  806. case SPFILENOTIFY_COPYERROR:
  807. {
  808. PFILEPATHS FilePaths = (PFILEPATHS)Param1;
  809. if (FilePaths)
  810. {
  811. TCHAR szDestination[MAX_PATH];
  812. TCHAR szFullPathBuffer[MAX_PATH];
  813. LPTSTR lpszFilePart = NULL;
  814. //
  815. // Initialize the Destination buffer for the FILEOP_NEWPATH case
  816. //
  817. ZeroMemory(szDestination, sizeof(szDestination));
  818. //
  819. // Call GetFullPathName to strip the file name away from FilePaths->Source
  820. //
  821. if ( GetFullPathName((LPTSTR)FilePaths->Source,
  822. AS(szFullPathBuffer),
  823. szFullPathBuffer,
  824. &lpszFilePart) && lpszFilePart && *lpszFilePart )
  825. {
  826. //
  827. // Call FixCopyQueueStuff to find the missing file in some magic locations...
  828. //
  829. Status = FixCopyQueueStuff(OfflineContext, lpszFilePart, szDestination);
  830. }
  831. //
  832. // If the force flag is set and the target exists, delete the destination file...
  833. //
  834. if ( ( GetOfflineInstallFlags() & INSTALL_FLAG_FORCE ) &&
  835. ( SetFileAttributes( (LPTSTR)FilePaths->Target, FILE_ATTRIBUTE_NORMAL ) ) )
  836. {
  837. #ifdef DBG
  838. MessageBox(NULL, (LPTSTR)FilePaths->Target, TEXT("Deleting existing file"), MB_OK);
  839. #endif
  840. DeleteFile( (LPTSTR)FilePaths->Target );
  841. }
  842. //
  843. // If we got back FILEOP_NEWPATH, we want to fix up Param2 and let SetupAPI copy from there...
  844. //
  845. if ( (Status == FILEOP_NEWPATH) && *szDestination )
  846. {
  847. lstrcpy((LPTSTR)Param2, szDestination);
  848. }
  849. else
  850. {
  851. #if DBG
  852. MessageBox(NULL, (LPTSTR)FilePaths->Source, TEXT("CopyError: Skipping!"), MB_OK);
  853. #endif
  854. Status = FILEOP_SKIP;
  855. }
  856. return Status;
  857. }
  858. else
  859. {
  860. //
  861. // Bad Times!
  862. //
  863. }
  864. }
  865. break;
  866. case SPFILENOTIFY_NEEDMEDIA:
  867. {
  868. PSOURCE_MEDIA pSourceMedia = (PSOURCE_MEDIA) Param1;
  869. if (pSourceMedia)
  870. {
  871. TCHAR szDestination[MAX_PATH];
  872. //
  873. // Initialize the Destination buffer for the FILEOP_NEWPATH case
  874. //
  875. ZeroMemory(szDestination, sizeof(szDestination));
  876. //
  877. // Call FixCopyQueueStuff to find the missing file in some magic locations...
  878. //
  879. Status = FixCopyQueueStuff(OfflineContext, (LPTSTR)pSourceMedia->SourceFile, szDestination);
  880. //
  881. // If we got back FILEOP_NEWPATH, we want to fix up Param2 and let SetupAPI copy from there...
  882. //
  883. if ( (Status == FILEOP_NEWPATH) && *szDestination )
  884. {
  885. lstrcpy((LPTSTR)Param2, szDestination);
  886. }
  887. else
  888. {
  889. #if DBG
  890. MessageBox(NULL, pSourceMedia->SourceFile, TEXT("NeedMedia: Skipping!"), MB_OK);
  891. #endif
  892. Status = FILEOP_SKIP;
  893. }
  894. return Status;
  895. }
  896. else
  897. {
  898. //
  899. // Bad Times!
  900. //
  901. }
  902. }
  903. break;
  904. case SPFILENOTIFY_ENDCOPY:
  905. {
  906. //
  907. // If we extracted a file out to the TemporaryFilePath, we want to delete it now!
  908. //
  909. if (OfflineContext->TemporaryFilePath && *OfflineContext->TemporaryFilePath)
  910. {
  911. //
  912. // Do we care if this fails???
  913. //
  914. DeleteFile(OfflineContext->TemporaryFilePath);
  915. //
  916. // Re-initialize the TemporaryFilePath for the next file in the queue...
  917. //
  918. *(OfflineContext->TemporaryFilePath) = NULLCHR;
  919. }
  920. }
  921. break;
  922. default:
  923. //
  924. // If the caller passed in the "force" switch, then silently overwrite...
  925. // Note: The SPFILENOTIFY_TARGETEXISTS is a bit flag, which is why we check it here
  926. //
  927. if ( ( Notification & (SPFILENOTIFY_LANGMISMATCH | SPFILENOTIFY_TARGETNEWER | SPFILENOTIFY_TARGETEXISTS) ) &&
  928. ( GetOfflineInstallFlags() & INSTALL_FLAG_FORCE ) )
  929. {
  930. return ( FILEOP_DOIT );
  931. }
  932. break;
  933. }
  934. //
  935. // Use default processing, then check for errors.
  936. //
  937. Status = SetupDefaultQueueCallback( OfflineContext->DefaultContext,
  938. Notification,
  939. Param1,
  940. Param2 );
  941. return Status;
  942. }
  943. static VOID
  944. FreeOfflineContext(
  945. IN PVOID Context
  946. )
  947. {
  948. POFFLINE_QUEUE_CONTEXT OfflineContext = Context;
  949. try
  950. {
  951. if (OfflineContext->DefaultContext)
  952. {
  953. SetupTermDefaultQueueCallback(OfflineContext->DefaultContext);
  954. }
  955. //
  956. // Free the TemporaryFilePath buffer that we allocated
  957. //
  958. if (OfflineContext->TemporaryFilePath)
  959. FREE(OfflineContext->TemporaryFilePath);
  960. FREE(OfflineContext);
  961. }
  962. except(EXCEPTION_EXECUTE_HANDLER)
  963. {
  964. ;
  965. }
  966. }
  967. static PVOID
  968. InitOfflineQueueCallback(
  969. VOID
  970. )
  971. /*++
  972. ===============================================================================
  973. Routine Description:
  974. Initialize the data structure used for the callback that fires when
  975. we commit the file copy queue.
  976. Arguments:
  977. Return Value:
  978. ===============================================================================
  979. --*/
  980. {
  981. POFFLINE_QUEUE_CONTEXT OfflineContext;
  982. OfflineContext = MALLOC(sizeof(OFFLINE_QUEUE_CONTEXT));
  983. if( OfflineContext )
  984. {
  985. OfflineContext->InfPath = NULL;
  986. OfflineContext->OfflineSourcePath = NULL;
  987. OfflineContext->TemporaryFilePath = MALLOC(MAX_PATH * sizeof(TCHAR));
  988. OfflineContext->DefaultContext = SetupInitDefaultQueueCallbackEx( NULL,
  989. INVALID_HANDLE_VALUE,
  990. 0,
  991. 0,
  992. NULL );
  993. }
  994. return OfflineContext;
  995. }
  996. static
  997. DWORD GetSetOfflineInstallFlags(
  998. IN BOOL bSet,
  999. IN DWORD dwFlags
  1000. )
  1001. {
  1002. static DWORD dwOfflineFlags = 0;
  1003. DWORD dwRet = 0;
  1004. if ( bSet )
  1005. {
  1006. dwOfflineFlags = dwFlags;
  1007. }
  1008. else
  1009. {
  1010. dwRet = dwOfflineFlags;
  1011. }
  1012. return dwRet;
  1013. }
  1014. VOID
  1015. SetOfflineInstallFlags(
  1016. IN DWORD dwFlags
  1017. )
  1018. {
  1019. GetSetOfflineInstallFlags( TRUE, dwFlags );
  1020. }
  1021. DWORD
  1022. GetOfflineInstallFlags(
  1023. VOID
  1024. )
  1025. {
  1026. return ( GetSetOfflineInstallFlags( FALSE, 0 ) );
  1027. }
  1028. BOOL
  1029. UpdateOfflineDevicePath(
  1030. IN LPTSTR lpszInfPath,
  1031. IN HKEY hKeySoftware
  1032. )
  1033. {
  1034. BOOL fRet = FALSE;
  1035. LPTSTR lpszDevicePath;
  1036. DWORD cbDevicePath;
  1037. // Get a buffer for the device paths. It will be either empty if they
  1038. // don't have the optional additional paths key in the winbom.
  1039. //
  1040. if ( NULL != (lpszDevicePath = IniGetStringEx(lpszInfPath, INI_SEC_WBOM_DRIVERUPDATE, INI_VAL_WBOM_DEVICEPATH, NULL, &cbDevicePath)) )
  1041. {
  1042. // If we are saving this list to the registry, then
  1043. // we need to add to our buffer.
  1044. //
  1045. if ( *lpszDevicePath )
  1046. {
  1047. fRet = UpdateDevicePathEx( hKeySoftware,
  1048. TEXT("Microsoft\\Windows\\CurrentVersion"),
  1049. lpszDevicePath,
  1050. NULL,
  1051. FALSE );
  1052. }
  1053. // Clean up any memory (macro checks for NULL).
  1054. //
  1055. FREE(lpszDevicePath);
  1056. }
  1057. return fRet;
  1058. }