Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5010 lines
160 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. backup.c
  5. Abstract:
  6. Routines to control backup during install process
  7. And restore of an old install process
  8. Author:
  9. Jamie Hunter (jamiehun) 13-Jan-1997
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. VOID
  15. pSetupExemptFileFromProtection(
  16. IN PCTSTR FileName,
  17. IN DWORD FileChangeFlags,
  18. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  19. OUT PDWORD QueueNodeFlags OPTIONAL
  20. );
  21. //
  22. // ==========================================================
  23. //
  24. DWORD
  25. pSetupQueueBackupCopy(
  26. IN HSPFILEQ QueueHandle,
  27. IN LONG TargetRootPath,
  28. IN LONG TargetSubDir, OPTIONAL
  29. IN LONG TargetFilename,
  30. IN LONG BackupRootPath,
  31. IN LONG BackupSubDir, OPTIONAL
  32. IN LONG BackupFilename
  33. )
  34. /*++
  35. Routine Description:
  36. Place a backup copy operation on a setup file queue.
  37. Target is to be backed up at Backup location
  38. Arguments:
  39. QueueHandle - supplies a handle to a setup file queue, as returned
  40. by SetupOpenFileQueue.
  41. TargetRootPath - Supplies the source directory, eg C:\WINNT\
  42. TargetSubDir - Supplies the optional sub-directory (eg WINNT if RootPath = c:\ )
  43. TargetFilename - supplies the filename part of the file to be copied.
  44. BackupRootPath - supplies the directory where the file is to be copied.
  45. BackupSubDir - supplies the optional sub-directory
  46. BackupFilename - supplies the name of the target file.
  47. Return Value:
  48. same value as GetLastError() indicating error, or NO_ERROR
  49. --*/
  50. {
  51. PSP_FILE_QUEUE Queue;
  52. PSP_FILE_QUEUE_NODE QueueNode,TempNode;
  53. int Size;
  54. DWORD Err;
  55. PVOID StringTable;
  56. PTSTR FullRootName;
  57. Queue = (PSP_FILE_QUEUE)QueueHandle;
  58. Err = NO_ERROR;
  59. try {
  60. StringTable = Queue->StringTable; // used for strings in source queue
  61. } except (EXCEPTION_EXECUTE_HANDLER) {
  62. Err = ERROR_INVALID_HANDLE;
  63. goto clean0;
  64. }
  65. //
  66. // Allocate a queue structure.
  67. //
  68. QueueNode = MyMalloc(sizeof(SP_FILE_QUEUE_NODE));
  69. if (!QueueNode) {
  70. Err = ERROR_NOT_ENOUGH_MEMORY;
  71. goto clean0;
  72. }
  73. //
  74. // Operation is backup.
  75. //
  76. QueueNode->Operation = FILEOP_BACKUP;
  77. QueueNode->InternalFlags = 0;
  78. QueueNode->SourceRootPath = BackupRootPath;
  79. QueueNode->SourcePath = BackupSubDir;
  80. QueueNode->SourceFilename = BackupFilename;
  81. // if target has a sub-dir, we have to combine root and subdir into one string
  82. if (TargetSubDir != -1) {
  83. FullRootName = pSetupFormFullPath(
  84. StringTable,
  85. TargetRootPath,
  86. TargetSubDir,
  87. -1);
  88. if (!FullRootName) {
  89. Err = ERROR_NOT_ENOUGH_MEMORY;
  90. goto clean1;
  91. }
  92. TargetRootPath = pSetupStringTableAddString(StringTable,
  93. FullRootName,
  94. STRTAB_CASE_SENSITIVE
  95. );
  96. MyFree(FullRootName);
  97. if (TargetRootPath == -1) {
  98. Err = ERROR_NOT_ENOUGH_MEMORY;
  99. goto clean1;
  100. }
  101. // now combined into TargetRootPath
  102. TargetSubDir = -1;
  103. }
  104. QueueNode->TargetDirectory = TargetRootPath;
  105. QueueNode->TargetFilename = TargetFilename;
  106. QueueNode->Next = NULL;
  107. //
  108. // Link the node onto the end of the backup queue
  109. //
  110. if (Queue->BackupQueue) {
  111. for (TempNode = Queue->BackupQueue; TempNode->Next; TempNode=TempNode->Next) /* blank */ ;
  112. TempNode->Next = QueueNode;
  113. } else {
  114. Queue->BackupQueue = QueueNode;
  115. }
  116. Queue->BackupNodeCount++;
  117. Err = NO_ERROR;
  118. goto clean0;
  119. clean1:
  120. MyFree(QueueNode);
  121. clean0:
  122. SetLastError(Err);
  123. return Err;
  124. }
  125. //
  126. // ==========================================================
  127. //
  128. BOOL
  129. pSetupGetFullBackupPath(
  130. OUT PTSTR FullPath,
  131. IN PCTSTR Path, OPTIONAL
  132. IN UINT TargetBufferSize,
  133. OUT PUINT RequiredSize OPTIONAL
  134. )
  135. /*++
  136. Routine Description:
  137. This routine takes a potentially relative path
  138. and concatenates it to the base path
  139. Arguments:
  140. FullPath - Destination for full path
  141. Path - Relative source path to backup directory if specified.
  142. If NULL, generates a temporary path
  143. TargetBufferSize - Size of buffer (characters)
  144. RequiredSize - Filled in with size required to contain full path
  145. Return Value:
  146. If the function succeeds, return TRUE
  147. If there was an error, return FALSE
  148. --*/
  149. {
  150. UINT PathLen;
  151. LPCTSTR Base = WindowsBackupDirectory;
  152. if(!Path) {
  153. //
  154. // temporary location
  155. //
  156. Path = SP_BACKUP_OLDFILES;
  157. Base = WindowsDirectory;
  158. }
  159. //
  160. // Backup directory is stored in "WindowsBackupDirectory" for permanent backups
  161. // and WindowsDirectory\SP_BACKUP_OLDFILES for temporary backups
  162. //
  163. PathLen = lstrlen(Base);
  164. if ( FullPath == NULL || TargetBufferSize <= PathLen ) {
  165. // just calculate required path len
  166. FullPath = (PTSTR) Base;
  167. TargetBufferSize = 0;
  168. } else {
  169. // calculate and copy
  170. lstrcpy(FullPath, Base);
  171. }
  172. return pSetupConcatenatePaths(FullPath, Path, TargetBufferSize, RequiredSize);
  173. }
  174. //
  175. // ==========================================================
  176. //
  177. DWORD
  178. pSetupBackupCopyString(
  179. IN PVOID DestStringTable,
  180. OUT PLONG DestStringID,
  181. IN PVOID SrcStringTable,
  182. IN LONG SrcStringID
  183. )
  184. /*++
  185. Routine Description:
  186. Gets a string from source string table, adds it to destination string table with new ID.
  187. Arguments:
  188. DestStringTable - Where string has to go
  189. DestStringID - pointer, set to string ID in respect to DestStringTable
  190. SrcStringTable - Where string is coming from
  191. StringID - string ID in respect to SrcStringTable
  192. Return Value:
  193. Returns error code (LastError is also set)
  194. If the function succeeds, returns NO_ERROR
  195. --*/
  196. {
  197. DWORD Err = NO_ERROR;
  198. LONG DestID;
  199. PTSTR String;
  200. if (DestStringID == NULL) {
  201. Err = ERROR_INVALID_HANDLE;
  202. goto clean0;
  203. }
  204. if (SrcStringID == -1) {
  205. // "not supplied"
  206. DestID = -1;
  207. } else {
  208. // actually need to copy
  209. String = pSetupStringTableStringFromId( SrcStringTable, SrcStringID );
  210. if (String == NULL) {
  211. Err = ERROR_NOT_ENOUGH_MEMORY;
  212. goto clean0;
  213. }
  214. DestID = pSetupStringTableAddString( DestStringTable, String, STRTAB_CASE_SENSITIVE );
  215. if (DestID == -1) {
  216. Err = ERROR_NOT_ENOUGH_MEMORY;
  217. goto clean0;
  218. }
  219. *DestStringID = DestID;
  220. }
  221. Err = NO_ERROR;
  222. clean0:
  223. SetLastError(Err);
  224. return Err;
  225. }
  226. //
  227. // ==========================================================
  228. //
  229. DWORD
  230. pSetupBackupGetTargetByPath(
  231. IN HSPFILEQ QueueHandle,
  232. IN PVOID PathStringTable, OPTIONAL
  233. IN PCTSTR TargetPath, OPTIONAL
  234. IN LONG TargetRoot,
  235. IN LONG TargetSubDir, OPTIONAL
  236. IN LONG TargetFilename,
  237. OUT PLONG TableID, OPTIONAL
  238. OUT PSP_TARGET_ENT TargetInfo
  239. )
  240. /*++
  241. Routine Description:
  242. Given a pathname, obtains/creates target info
  243. Arguments:
  244. QueueHandle - Queue we're looking at
  245. PathStringTable - String table used for the Target Root/SubDir/Filename strings, NULL if same as QueueHandle's
  246. TargetPath - if given, is the full path, previously generated
  247. TargetRoot - root portion, eg c:\winnt
  248. TargetSubDir - optional sub-directory portion, -1 if not provided
  249. TargetFilename - filename , eg readme.txt
  250. TableID - filled with ID for future use in pSetupBackupGetTargetByID or pSetupBackupSetTargetByID
  251. TargetInfo - Filled with information about target
  252. Return Value:
  253. Returns error code (LastError is also set)
  254. If the function succeeds, returns NO_ERROR
  255. --*/
  256. {
  257. LONG PathID;
  258. TCHAR PathBuffer[MAX_PATH];
  259. PTSTR TmpPtr;
  260. PVOID LookupTable = NULL;
  261. PVOID QueueStringTable = NULL;
  262. PTSTR FullTargetPath = NULL;
  263. DWORD Err = NO_ERROR;
  264. PSP_FILE_QUEUE Queue;
  265. DWORD RequiredSize;
  266. Queue = (PSP_FILE_QUEUE)QueueHandle;
  267. try {
  268. LookupTable = Queue->TargetLookupTable; // used for path lookup in source queue
  269. QueueStringTable = Queue->StringTable; // used for strings in source queue
  270. } except (EXCEPTION_EXECUTE_HANDLER) {
  271. Err = ERROR_INVALID_HANDLE;
  272. goto clean0;
  273. }
  274. if (PathStringTable == NULL) {
  275. // default string table is that of queue's
  276. PathStringTable = QueueStringTable;
  277. }
  278. if (TargetPath == NULL) {
  279. // obtain the complete target path and filename (Duplicated String)
  280. FullTargetPath = pSetupFormFullPath(
  281. PathStringTable,
  282. TargetRoot,
  283. TargetSubDir,
  284. TargetFilename);
  285. if (!FullTargetPath) {
  286. Err = ERROR_NOT_ENOUGH_MEMORY;
  287. goto clean0;
  288. }
  289. TargetPath = FullTargetPath;
  290. }
  291. //
  292. // normalize path
  293. //
  294. RequiredSize = GetFullPathName(TargetPath,
  295. SIZECHARS(PathBuffer),
  296. PathBuffer,
  297. &TmpPtr
  298. );
  299. //
  300. // This call should always succeed.
  301. //
  302. MYASSERT((RequiredSize > 0) &&
  303. (RequiredSize < SIZECHARS(PathBuffer)) // RequiredSize doesn't include terminating NULL char
  304. );
  305. //
  306. // Even though we asserted that this should not be the case above,
  307. // we should handle failure in case asserts are turned off.
  308. //
  309. if(!RequiredSize) {
  310. Err = GetLastError();
  311. goto clean0;
  312. } else if(RequiredSize >= SIZECHARS(PathBuffer)) {
  313. Err = ERROR_BUFFER_OVERFLOW;
  314. goto clean0;
  315. }
  316. PathID = pSetupStringTableLookUpStringEx(LookupTable, PathBuffer, 0, TargetInfo, sizeof(SP_TARGET_ENT));
  317. if (PathID == -1) {
  318. ZeroMemory(TargetInfo, sizeof(SP_TARGET_ENT));
  319. if (PathStringTable != QueueStringTable) {
  320. // need to add entries to Queue's string table if we're using another
  321. Err = pSetupBackupCopyString(QueueStringTable, &TargetRoot, PathStringTable, TargetRoot);
  322. if (Err != NO_ERROR) {
  323. goto clean0;
  324. }
  325. Err = pSetupBackupCopyString(QueueStringTable, &TargetSubDir, PathStringTable, TargetSubDir);
  326. if (Err != NO_ERROR) {
  327. goto clean0;
  328. }
  329. Err = pSetupBackupCopyString(QueueStringTable, &TargetFilename, PathStringTable, TargetFilename);
  330. if (Err != NO_ERROR) {
  331. goto clean0;
  332. }
  333. PathStringTable = QueueStringTable;
  334. }
  335. TargetInfo->TargetRoot = TargetRoot;
  336. TargetInfo->TargetSubDir = TargetSubDir;
  337. TargetInfo->TargetFilename = TargetFilename;
  338. TargetInfo->BackupRoot = -1;
  339. TargetInfo->BackupSubDir = -1;
  340. TargetInfo->BackupFilename = -1;
  341. TargetInfo->NewTargetFilename = -1;
  342. TargetInfo->InternalFlags = 0;
  343. PathID = pSetupStringTableAddStringEx(LookupTable, PathBuffer, 0, TargetInfo, sizeof(SP_TARGET_ENT));
  344. if (PathID == -1)
  345. {
  346. Err = ERROR_NOT_ENOUGH_MEMORY;
  347. goto clean0;
  348. }
  349. }
  350. if (TableID != NULL) {
  351. *TableID = PathID;
  352. }
  353. Err = NO_ERROR;
  354. clean0:
  355. if (FullTargetPath != NULL) {
  356. MyFree(FullTargetPath);
  357. }
  358. SetLastError(Err);
  359. return Err;
  360. }
  361. //
  362. // ==========================================================
  363. //
  364. DWORD
  365. pSetupBackupGetTargetByID(
  366. IN HSPFILEQ QueueHandle,
  367. IN LONG TableID,
  368. OUT PSP_TARGET_ENT TargetInfo
  369. )
  370. /*++
  371. Routine Description:
  372. Given an entry in the LookupTable, gets info
  373. Arguments:
  374. QueueHandle - Queue we're looking at
  375. TableID - ID relating to string entry we've found (via pSetupBackupGetTargetByPath)
  376. TargetInfo - Filled with information about target
  377. Return Value:
  378. Returns error code (LastError is also set)
  379. If the function succeeds, returns NO_ERROR
  380. --*/
  381. {
  382. PVOID LookupTable = NULL;
  383. DWORD Err = NO_ERROR;
  384. PSP_FILE_QUEUE Queue;
  385. Queue = (PSP_FILE_QUEUE)QueueHandle;
  386. try {
  387. LookupTable = Queue->TargetLookupTable; // used for strings in source queue
  388. }
  389. except (EXCEPTION_EXECUTE_HANDLER)
  390. {
  391. Err = ERROR_INVALID_HANDLE;
  392. goto clean0;
  393. }
  394. if (pSetupStringTableGetExtraData(LookupTable, TableID, TargetInfo, sizeof(SP_TARGET_ENT)) == FALSE) {
  395. Err = ERROR_INVALID_HANDLE;
  396. goto clean0;
  397. }
  398. Err = NO_ERROR;
  399. clean0:
  400. SetLastError(Err);
  401. return Err;
  402. }
  403. //
  404. // ==========================================================
  405. //
  406. DWORD
  407. pSetupBackupSetTargetByID(
  408. IN HSPFILEQ QueueHandle,
  409. IN LONG TableID,
  410. IN PSP_TARGET_ENT TargetInfo
  411. )
  412. /*++
  413. Routine Description:
  414. Given an entry in the LookupTable, sets info
  415. Arguments:
  416. QueueHandle - Queue we're looking at
  417. TableID - ID relating to string entry we've found (via pSetupBackupGetTargetByPath)
  418. TargetInfo - Filled with information about target
  419. Return Value:
  420. Returns error code (LastError is also set)
  421. If the function succeeds, returns NO_ERROR
  422. --*/
  423. {
  424. PVOID LookupTable = NULL;
  425. DWORD Err = NO_ERROR;
  426. PSP_FILE_QUEUE Queue;
  427. Queue = (PSP_FILE_QUEUE)QueueHandle;
  428. try {
  429. LookupTable = Queue->TargetLookupTable; // used for strings in source queue
  430. }
  431. except (EXCEPTION_EXECUTE_HANDLER)
  432. {
  433. Err = ERROR_INVALID_HANDLE;
  434. goto clean0;
  435. }
  436. if ( pSetupStringTableSetExtraData(LookupTable, TableID, TargetInfo, sizeof(SP_TARGET_ENT)) == FALSE) {
  437. Err = ERROR_INVALID_HANDLE;
  438. goto clean0;
  439. }
  440. Err = NO_ERROR;
  441. clean0:
  442. SetLastError(Err);
  443. return Err;
  444. }
  445. //
  446. // ==========================================================
  447. //
  448. DWORD
  449. pSetupBackupGetReinstallKeyStrings(
  450. IN PSP_FILE_QUEUE BackupFileQueue,
  451. IN HDEVINFO DeviceInfoSet,
  452. IN PSP_DEVINFO_DATA DeviceInfoData,
  453. IN PCTSTR DeviceID
  454. )
  455. /*++
  456. Routine Description:
  457. This routine will save the values needed to create the Reinstall backup key
  458. in the string table of the backup queue. We save these strings in the string
  459. table before the new drivers are installed and then create the registry key
  460. after the new device is installed. It is done this way because the Rollback
  461. UI code will look for the Reinstall subkeys, so we want to make sure that
  462. we have successfully backed-up all of the needed files before we create
  463. this Reinstall subkey.
  464. Arguments:
  465. BackupFileQueue -
  466. DeviceInfoSet -
  467. DeviceInfoData -
  468. DeviceID -
  469. Return Value:
  470. Returns error code (LastError is also set)
  471. If the function succeeds, returns NO_ERROR
  472. --*/
  473. {
  474. DWORD Err = NO_ERROR;
  475. HKEY hKeyDevReg = INVALID_HANDLE_VALUE;
  476. DWORD RegCreated, cbData;
  477. TCHAR Buffer[MAX_PATH];
  478. try {
  479. //
  480. // Get the DeviceDesc of the device and fill in the BackupDevDescID and
  481. // BackupDisplayNameID values in the string table. This value is
  482. // required since it is needed during a rollback for us to choose the
  483. // exact driver that was installed from the specific INF.
  484. //
  485. if (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  486. DeviceInfoData,
  487. SPDRP_DEVICEDESC,
  488. NULL,
  489. (PBYTE)Buffer,
  490. sizeof(Buffer),
  491. NULL)) {
  492. Err = GetLastError();
  493. goto clean0;
  494. }
  495. BackupFileQueue->BackupDeviceDescID =
  496. pSetupStringTableAddString(BackupFileQueue->StringTable,
  497. Buffer,
  498. STRTAB_CASE_SENSITIVE);
  499. if (BackupFileQueue->BackupDeviceDescID == -1) {
  500. Err = ERROR_NOT_ENOUGH_MEMORY;
  501. goto clean0;
  502. }
  503. //
  504. // At this point we will also set the BackupDisplayNameID value just in
  505. // case the device does not have a FriendlyName.
  506. //
  507. BackupFileQueue->BackupDisplayNameID =
  508. pSetupStringTableAddString(BackupFileQueue->StringTable,
  509. Buffer,
  510. STRTAB_CASE_SENSITIVE);
  511. if (BackupFileQueue->BackupDisplayNameID == -1) {
  512. Err = ERROR_NOT_ENOUGH_MEMORY;
  513. goto clean0;
  514. }
  515. //
  516. // We will try and get the device's FriendlyName. If it has one then we
  517. // will set the BackupDisplayNameID to this value, otherwise DisplayName
  518. // will just be the DeviceDesc.
  519. //
  520. if (SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  521. DeviceInfoData,
  522. SPDRP_FRIENDLYNAME,
  523. NULL,
  524. (PBYTE)Buffer,
  525. sizeof(Buffer),
  526. NULL)) {
  527. BackupFileQueue->BackupDisplayNameID =
  528. pSetupStringTableAddString(BackupFileQueue->StringTable,
  529. Buffer,
  530. STRTAB_CASE_SENSITIVE);
  531. if (BackupFileQueue->BackupDisplayNameID == -1) {
  532. Err = ERROR_NOT_ENOUGH_MEMORY;
  533. goto clean0;
  534. }
  535. }
  536. //
  537. // Set the BackupMfgID value.
  538. //
  539. if (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  540. DeviceInfoData,
  541. SPDRP_MFG,
  542. NULL,
  543. (PBYTE)Buffer,
  544. sizeof(Buffer),
  545. NULL)) {
  546. Err = GetLastError();
  547. goto clean0;
  548. }
  549. BackupFileQueue->BackupMfgID =
  550. pSetupStringTableAddString(BackupFileQueue->StringTable,
  551. Buffer,
  552. STRTAB_CASE_SENSITIVE);
  553. if (BackupFileQueue->BackupMfgID == -1) {
  554. Err = ERROR_NOT_ENOUGH_MEMORY;
  555. goto clean0;
  556. }
  557. //
  558. // Set the BackupProviderNameID value.
  559. //
  560. hKeyDevReg = SetupDiOpenDevRegKey(DeviceInfoSet,
  561. DeviceInfoData,
  562. DICS_FLAG_GLOBAL,
  563. 0,
  564. DIREG_DRV,
  565. KEY_READ
  566. );
  567. if (hKeyDevReg == INVALID_HANDLE_VALUE) {
  568. goto clean0;
  569. }
  570. cbData = sizeof(Buffer);
  571. Err = RegQueryValueEx(hKeyDevReg,
  572. pszProviderName,
  573. NULL,
  574. NULL,
  575. (PBYTE)Buffer,
  576. &cbData
  577. );
  578. RegCloseKey(hKeyDevReg);
  579. if (Err != ERROR_SUCCESS) {
  580. goto clean0;
  581. }
  582. BackupFileQueue->BackupProviderNameID =
  583. pSetupStringTableAddString(BackupFileQueue->StringTable,
  584. Buffer,
  585. STRTAB_CASE_SENSITIVE);
  586. if (BackupFileQueue->BackupProviderNameID == -1) {
  587. Err = ERROR_NOT_ENOUGH_MEMORY;
  588. goto clean0;
  589. }
  590. //
  591. // Set the DeviceInstanceIds value. This is a multi-sz value so make
  592. // sure we put a double NULL on the end.
  593. //
  594. BackupFileQueue->BackupDeviceInstanceID =
  595. pSetupStringTableAddString(BackupFileQueue->StringTable,
  596. (PTSTR)DeviceID,
  597. STRTAB_CASE_SENSITIVE);
  598. if (BackupFileQueue->BackupDeviceInstanceID == -1) {
  599. Err = ERROR_NOT_ENOUGH_MEMORY;
  600. goto clean0;
  601. }
  602. clean0: ; // Nothing to do.
  603. } except(EXCEPTION_EXECUTE_HANDLER) {
  604. //
  605. // if we except, assume it's due to invalid parameter
  606. //
  607. Err = ERROR_INVALID_PARAMETER;
  608. }
  609. SetLastError(Err);
  610. return Err;
  611. }
  612. //
  613. // ==========================================================
  614. //
  615. DWORD
  616. pSetupBackupCreateReinstallKey(
  617. IN PSP_FILE_QUEUE BackupFileQueue
  618. )
  619. /*++
  620. Routine Description:
  621. This routine will create the needed reinstall registry key so that these
  622. drivers can be later rolled back. The reinstall registry key lives in the
  623. following location:
  624. HKLM\Software\Microsoft\Windows\CurrentVersion\Reinstall\xxxx
  625. where xxxx is the BackupInstanceId.
  626. Under this key we will store the following information
  627. DisplayName - This is the name that is displayed in any UI of
  628. drivers that can be reinstalled. This is normally
  629. just the device description.
  630. DeviceInstanceIds - Multi-sz string of the device instance Ids of
  631. every device that is using this backup. Setupapi
  632. only sets the first device instance Id. Newdev
  633. can append other device instance Ids to this list
  634. if it is doing multiple device installs (in the
  635. case of UpdateDriverForPlugAndPlayDevices or
  636. InstallWindowsUpdateDriver).
  637. ReinstallString - The full backup path including the INF file
  638. DeviceDesc - The DeviceDesc of the driver that was installed.
  639. This is needed to make sure we pick the identical
  640. driver during a roll back.
  641. Mfg - The Mfg of the driver that was installed.
  642. This is needed to make sure we pick the identical
  643. driver during a roll back.
  644. ProviderName - The ProviderName of the driver that was installed.
  645. This is needed to make sure we pick the identical
  646. driver during a roll back.
  647. Arguments:
  648. BackupFileQueue -
  649. Return Value:
  650. Returns error code (LastError is also set)
  651. If the function succeeds, returns NO_ERROR
  652. --*/
  653. {
  654. DWORD Err = NO_ERROR;
  655. HKEY hKeyReinstall = INVALID_HANDLE_VALUE;
  656. HKEY hKeyReinstallInstance = INVALID_HANDLE_VALUE;
  657. DWORD RegCreated, cbData;
  658. TCHAR Buffer[MAX_PATH];
  659. BOOL b;
  660. try {
  661. //
  662. // Make sure the BackupInfID is valid. If it is -1 then something went
  663. // wrong during the backup and so we do not want to create the Reinstall
  664. // instance subkey.
  665. //
  666. if (BackupFileQueue->BackupInfID == -1) {
  667. Err = ERROR_NO_BACKUP;
  668. goto clean0;
  669. }
  670. //
  671. // Open/Create the Reinstall registry key. Call RegCreateKeyEx in case
  672. // this is the first time a backup is being performed and this key
  673. // does not yet exist.
  674. //
  675. Err = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  676. pszReinstallPath,
  677. 0,
  678. NULL,
  679. REG_OPTION_NON_VOLATILE,
  680. KEY_WRITE,
  681. NULL,
  682. &hKeyReinstall,
  683. &RegCreated
  684. );
  685. if (Err != ERROR_SUCCESS) {
  686. goto clean0;
  687. }
  688. //
  689. // Create the Reinstall instance key under the Reinstall key.
  690. //
  691. cbData = MAX_PATH;
  692. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  693. BackupFileQueue->BackupInstanceID,
  694. Buffer,
  695. &cbData);
  696. if (b == FALSE) {
  697. if (cbData == 0) {
  698. Err = ERROR_NO_BACKUP;
  699. } else {
  700. Err = ERROR_INSUFFICIENT_BUFFER;
  701. }
  702. goto clean0;
  703. }
  704. Err = RegCreateKeyEx(hKeyReinstall,
  705. Buffer,
  706. 0,
  707. NULL,
  708. REG_OPTION_NON_VOLATILE,
  709. KEY_WRITE,
  710. NULL,
  711. &hKeyReinstallInstance,
  712. &RegCreated
  713. );
  714. if (Err != ERROR_SUCCESS) {
  715. goto clean0;
  716. }
  717. //
  718. // Add the DeviceDesc to the Reinstall instance subkey
  719. //
  720. cbData = MAX_PATH;
  721. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  722. BackupFileQueue->BackupDeviceDescID,
  723. Buffer,
  724. &cbData);
  725. if (b == FALSE) {
  726. if (cbData == 0) {
  727. Err = ERROR_NO_BACKUP;
  728. } else {
  729. Err = ERROR_INSUFFICIENT_BUFFER;
  730. }
  731. goto clean0;
  732. }
  733. Err = RegSetValueEx(hKeyReinstallInstance,
  734. pszDeviceDesc,
  735. 0,
  736. REG_SZ,
  737. (PBYTE)Buffer,
  738. (lstrlen(Buffer) + 1) * sizeof(TCHAR)
  739. );
  740. if (Err != ERROR_SUCCESS) {
  741. goto clean0;
  742. }
  743. //
  744. // Add the DisplayName to the Reinstall instance subkey
  745. //
  746. cbData = MAX_PATH;
  747. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  748. BackupFileQueue->BackupDisplayNameID,
  749. Buffer,
  750. &cbData);
  751. if (b == FALSE) {
  752. if (cbData == 0) {
  753. Err = ERROR_NO_BACKUP;
  754. } else {
  755. Err = ERROR_INSUFFICIENT_BUFFER;
  756. }
  757. goto clean0;
  758. }
  759. RegSetValueEx(hKeyReinstallInstance,
  760. pszReinstallDisplayName,
  761. 0,
  762. REG_SZ,
  763. (PBYTE)Buffer,
  764. (lstrlen(Buffer) + 1) * sizeof(TCHAR)
  765. );
  766. if (Err != ERROR_SUCCESS) {
  767. goto clean0;
  768. }
  769. //
  770. // Add the Mfg to the Reinstall instance subkey
  771. //
  772. cbData = MAX_PATH;
  773. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  774. BackupFileQueue->BackupMfgID,
  775. Buffer,
  776. &cbData);
  777. if (b == FALSE) {
  778. if (cbData == 0) {
  779. Err = ERROR_NO_BACKUP;
  780. } else {
  781. Err = ERROR_INSUFFICIENT_BUFFER;
  782. }
  783. goto clean0;
  784. }
  785. Err = RegSetValueEx(hKeyReinstallInstance,
  786. pszMfg,
  787. 0,
  788. REG_SZ,
  789. (PBYTE)Buffer,
  790. (lstrlen(Buffer) + 1) * sizeof(TCHAR)
  791. );
  792. if (Err != ERROR_SUCCESS) {
  793. goto clean0;
  794. }
  795. //
  796. // Add the ProviderName to the Reinstall instance subkey
  797. //
  798. cbData = MAX_PATH;
  799. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  800. BackupFileQueue->BackupProviderNameID,
  801. Buffer,
  802. &cbData);
  803. if (b == FALSE) {
  804. if (cbData == 0) {
  805. Err = ERROR_NO_BACKUP;
  806. } else {
  807. Err = ERROR_INSUFFICIENT_BUFFER;
  808. }
  809. goto clean0;
  810. }
  811. Err = RegSetValueEx(hKeyReinstallInstance,
  812. pszProviderName,
  813. 0,
  814. REG_SZ,
  815. (PBYTE)Buffer,
  816. (lstrlen(Buffer) + 1) * sizeof(TCHAR)
  817. );
  818. if (Err != ERROR_SUCCESS) {
  819. goto clean0;
  820. }
  821. //
  822. // Set the DeviceInstanceIds value. This is a multi-sz value so make
  823. // sure we put a double NULL on the end.
  824. //
  825. //
  826. // Add the Mfg to the Reinstall instance subkey
  827. //
  828. cbData = MAX_PATH;
  829. ZeroMemory(Buffer, sizeof(Buffer));
  830. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  831. BackupFileQueue->BackupDeviceInstanceID,
  832. Buffer,
  833. &cbData);
  834. if (b == FALSE) {
  835. if (cbData == 0) {
  836. Err = ERROR_NO_BACKUP;
  837. } else {
  838. Err = ERROR_INSUFFICIENT_BUFFER;
  839. }
  840. goto clean0;
  841. }
  842. Err = RegSetValueEx(hKeyReinstallInstance,
  843. pszReinstallDeviceInstanceIds,
  844. 0,
  845. REG_MULTI_SZ,
  846. (PBYTE)Buffer,
  847. (lstrlen(Buffer) + 2) * sizeof(TCHAR)
  848. );
  849. if (Err != ERROR_SUCCESS) {
  850. goto clean0;
  851. }
  852. //
  853. // Add the ReinstallString to the Reinstall instance subkey
  854. //
  855. cbData = MAX_PATH;
  856. b = pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  857. BackupFileQueue->BackupInfID,
  858. Buffer,
  859. &cbData);
  860. if (b == FALSE) {
  861. if (cbData == 0) {
  862. Err = ERROR_NO_BACKUP;
  863. } else {
  864. Err = ERROR_INSUFFICIENT_BUFFER;
  865. }
  866. goto clean0;
  867. }
  868. Err = RegSetValueEx(hKeyReinstallInstance,
  869. pszReinstallString,
  870. 0,
  871. REG_SZ,
  872. (PBYTE)Buffer,
  873. (lstrlen(Buffer) + 1) * sizeof(TCHAR)
  874. );
  875. if (Err != ERROR_SUCCESS) {
  876. goto clean0;
  877. }
  878. clean0: ; // Nothing to do.
  879. } except(EXCEPTION_EXECUTE_HANDLER) {
  880. //
  881. // if we except, assume it's due to invalid parameter
  882. //
  883. Err = ERROR_INVALID_PARAMETER;
  884. }
  885. if (hKeyReinstallInstance != INVALID_HANDLE_VALUE) {
  886. RegCloseKey(hKeyReinstallInstance);
  887. }
  888. if (hKeyReinstall != INVALID_HANDLE_VALUE) {
  889. RegCloseKey(hKeyReinstall);
  890. }
  891. SetLastError(Err);
  892. return Err;
  893. }
  894. //
  895. // ==========================================================
  896. //
  897. DWORD
  898. pSetupBackupAppendFiles(
  899. IN HSPFILEQ TargetQueueHandle,
  900. IN PCTSTR BackupSubDir,
  901. IN DWORD BackupFlags,
  902. IN HSPFILEQ SourceQueueHandle OPTIONAL
  903. )
  904. /*++
  905. Routine Description:
  906. This routine will take a list of files from SourceQueueHandle Copy sub-queue's
  907. These files will appear in the Target Queue's target cache
  908. And may be placed into the Target Backup Queue
  909. Typically the copy queue is entries of..
  910. <oldsrc-root>\<oldsrc-sub>\<oldsrc-name> copied to
  911. <olddest-path>\<olddest-name>
  912. Arguments:
  913. TargetQueueHandle - Where Backups are queued to
  914. BackupSubDir - Directory to backup to, relative to backup root
  915. BackupFlags - How backup should occur
  916. SourceQueueHandle - Handle that has a series of copy operations (backup hint)
  917. created, say, by pretending to do the re-install
  918. If not specified, only flags are passed
  919. Return Value:
  920. Returns error code (LastError is also set)
  921. If the function succeeds, returns NO_ERROR
  922. --*/
  923. {
  924. TCHAR BackupPath[MAX_PATH];
  925. PSP_FILE_QUEUE SourceQueue = NULL;
  926. PSP_FILE_QUEUE TargetQueue = NULL;
  927. PSP_FILE_QUEUE_NODE QueueNode = NULL;
  928. PSOURCE_MEDIA_INFO SourceMediaInfo = NULL;
  929. BOOL b = TRUE;
  930. PVOID SourceStringTable = NULL;
  931. PVOID TargetStringTable = NULL;
  932. LONG BackupRootID = -1;
  933. DWORD Err = NO_ERROR;
  934. LONG PathID = -1;
  935. SP_TARGET_ENT TargetInfo;
  936. SourceQueue = (PSP_FILE_QUEUE)SourceQueueHandle; // optional
  937. TargetQueue = (PSP_FILE_QUEUE)TargetQueueHandle;
  938. b=TRUE; // set if we can skip this routine
  939. try {
  940. TargetStringTable = TargetQueue->StringTable; // used for strings in target queue
  941. if (SourceQueue == NULL) {
  942. b = TRUE; // nothing to do
  943. } else {
  944. SourceStringTable = SourceQueue->StringTable; // used for strings in source queue
  945. b = (!SourceQueue->CopyNodeCount);
  946. }
  947. }
  948. except (EXCEPTION_EXECUTE_HANDLER)
  949. {
  950. Err = ERROR_INVALID_HANDLE;
  951. goto clean0;
  952. }
  953. // these are backup flags to be passed into the queue
  954. if (BackupFlags & SP_BKFLG_CALLBACK) {
  955. TargetQueue->Flags |= FQF_BACKUP_AWARE;
  956. }
  957. if (b) {
  958. // nothing to do
  959. goto clean0;
  960. }
  961. //
  962. // get full directory path of backup - this appears as the "dest" for any backup entries
  963. //
  964. if ( BackupSubDir == NULL ) {
  965. Err = ERROR_INVALID_HANDLE;
  966. goto clean0;
  967. }
  968. if ( pSetupGetFullBackupPath(BackupPath, BackupSubDir, MAX_PATH,NULL) == FALSE ) {
  969. Err = ERROR_INVALID_HANDLE;
  970. goto clean0;
  971. }
  972. //
  973. // Target will often use this, so we create the ID now instead of later
  974. //
  975. BackupRootID = pSetupStringTableAddString(TargetStringTable,
  976. BackupPath,
  977. STRTAB_CASE_SENSITIVE);
  978. if (BackupRootID == -1) {
  979. Err = ERROR_NOT_ENOUGH_MEMORY;
  980. goto clean0;
  981. }
  982. //
  983. // CopyQueue is split over a number of media's
  984. // we're not (currently) bothered about media
  985. // iterate through all the copy sub-queue's
  986. // and (1) add them to the target lookup table
  987. // (2) if wanted, add them into the backup queue
  988. for (SourceMediaInfo=SourceQueue->SourceMediaList; SourceMediaInfo!=NULL ; SourceMediaInfo=SourceMediaInfo->Next) {
  989. if (!SourceMediaInfo->CopyNodeCount) {
  990. continue;
  991. }
  992. MYASSERT(SourceMediaInfo->CopyQueue);
  993. for (QueueNode = SourceMediaInfo->CopyQueue; QueueNode!=NULL; QueueNode = QueueNode->Next) {
  994. // for each "Copy"
  995. // we want information about the destination path
  996. //
  997. Err = pSetupBackupGetTargetByPath(TargetQueueHandle,
  998. SourceStringTable,
  999. NULL, // precalculated string
  1000. QueueNode->TargetDirectory,
  1001. -1,
  1002. QueueNode->TargetFilename,
  1003. &PathID,
  1004. &TargetInfo);
  1005. if (Err != NO_ERROR) {
  1006. goto clean0;
  1007. }
  1008. // we now have a created (or obtained) TargetInfo, and PathID
  1009. // provide a source name for backup
  1010. TargetInfo.BackupRoot = BackupRootID;
  1011. Err = pSetupBackupCopyString(TargetStringTable, &TargetInfo.BackupSubDir, SourceStringTable, QueueNode->SourcePath);
  1012. if (Err != NO_ERROR) {
  1013. goto clean0;
  1014. }
  1015. Err = pSetupBackupCopyString(TargetStringTable, &TargetInfo.BackupFilename, SourceStringTable, QueueNode->SourceFilename);
  1016. if (Err != NO_ERROR) {
  1017. goto clean0;
  1018. }
  1019. if ((BackupFlags & SP_BKFLG_LATEBACKUP) == FALSE) {
  1020. // we need to add this item to the backup queue
  1021. Err = pSetupQueueBackupCopy(TargetQueueHandle,
  1022. // source
  1023. TargetInfo.TargetRoot,
  1024. TargetInfo.TargetSubDir,
  1025. TargetInfo.TargetFilename,
  1026. TargetInfo.BackupRoot,
  1027. TargetInfo.BackupSubDir,
  1028. TargetInfo.BackupFilename);
  1029. if (Err != NO_ERROR) {
  1030. goto clean0;
  1031. }
  1032. // flag that we've added it to the pre-copy backup sub-queue
  1033. TargetInfo.InternalFlags |= SP_TEFLG_BACKUPQUEUE;
  1034. }
  1035. // any backups should go to this specified directory
  1036. TargetInfo.InternalFlags |= SP_TEFLG_ORIGNAME;
  1037. Err = pSetupBackupSetTargetByID(TargetQueueHandle, PathID, &TargetInfo);
  1038. if (Err != NO_ERROR) {
  1039. goto clean0;
  1040. }
  1041. }
  1042. }
  1043. Err = NO_ERROR;
  1044. clean0:
  1045. SetLastError(Err);
  1046. return (Err);
  1047. }
  1048. //
  1049. // ==========================================================
  1050. //
  1051. DWORD
  1052. pSetupBackupFile(
  1053. IN HSPFILEQ QueueHandle,
  1054. IN PCTSTR TargetPath,
  1055. IN PCTSTR BackupPath,
  1056. IN LONG TargetID, OPTIONAL
  1057. IN LONG TargetRootPath,
  1058. IN LONG TargetSubDir,
  1059. IN LONG TargetFilename,
  1060. IN LONG BackupRootPath,
  1061. IN LONG BackupSubDir,
  1062. IN LONG BackupFilename,
  1063. OUT BOOL *InUseFlag
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. If BackupFilename not supplied, it is obtained/created
  1068. Will either
  1069. 1) copy a file to the backup directory, or
  1070. 2) queue that a file is backed up on reboot
  1071. The latter occurs if the file was locked.
  1072. Arguments:
  1073. HSPFILEQ - QueueHandle - specifies Queue
  1074. LONG - TargetID - if specified (not -1), use for target
  1075. LONG - TargetRootPath - used if TargetID == -1
  1076. LONG - TargetSubDir - used if TargetID == -1
  1077. LONG - TargetFilename - used if TargetID == -1
  1078. LONG - BackupRootPath - alternate root (valid if BackupFilename != -1)
  1079. LONG - BackupSubDir - alternate directory (valid if BackupFilename != -1)
  1080. LONG - BackupFilename - alternate filename
  1081. Return Value:
  1082. If the function succeeds, return value is TRUE
  1083. If the function fails, return value is FALSE
  1084. --*/
  1085. {
  1086. PSP_FILE_QUEUE Queue = NULL;
  1087. PVOID StringTable = NULL;
  1088. PVOID LookupTable = NULL;
  1089. DWORD Err = NO_ERROR;
  1090. SP_TARGET_ENT TargetInfo;
  1091. PTSTR FullTargetPath = NULL;
  1092. PTSTR FullBackupPath = NULL;
  1093. BOOL InUse = FALSE;
  1094. PTSTR TempNamePtr = NULL, DirTruncPos;
  1095. TCHAR TempPath[MAX_PATH];
  1096. TCHAR TempFilename[MAX_PATH];
  1097. TCHAR ParsedPath[MAX_PATH];
  1098. UINT OldMode;
  1099. LONG NewTargetFilename;
  1100. BOOL DoRename = FALSE;
  1101. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  1102. Queue = (PSP_FILE_QUEUE)QueueHandle;
  1103. try {
  1104. StringTable = Queue->StringTable; // used for strings in source queue
  1105. }
  1106. except (EXCEPTION_EXECUTE_HANDLER)
  1107. {
  1108. Err = ERROR_INVALID_HANDLE;
  1109. goto clean1;
  1110. }
  1111. if(TargetPath == NULL && TargetID == -1) {
  1112. if (TargetRootPath == -1 || TargetFilename == -1) {
  1113. Err = ERROR_INVALID_HANDLE;
  1114. goto clean0;
  1115. }
  1116. // complete target path
  1117. FullTargetPath = pSetupFormFullPath(
  1118. StringTable,
  1119. TargetRootPath,
  1120. TargetSubDir,
  1121. TargetFilename
  1122. );
  1123. if (!FullTargetPath) {
  1124. Err = ERROR_NOT_ENOUGH_MEMORY;
  1125. goto clean0;
  1126. }
  1127. TargetPath = FullTargetPath;
  1128. }
  1129. if (TargetID == -1) {
  1130. Err = pSetupBackupGetTargetByPath(QueueHandle,
  1131. NULL, // string table
  1132. TargetPath, // precalculated string
  1133. TargetRootPath,
  1134. TargetSubDir,
  1135. TargetFilename,
  1136. &TargetID,
  1137. &TargetInfo);
  1138. } else {
  1139. Err = pSetupBackupGetTargetByID(QueueHandle,
  1140. TargetID,
  1141. &TargetInfo);
  1142. }
  1143. if(Err != NO_ERROR) {
  1144. goto clean0;
  1145. }
  1146. //
  1147. // if we're not interested in backing up (global flag) we can skip
  1148. // but it's only safe to do so if we'd copy & then later throw the copy away on success
  1149. // note that if FQF_DEVICE_BACKUP is set, we'll always backup
  1150. //
  1151. if (((TargetInfo.InternalFlags & SP_TEFLG_RENAMEEXISTING) == 0)
  1152. && ((Queue->Flags & FQF_DEVICE_BACKUP)==0)
  1153. && ((GlobalSetupFlags & PSPGF_NO_BACKUP)!=0)) {
  1154. Err = NO_ERROR;
  1155. goto clean0;
  1156. }
  1157. //
  1158. // Figure out whether we've been asked to rename the existing file to a
  1159. // temp name in the same directory, but haven't yet done so.
  1160. //
  1161. DoRename = ((TargetInfo.InternalFlags & (SP_TEFLG_RENAMEEXISTING | SP_TEFLG_MOVED)) == SP_TEFLG_RENAMEEXISTING);
  1162. if(BackupFilename == -1) {
  1163. //
  1164. // non-specific backup
  1165. //
  1166. if((TargetInfo.InternalFlags & SP_TEFLG_SAVED) && !DoRename) {
  1167. //
  1168. // Already backed up, and we don't need to rename the existing file.
  1169. // Nothing to do.
  1170. //
  1171. Err = NO_ERROR;
  1172. goto clean0;
  1173. }
  1174. if(TargetInfo.InternalFlags & SP_TEFLG_INUSE) {
  1175. //
  1176. // Previously marked as INUSE, not allowed to change it. If we
  1177. // were asked to rename the existing file, then we need to return
  1178. // failure, otherwise, we can report success.
  1179. //
  1180. //
  1181. InUse = TRUE;
  1182. Err = DoRename ? ERROR_SHARING_VIOLATION : NO_ERROR;
  1183. goto clean0;
  1184. }
  1185. if(TargetInfo.InternalFlags & SP_TEFLG_ORIGNAME) {
  1186. //
  1187. // original name given, use that
  1188. //
  1189. BackupRootPath = TargetInfo.BackupRoot;
  1190. BackupSubDir = TargetInfo.BackupSubDir;
  1191. BackupFilename = TargetInfo.BackupFilename;
  1192. }
  1193. } else {
  1194. //
  1195. // We should never be called if the file has already been
  1196. // saved.
  1197. //
  1198. MYASSERT(!(TargetInfo.InternalFlags & SP_TEFLG_SAVED));
  1199. //
  1200. // Even if the above assert fires, we should still deal with
  1201. // the case where this occurs. Also, we should deal with the
  1202. // case where a backup was previously attempted but failed due
  1203. // to the file being in-use.
  1204. //
  1205. if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
  1206. //
  1207. // nothing to do, we shouldn't treat this as an actual error
  1208. //
  1209. Err = NO_ERROR;
  1210. goto clean0;
  1211. } else if(TargetInfo.InternalFlags & SP_TEFLG_INUSE) {
  1212. //
  1213. // force the issue of InUse
  1214. //
  1215. InUse = TRUE;
  1216. Err = ERROR_SHARING_VIOLATION;
  1217. goto clean0;
  1218. }
  1219. TargetInfo.BackupRoot = BackupRootPath;
  1220. TargetInfo.BackupSubDir = BackupSubDir;
  1221. TargetInfo.BackupFilename = BackupFilename;
  1222. TargetInfo.InternalFlags |= SP_TEFLG_ORIGNAME;
  1223. TargetInfo.InternalFlags &= ~SP_TEFLG_TEMPNAME;
  1224. }
  1225. if(TargetPath == NULL) {
  1226. //
  1227. // must have looked up using TargetID, use TargetInfo to generate TargetPath
  1228. // complete target path
  1229. //
  1230. FullTargetPath = pSetupFormFullPath(StringTable,
  1231. TargetInfo.TargetRoot,
  1232. TargetInfo.TargetSubDir,
  1233. TargetInfo.TargetFilename
  1234. );
  1235. if(!FullTargetPath) {
  1236. Err = ERROR_NOT_ENOUGH_MEMORY;
  1237. goto clean0;
  1238. }
  1239. TargetPath = FullTargetPath;
  1240. }
  1241. if(DoRename) {
  1242. //
  1243. // We'd better not already have a temp filename stored in our TargetInfo.
  1244. //
  1245. MYASSERT(TargetInfo.NewTargetFilename == -1);
  1246. //
  1247. // First, strip the filename off the path.
  1248. //
  1249. _tcscpy(TempPath, TargetPath);
  1250. TempNamePtr = (PTSTR)pSetupGetFileTitle(TempPath);
  1251. *TempNamePtr = TEXT('\0');
  1252. //
  1253. // Now get a temp filename within that directory...
  1254. //
  1255. if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
  1256. Err = GetLastError();
  1257. goto clean0;
  1258. }
  1259. //
  1260. // ...and store this path's string ID in our TargetInfo
  1261. //
  1262. NewTargetFilename = pSetupStringTableAddString(StringTable,
  1263. TempFilename,
  1264. STRTAB_CASE_SENSITIVE
  1265. );
  1266. if(NewTargetFilename == -1) {
  1267. Err = ERROR_NOT_ENOUGH_MEMORY;
  1268. goto clean0;
  1269. }
  1270. }
  1271. if(!(TargetInfo.InternalFlags & (SP_TEFLG_ORIGNAME | SP_TEFLG_TEMPNAME))) {
  1272. //
  1273. // If we don't yet have a name to use in backing up this file, then
  1274. // generate one now. If we are doing a rename, we can use that name.
  1275. //
  1276. if(DoRename) {
  1277. //
  1278. // Make sure that all flags agree on the fact that we need to back
  1279. // up this file.
  1280. //
  1281. MYASSERT(!(TargetInfo.InternalFlags & SP_TEFLG_SAVED));
  1282. //
  1283. // Temp filename was stored in TempFilename buffer above.
  1284. //
  1285. TempNamePtr = (PTSTR)pSetupGetFileTitle(TempFilename);
  1286. BackupFilename = pSetupStringTableAddString(StringTable, TempNamePtr, STRTAB_CASE_SENSITIVE);
  1287. if(BackupFilename == -1) {
  1288. Err = ERROR_NOT_ENOUGH_MEMORY;
  1289. goto clean0;
  1290. }
  1291. DirTruncPos = CharPrev(TempFilename, TempNamePtr);
  1292. //
  1293. // (We know pSetupGetFileTitle will never return a pointer to a path
  1294. // separator character, so the following check is valid.)
  1295. //
  1296. if(*DirTruncPos == TEXT('\\')) {
  1297. //
  1298. // If this is in a root directory (e.g., "A:\"), then we don't want to strip off
  1299. // the trailing backslash.
  1300. //
  1301. if(((DirTruncPos - TempFilename) != 2) || (*CharNext(TempFilename) != TEXT(':'))) {
  1302. TempNamePtr = DirTruncPos;
  1303. }
  1304. }
  1305. lstrcpyn(TempPath, TempFilename, (int)(TempNamePtr - TempFilename) + 1);
  1306. BackupRootPath = pSetupStringTableAddString(StringTable, TempPath, STRTAB_CASE_SENSITIVE);
  1307. if(BackupRootPath == -1) {
  1308. Err = ERROR_NOT_ENOUGH_MEMORY;
  1309. goto clean0;
  1310. }
  1311. } else {
  1312. //
  1313. // specify "NULL" as the sub-directory, since all we want is a temporary location
  1314. //
  1315. if(pSetupGetFullBackupPath(TempPath, NULL, MAX_PATH,NULL) == FALSE ) {
  1316. Err = ERROR_INVALID_HANDLE;
  1317. goto clean0;
  1318. }
  1319. _tcscpy(TempFilename,TempPath);
  1320. //
  1321. // Note: In the code below, we employ a "trick" to get the
  1322. // pSetupMakeSurePathExists API to make sure that a directory
  1323. // exists. Since we don't yet know the filename (we need to call
  1324. // GetTempFileName against an existing directory to find that out),
  1325. // we just use a dummy placeholder filename ("OLD") so that it can
  1326. // be discarded by the pSetupMakeSurePathExists API.
  1327. //
  1328. if(pSetupConcatenatePaths(TempFilename, TEXT("OLD"), MAX_PATH, NULL) == FALSE ) {
  1329. Err = GetLastError();
  1330. goto clean0;
  1331. }
  1332. pSetupMakeSurePathExists(TempFilename);
  1333. if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
  1334. Err = GetLastError();
  1335. goto clean0;
  1336. }
  1337. TempNamePtr = TempFilename + _tcslen(TempPath) + 1 /* 1 to skip past \ */;
  1338. BackupRootPath = pSetupStringTableAddString( StringTable, TempPath, STRTAB_CASE_SENSITIVE );
  1339. if(BackupRootPath == -1) {
  1340. Err = ERROR_NOT_ENOUGH_MEMORY;
  1341. goto clean0;
  1342. }
  1343. BackupFilename = pSetupStringTableAddString( StringTable, TempNamePtr, STRTAB_CASE_SENSITIVE );
  1344. if(BackupFilename == -1) {
  1345. Err = ERROR_NOT_ENOUGH_MEMORY;
  1346. goto clean0;
  1347. }
  1348. }
  1349. BackupPath = TempFilename;
  1350. TargetInfo.BackupRoot = BackupRootPath;
  1351. TargetInfo.BackupSubDir = BackupSubDir = -1;
  1352. TargetInfo.BackupFilename = BackupFilename;
  1353. TargetInfo.InternalFlags |= SP_TEFLG_TEMPNAME;
  1354. }
  1355. if(BackupPath == NULL) {
  1356. //
  1357. // make a complete path from this source
  1358. //
  1359. FullBackupPath = pSetupFormFullPath(StringTable,
  1360. BackupRootPath,
  1361. BackupSubDir,
  1362. BackupFilename
  1363. );
  1364. if (!FullBackupPath) {
  1365. Err = ERROR_NOT_ENOUGH_MEMORY;
  1366. goto clean0;
  1367. }
  1368. BackupPath = FullBackupPath;
  1369. }
  1370. //
  1371. // If we need to make a copy of the existing file, do so now.
  1372. //
  1373. if(!DoRename || (TargetInfo.InternalFlags & SP_TEFLG_ORIGNAME)) {
  1374. SetFileAttributes(BackupPath, FILE_ATTRIBUTE_NORMAL);
  1375. pSetupMakeSurePathExists(BackupPath);
  1376. Err = CopyFile(TargetPath, BackupPath, FALSE) ? NO_ERROR : GetLastError();
  1377. if(Err == NO_ERROR) {
  1378. TargetInfo.InternalFlags |= SP_TEFLG_SAVED;
  1379. } else {
  1380. //
  1381. // Delete placeholder file created by GetTempFileName.
  1382. //
  1383. SetFileAttributes(BackupPath, FILE_ATTRIBUTE_NORMAL);
  1384. DeleteFile(BackupPath);
  1385. if(Err == ERROR_SHARING_VIOLATION) {
  1386. //
  1387. // Unless we were also going to attempt a rename, don't
  1388. // consider sharing violations to be errors.
  1389. //
  1390. InUse = TRUE;
  1391. TargetInfo.InternalFlags |= SP_TEFLG_INUSE;
  1392. if(!DoRename) {
  1393. Err = NO_ERROR;
  1394. }
  1395. }
  1396. }
  1397. }
  1398. //
  1399. // OK, now rename the existing file, if necessary.
  1400. //
  1401. if(DoRename && (Err == NO_ERROR)) {
  1402. if(DoMove(TargetPath, TempFilename)) {
  1403. TargetInfo.InternalFlags |= SP_TEFLG_MOVED;
  1404. TargetInfo.NewTargetFilename = NewTargetFilename;
  1405. //
  1406. // Post a delayed deletion for this temp filename so it'll get
  1407. // cleaned up after reboot.
  1408. //
  1409. if(!PostDelayedMove(Queue, TempFilename, NULL, -1, FALSE)) {
  1410. //
  1411. // Don't abort just because we couldn't schedule a delayed
  1412. // delete. If this fails, the only bad thing that will happen
  1413. // is a turd will get left over after reboot.
  1414. //
  1415. // We should log an event about this, however.
  1416. //
  1417. Err = GetLastError();
  1418. WriteLogEntry(Queue->LogContext,
  1419. SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
  1420. MSG_LOG_RENAME_EXISTING_DELAYED_DELETE_FAILED,
  1421. NULL,
  1422. TargetPath,
  1423. TempFilename
  1424. );
  1425. WriteLogError(Queue->LogContext,
  1426. SETUP_LOG_WARNING,
  1427. Err
  1428. );
  1429. Err = NO_ERROR;
  1430. }
  1431. } else {
  1432. Err = GetLastError();
  1433. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  1434. DeleteFile(TempFilename);
  1435. if(Err == ERROR_SHARING_VIOLATION) {
  1436. InUse = TRUE;
  1437. TargetInfo.InternalFlags |= SP_TEFLG_INUSE;
  1438. }
  1439. }
  1440. }
  1441. //
  1442. // update internal info (this call should never fail)
  1443. //
  1444. pSetupBackupSetTargetByID(QueueHandle,
  1445. TargetID,
  1446. &TargetInfo
  1447. );
  1448. clean0:
  1449. if (FullTargetPath != NULL) {
  1450. MyFree(FullTargetPath);
  1451. }
  1452. if (FullBackupPath != NULL) {
  1453. MyFree(FullBackupPath);
  1454. }
  1455. if (Err != NO_ERROR) {
  1456. //
  1457. // note the fact that at least one backup error has occurred
  1458. //
  1459. Queue->Flags |= FQF_BACKUP_INCOMPLETE;
  1460. }
  1461. clean1:
  1462. SetErrorMode(OldMode);
  1463. SetLastError(Err);
  1464. if(InUseFlag) {
  1465. *InUseFlag = InUse;
  1466. }
  1467. return Err;
  1468. }
  1469. //
  1470. // ==========================================================
  1471. //
  1472. VOID
  1473. pSetupDeleteBackup(
  1474. IN PCTSTR BackupInstance
  1475. )
  1476. /*++
  1477. Routine Description:
  1478. This function will delete an entire backup instance. This entails deleting
  1479. the relative BackupInstance out of the registry Reinstall key as well
  1480. Arguments:
  1481. BackupInstance - Instance Id of the backup
  1482. Return Value:
  1483. If the function succeeds, return value is TRUE
  1484. If the function fails, return value is FALSE
  1485. --*/
  1486. {
  1487. TCHAR Buffer[MAX_PATH];
  1488. HKEY hKeyReinstall = INVALID_HANDLE_VALUE;
  1489. if (BackupInstance == NULL) {
  1490. return;
  1491. }
  1492. //
  1493. // Delete this instance from the Reinstall key.
  1494. //
  1495. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1496. pszReinstallPath,
  1497. 0,
  1498. KEY_ALL_ACCESS,
  1499. &hKeyReinstall
  1500. ) == ERROR_SUCCESS) {
  1501. RegDeleteKey(hKeyReinstall, BackupInstance);
  1502. RegCloseKey(hKeyReinstall);
  1503. }
  1504. //
  1505. // Now delete the entire backup directory.
  1506. //
  1507. if (pSetupGetFullBackupPath(Buffer, BackupInstance, MAX_PATH, NULL)) {
  1508. pRemoveDirectory(Buffer);
  1509. }
  1510. }
  1511. //
  1512. // ==========================================================
  1513. //
  1514. DWORD
  1515. pSetupGetCurrentlyInstalledDriverNode(
  1516. IN HDEVINFO DeviceInfoSet,
  1517. IN OUT PSP_DEVINFO_DATA DeviceInfoData
  1518. )
  1519. /*++
  1520. Routine Description:
  1521. Get driver node that relates to current INF file of device
  1522. Arguments:
  1523. DeviceInfoSet
  1524. DeviceInfoData
  1525. Return Value:
  1526. Error Status
  1527. --*/
  1528. {
  1529. DWORD Err;
  1530. SP_DEVINSTALL_PARAMS DeviceInstallParams;
  1531. SP_DRVINFO_DATA DriverInfoData;
  1532. ZeroMemory(&DeviceInstallParams, sizeof(DeviceInstallParams));
  1533. ZeroMemory(&DriverInfoData, sizeof(DriverInfoData));
  1534. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  1535. DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
  1536. if(!SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams)) {
  1537. return GetLastError();
  1538. }
  1539. //
  1540. // Set the flags that tell SetupDiBuildDriverInfoList to just put the currently installed
  1541. // driver node in the list, and that it should allow excluded drivers.
  1542. //
  1543. DeviceInstallParams.FlagsEx |= (DI_FLAGSEX_INSTALLEDDRIVER | DI_FLAGSEX_ALLOWEXCLUDEDDRVS);
  1544. if(!SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams)) {
  1545. Err = GetLastError();
  1546. goto clean0;
  1547. }
  1548. //
  1549. // Now build a class driver list that just contains the currently installed driver.
  1550. //
  1551. if(!SetupDiBuildDriverInfoList(DeviceInfoSet, DeviceInfoData, SPDIT_CLASSDRIVER)) {
  1552. Err = GetLastError();
  1553. goto clean0;
  1554. }
  1555. //
  1556. // The only driver in the list should be the currently installed driver, if there
  1557. // is a currently installed driver.
  1558. //
  1559. if (!SetupDiEnumDriverInfo(DeviceInfoSet, DeviceInfoData, SPDIT_CLASSDRIVER,
  1560. 0, &DriverInfoData)) {
  1561. Err = GetLastError();
  1562. goto clean0;
  1563. }
  1564. //
  1565. // Make the currently installed driver the selected driver.
  1566. //
  1567. if(!SetupDiSetSelectedDriver(DeviceInfoSet, DeviceInfoData, &DriverInfoData)) {
  1568. Err = GetLastError();
  1569. goto clean0;
  1570. }
  1571. //
  1572. // At this point, we've successfully selected the currently installed driver for the specified
  1573. // device information element. We're done!
  1574. //
  1575. Err = NO_ERROR;
  1576. clean0:
  1577. SetLastError(Err);
  1578. return Err;
  1579. }
  1580. //
  1581. // ==========================================================
  1582. //
  1583. DWORD
  1584. pSetupGetBackupQueue(
  1585. IN PCTSTR DeviceID,
  1586. IN OUT HSPFILEQ FileQueue,
  1587. IN DWORD BackupFlags
  1588. )
  1589. /*++
  1590. Routine Description:
  1591. Creates a backup Queue for current device (DeviceID)
  1592. Also makes sure that the INF file is backed up
  1593. Arguments:
  1594. DeviceID String ID of device
  1595. FileQueue Backup queue is filled with files that need copying
  1596. BackupFlags Various flags
  1597. Return Value:
  1598. Error Status
  1599. --*/
  1600. {
  1601. //
  1602. // we want to obtain a copy/move list of device associated with DeviceID
  1603. //
  1604. //
  1605. PSP_FILE_QUEUE FileQ = (PSP_FILE_QUEUE)FileQueue;
  1606. HDEVINFO TempInfoSet = (HDEVINFO)INVALID_HANDLE_VALUE;
  1607. HSPFILEQ TempQueueHandle = (HSPFILEQ)INVALID_HANDLE_VALUE;
  1608. SP_DEVINFO_DATA TempInfoData;
  1609. SP_DEVINSTALL_PARAMS TempParams;
  1610. TCHAR SubDir[MAX_PATH];
  1611. LONG Instance;
  1612. PDEVINFO_ELEM DevInfoElem = NULL;
  1613. PTSTR szInfFileName = NULL;
  1614. PTSTR szInfFileNameExt = NULL;
  1615. PTSTR BackupPathExt = NULL;
  1616. TCHAR BackupInstance[MAX_PATH];
  1617. TCHAR BackupPath[MAX_PATH];
  1618. TCHAR ReinstallString[MAX_PATH];
  1619. TCHAR OemOrigName[MAX_PATH];
  1620. TCHAR CatBackupPath[MAX_PATH];
  1621. TCHAR CatSourcePath[MAX_PATH];
  1622. DWORD Err;
  1623. PDEVICE_INFO_SET pDeviceInfoSet = NULL;
  1624. int InstanceId;
  1625. DWORD BackupInfID = -1;
  1626. DWORD BackupInstanceID = -1;
  1627. PSP_INF_INFORMATION pInfInformation = NULL;
  1628. DWORD InfInformationSize;
  1629. SP_ORIGINAL_FILE_INFO InfOriginalFileInformation;
  1630. BOOL success;
  1631. PSETUP_LOG_CONTEXT SavedLogContext = NULL;
  1632. PSETUP_LOG_CONTEXT LocalLogContext = NULL;
  1633. BOOL ChangedThreadLogContext = FALSE;
  1634. //
  1635. // if backup information exists, abort (no flags will be set)
  1636. //
  1637. if(FileQ->BackupInfID != -1) {
  1638. return ERROR_ALREADY_EXISTS;
  1639. }
  1640. //
  1641. // detach any backup related logging from current log section
  1642. // putting it into it's own section
  1643. // this stops confusion when debugging (v)verbose logs
  1644. // and we're going down this path
  1645. //
  1646. CreateLogContext(NULL,FALSE,&LocalLogContext);
  1647. if(LocalLogContext) {
  1648. DWORD LogTag = AllocLogInfoSlot(LocalLogContext,TRUE);
  1649. if(LogTag) {
  1650. WriteLogEntry(LocalLogContext,
  1651. LogTag,
  1652. MSG_LOG_DRIVERBACKUP,
  1653. NULL,
  1654. DeviceID
  1655. );
  1656. }
  1657. }
  1658. ChangedThreadLogContext = SetThreadLogContext(LocalLogContext,&SavedLogContext);
  1659. CatBackupPath[0] = 0; // by default, don't bother with a catalog
  1660. CatSourcePath[0] = 0;
  1661. // pretend we're installing old INF
  1662. // this gives us a list of files we need
  1663. TempInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
  1664. if ( TempInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE ) {
  1665. Err = GetLastError();
  1666. goto clean0;
  1667. }
  1668. if(!(pDeviceInfoSet = AccessDeviceInfoSet(TempInfoSet))) {
  1669. Err = ERROR_INVALID_HANDLE;
  1670. goto clean0;
  1671. }
  1672. //
  1673. // make sure info-set has our local context
  1674. //
  1675. InheritLogContext(LocalLogContext,&pDeviceInfoSet->InstallParamBlock.LogContext);
  1676. //
  1677. // Open the driver info, related to DeviceID I was given
  1678. //
  1679. TempInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1680. if ( SetupDiOpenDeviceInfo(TempInfoSet ,DeviceID, NULL, 0, &TempInfoData) == FALSE ) {
  1681. Err = GetLastError();
  1682. goto clean0;
  1683. }
  1684. //
  1685. // make sure the temporary element has our backup logging context
  1686. //
  1687. DevInfoElem = FindAssociatedDevInfoElem(TempInfoSet,
  1688. &TempInfoData,
  1689. NULL);
  1690. MYASSERT(DevInfoElem);
  1691. if(DevInfoElem) {
  1692. InheritLogContext(LocalLogContext,&DevInfoElem->InstallParamBlock.LogContext);
  1693. }
  1694. //
  1695. // Get the currently-installed driver node selected for this element.
  1696. //
  1697. if ( pSetupGetCurrentlyInstalledDriverNode(TempInfoSet, &TempInfoData) != NO_ERROR ) {
  1698. Err = GetLastError();
  1699. goto clean0;
  1700. }
  1701. //
  1702. // Now queue all files to be copied by this driver node into our own file queue.
  1703. // it'll inherit the backup logging context
  1704. //
  1705. TempQueueHandle = SetupOpenFileQueue();
  1706. if ( TempQueueHandle == (HSPFILEQ)INVALID_HANDLE_VALUE ) {
  1707. //
  1708. // SetupOpenFileQueue modified to return error
  1709. //
  1710. Err = GetLastError();
  1711. goto clean0;
  1712. }
  1713. TempParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
  1714. if ( !SetupDiGetDeviceInstallParams(TempInfoSet, &TempInfoData, &TempParams) ) {
  1715. Err = GetLastError();
  1716. goto clean0;
  1717. }
  1718. TempParams.FileQueue = TempQueueHandle;
  1719. TempParams.Flags |= DI_NOVCP;
  1720. if ( !SetupDiSetDeviceInstallParams(TempInfoSet, &TempInfoData, &TempParams) ) {
  1721. Err = GetLastError();
  1722. goto clean0;
  1723. }
  1724. if ( !SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, TempInfoSet, &TempInfoData) ) {
  1725. Err = GetLastError();
  1726. goto clean0;
  1727. }
  1728. //
  1729. // We want this backup to be in a unique directory. To do this we just do
  1730. // the standard instance number trick where we enumerate from 0000 to 9999
  1731. // and choose the first number where there isn't a backup directory with
  1732. // that name already created.
  1733. //
  1734. for (InstanceId=0; InstanceId<=9999; InstanceId++) {
  1735. wsprintf(SubDir, TEXT("\\%04d\\%s"), (LONG) InstanceId, (PCTSTR) SP_BACKUP_DRIVERFILES );
  1736. if ( pSetupGetFullBackupPath(BackupPath, SubDir, MAX_PATH, NULL) == FALSE ) {
  1737. Err = ERROR_INVALID_HANDLE;
  1738. goto clean0;
  1739. }
  1740. //
  1741. // If this backup path does not exist then we have a valid directory.
  1742. //
  1743. if (!FileExists(BackupPath, NULL)) {
  1744. break;
  1745. }
  1746. }
  1747. if (InstanceId <= 9999) {
  1748. //
  1749. // Add string indicating Backup InstanceID to file Queue
  1750. // for later retrieval
  1751. //
  1752. wsprintf(BackupInstance, TEXT("%04d"), (LONG) InstanceId);
  1753. BackupInstanceID = pSetupStringTableAddString(FileQ->StringTable,
  1754. BackupInstance,
  1755. STRTAB_CASE_SENSITIVE);
  1756. if (BackupInstanceID == -1) {
  1757. Err = ERROR_NOT_ENOUGH_MEMORY;
  1758. goto clean0;
  1759. }
  1760. } else {
  1761. //
  1762. // If we don't have any free backup directories then we will fail. There
  1763. // should never be this many drivers backed up so this shouldn't be a
  1764. // problem.
  1765. //
  1766. Err = ERROR_NOT_ENOUGH_MEMORY;
  1767. goto clean0;
  1768. }
  1769. //
  1770. // get the path of the INF file, we will need to back it up
  1771. //
  1772. if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
  1773. &TempInfoData,
  1774. NULL))) {
  1775. Err = ERROR_NOT_ENOUGH_MEMORY;
  1776. goto clean0;
  1777. }
  1778. szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
  1779. DevInfoElem->SelectedDriver->InfFileName
  1780. );
  1781. if (szInfFileName == NULL) {
  1782. Err = ERROR_NOT_ENOUGH_MEMORY;
  1783. goto clean0;
  1784. }
  1785. //
  1786. // we want to get the "real" name of the INF - we may have a precompiled inf
  1787. //
  1788. ZeroMemory(&InfOriginalFileInformation, sizeof(InfOriginalFileInformation));
  1789. //
  1790. // if nothing else, use same name as is in the INF directory
  1791. //
  1792. lstrcpy(OemOrigName,pSetupGetFileTitle(szInfFileName));
  1793. //
  1794. // but use the original name if available
  1795. //
  1796. InfInformationSize = 8192; // I'd rather have this too big and succeed first time, than read the INF twice
  1797. pInfInformation = (PSP_INF_INFORMATION)MyMalloc(InfInformationSize);
  1798. if (pInfInformation != NULL) {
  1799. success = SetupGetInfInformation(szInfFileName,INFINFO_INF_NAME_IS_ABSOLUTE,pInfInformation,InfInformationSize,&InfInformationSize);
  1800. if (!success && GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
  1801. PVOID newbuff = MyRealloc(pInfInformation,InfInformationSize);
  1802. if (!newbuff) {
  1803. MyFree(pInfInformation);
  1804. pInfInformation = NULL;
  1805. } else {
  1806. pInfInformation = (PSP_INF_INFORMATION)newbuff;
  1807. success = SetupGetInfInformation(szInfFileName,INFINFO_INF_NAME_IS_ABSOLUTE,pInfInformation,InfInformationSize,&InfInformationSize);
  1808. }
  1809. }
  1810. if (success) {
  1811. InfOriginalFileInformation.cbSize = sizeof(InfOriginalFileInformation);
  1812. if (SetupQueryInfOriginalFileInformation(pInfInformation,0,NULL,&InfOriginalFileInformation)) {
  1813. if (InfOriginalFileInformation.OriginalInfName[0]) {
  1814. //
  1815. // we have a "real" inf name
  1816. //
  1817. lstrcpy(OemOrigName,pSetupGetFileTitle(InfOriginalFileInformation.OriginalInfName));
  1818. } else {
  1819. MYASSERT(InfOriginalFileInformation.OriginalInfName[0]);
  1820. }
  1821. //
  1822. // Don't bother finding out about the INF's catalog if we're in
  1823. // "minimal embedded" mode...
  1824. //
  1825. if(!(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED)) {
  1826. if (InfOriginalFileInformation.OriginalCatalogName[0]) {
  1827. TCHAR CurrentCatName[MAX_PATH];
  1828. //
  1829. // given that the file is ....\OEMx.INF the catalog is "OEMx.CAT"
  1830. // we key off OemOrigName (eg mydisk.inf )
  1831. // and we won't bother copying the catalog if we can't verify the inf
  1832. //
  1833. lstrcpy(CurrentCatName,pSetupGetFileTitle(szInfFileName));
  1834. lstrcpy(_tcsrchr(CurrentCatName, TEXT('.')), pszCatSuffix);
  1835. //
  1836. // we have a catalog name
  1837. // now consider making a copy of the cached catalog
  1838. // into our backup we get out CatProblem and
  1839. // szCatFileName
  1840. //
  1841. // if all seems ok, copy file from szCatFileName to
  1842. // backupdir\OriginalCatalogName
  1843. //
  1844. Err = _VerifyFile(
  1845. FileQ->LogContext,
  1846. &(FileQ->VerifyContext),
  1847. CurrentCatName, // eg "OEMx.CAT"
  1848. NULL,0, // we're not verifying against another catalog image
  1849. OemOrigName, // eg "mydisk.inf"
  1850. szInfFileName, // eg "....\OEMx.INF"
  1851. NULL, // return: problem info
  1852. NULL, // return: problem file
  1853. FALSE, // has to be FALSE because we're getting full path
  1854. ((PSP_FILE_QUEUE)TempQueueHandle)->ValidationPlatform, // alt platform info
  1855. VERIFY_FILE_IGNORE_SELFSIGNED | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK,
  1856. CatSourcePath, // return: catalog file, full path
  1857. NULL, // return: number of catalogs considered
  1858. NULL,
  1859. NULL,
  1860. NULL
  1861. );
  1862. if(Err != NO_ERROR) {
  1863. //
  1864. // There may be an Authenticode-signed catalog, so
  1865. // we'll look for that, too.
  1866. //
  1867. Err = _VerifyFile(
  1868. FileQ->LogContext,
  1869. &(FileQ->VerifyContext),
  1870. CurrentCatName, // eg "OEMx.CAT"
  1871. NULL,0, // we're not verifying against another catalog image
  1872. OemOrigName, // eg "mydisk.inf"
  1873. szInfFileName, // eg "....\OEMx.INF"
  1874. NULL, // return: problem info
  1875. NULL, // return: problem file
  1876. FALSE, // has to be FALSE because we're getting full path
  1877. ((PSP_FILE_QUEUE)TempQueueHandle)->ValidationPlatform, // alt platform info
  1878. (VERIFY_FILE_IGNORE_SELFSIGNED
  1879. | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
  1880. | VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
  1881. CatSourcePath, // return: catalog file, full path
  1882. NULL, // return: number of catalogs considered
  1883. NULL,
  1884. NULL,
  1885. NULL
  1886. );
  1887. //
  1888. // For the purposes of this routine, we don't care
  1889. // whether or not the publisher is in the
  1890. // TrustedPublisher store.
  1891. //
  1892. if((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
  1893. (Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
  1894. Err = NO_ERROR;
  1895. }
  1896. }
  1897. if(Err == NO_ERROR && CatSourcePath[0]) {
  1898. //
  1899. // we have a catalog file of interest to copy
  1900. //
  1901. lstrcpy(CatBackupPath,BackupPath);
  1902. if (!pSetupConcatenatePaths(CatBackupPath, InfOriginalFileInformation.OriginalCatalogName, MAX_PATH, NULL)) {
  1903. //
  1904. // non-fatal
  1905. //
  1906. CatSourcePath[0]=0;
  1907. CatBackupPath[0]=0;
  1908. }
  1909. }
  1910. }
  1911. }
  1912. }
  1913. }
  1914. if (pInfInformation != NULL) {
  1915. MyFree(pInfInformation);
  1916. pInfInformation = NULL;
  1917. }
  1918. }
  1919. if ( pSetupConcatenatePaths(BackupPath, OemOrigName, MAX_PATH, NULL) == FALSE ) {
  1920. Err = ERROR_INVALID_HANDLE;
  1921. goto clean0;
  1922. }
  1923. pSetupMakeSurePathExists(BackupPath);
  1924. SetFileAttributes(BackupPath,FILE_ATTRIBUTE_NORMAL);
  1925. Err = CopyFile(szInfFileName, BackupPath ,FALSE) ? NO_ERROR : GetLastError();
  1926. if (Err != NO_ERROR) {
  1927. goto clean0;
  1928. }
  1929. if(CatSourcePath[0] && CatBackupPath[0]) {
  1930. //
  1931. // if we copied Inf file, try to copy catalog file
  1932. // if we don't succeed, don't consider this a fatal error
  1933. //
  1934. SetFileAttributes(CatBackupPath,FILE_ATTRIBUTE_NORMAL);
  1935. CopyFile(CatSourcePath, CatBackupPath ,FALSE);
  1936. }
  1937. //
  1938. // Add string indicating Backup INF location to file Queue
  1939. // for later retrieval
  1940. //
  1941. BackupInfID = pSetupStringTableAddString(FileQ->StringTable,
  1942. BackupPath,
  1943. STRTAB_CASE_SENSITIVE);
  1944. if (BackupInfID == -1) {
  1945. Err = ERROR_NOT_ENOUGH_MEMORY;
  1946. goto clean0;
  1947. }
  1948. //
  1949. // Save the full inf backup path since we need to put it in the registry
  1950. // below.
  1951. //
  1952. lstrcpy(ReinstallString, BackupPath);
  1953. //
  1954. // Also backup the PNF file
  1955. //
  1956. // WARNING: We reuse the szInfFileName and BackupPath variables at this point
  1957. // so if you add code that needs them then add it above.
  1958. //
  1959. szInfFileNameExt = _tcsrchr(szInfFileName,TEXT('.'));
  1960. MYASSERT(szInfFileNameExt);
  1961. BackupPathExt = _tcsrchr(BackupPath,TEXT('.'));
  1962. MYASSERT(BackupPathExt);
  1963. if (szInfFileNameExt && BackupPathExt) {
  1964. lstrcpy(szInfFileNameExt,pszPnfSuffix);
  1965. lstrcpy(BackupPathExt,pszPnfSuffix);
  1966. SetFileAttributes(BackupPath,FILE_ATTRIBUTE_NORMAL);
  1967. CopyFile(szInfFileName, BackupPath, FALSE);
  1968. }
  1969. //
  1970. // add items we may need to backup
  1971. // (ie the copy queue of TempQueueHandle is converted to a backup queue of FileQueue)
  1972. //
  1973. if ( pSetupBackupAppendFiles(FileQueue, SubDir, BackupFlags, TempQueueHandle) != NO_ERROR ) {
  1974. Err = GetLastError();
  1975. goto clean0;
  1976. }
  1977. //
  1978. // Create the Reinstall registry key that points to the backup directory we
  1979. // just made.
  1980. //
  1981. if (pSetupBackupGetReinstallKeyStrings(FileQ,
  1982. TempInfoSet,
  1983. &TempInfoData,
  1984. DeviceID
  1985. ) != NO_ERROR) {
  1986. Err = GetLastError();
  1987. goto clean0;
  1988. }
  1989. Err = NO_ERROR;
  1990. //
  1991. // update BackupInfID so that INF name can be queried later. We will set
  1992. // these values at the very end since the fact that FileQ->BackupInfID is
  1993. // not -1 means the backup initialization has been successful.
  1994. //
  1995. FileQ->BackupInfID = BackupInfID;
  1996. FileQ->BackupInstanceID = BackupInstanceID;
  1997. //
  1998. // Set the FQF_DEVICE_BACKUP flag so that we know this is a device install
  1999. // backup.
  2000. //
  2001. FileQ->Flags |= FQF_DEVICE_BACKUP;
  2002. clean0:
  2003. //
  2004. // If we encountered an error during our backup initialization then we want
  2005. // to clean out the backup directory and Reinstall subkey.
  2006. //
  2007. if ((Err != NO_ERROR) &&
  2008. (BackupInstanceID != -1)) {
  2009. if (pSetupStringTableStringFromIdEx(FileQ->StringTable,
  2010. BackupInstanceID,
  2011. BackupInstance,
  2012. NULL)) {
  2013. pSetupDeleteBackup(BackupInstance);
  2014. }
  2015. }
  2016. //
  2017. // delete temporary structures used
  2018. //
  2019. if (pDeviceInfoSet != NULL ) {
  2020. UnlockDeviceInfoSet(pDeviceInfoSet);
  2021. }
  2022. if ( TempInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE ) {
  2023. SetupDiDestroyDeviceInfoList(TempInfoSet);
  2024. }
  2025. if ( TempQueueHandle != (HSPFILEQ)INVALID_HANDLE_VALUE ) {
  2026. SetupCloseFileQueue(TempQueueHandle);
  2027. }
  2028. if(ChangedThreadLogContext) {
  2029. //
  2030. // restore thread log context if we changed (cleared) it
  2031. //
  2032. SetThreadLogContext(SavedLogContext,NULL);
  2033. }
  2034. DeleteLogContext(LocalLogContext);
  2035. SetLastError(Err);
  2036. return Err;
  2037. }
  2038. //
  2039. // ==========================================================
  2040. //
  2041. DWORD
  2042. pSetupCompleteBackup(
  2043. IN OUT HSPFILEQ FileQueue
  2044. )
  2045. /*++
  2046. Routine Description:
  2047. This routine is called after we have successfully finished installing a
  2048. device. At this point the new backup that we have created is valid and
  2049. so any old backups that this device was using need to be removed.
  2050. To do this the code will enumerate all of the backup instances under the
  2051. Reinstall key and scan their DeviceInstanceIds multi-sz value. If it finds
  2052. this DeviceInstanceId in the list then it will remove it. If the list is
  2053. empty after this removal then the entire backup instance and its
  2054. cooresponding backup directory will be deleted.
  2055. Arguments:
  2056. FileQueue Backup queue is filled with files that need copying
  2057. Return Value:
  2058. Error Status
  2059. --*/
  2060. {
  2061. PSP_FILE_QUEUE BackupFileQueue = (PSP_FILE_QUEUE)FileQueue;
  2062. HKEY hKeyReinstall;
  2063. HKEY hKeyReinstallInstance;
  2064. DWORD Index;
  2065. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  2066. TCHAR ReinstallInstance[MAX_PATH];
  2067. FILETIME ftLastWriteTime;
  2068. DWORD cbData, cbInstanceSize;
  2069. BOOL bDeleteBackupInstance;
  2070. DWORD Err = NO_ERROR;
  2071. PTSTR DeviceInstanceIdsList, p;
  2072. try {
  2073. //
  2074. // If we don't have a BackupInfID then the backup failed, so don't bother
  2075. // cleaning out the old backup information or creating the new Reinstall
  2076. // instance key.
  2077. //
  2078. if (BackupFileQueue->BackupInfID == -1) {
  2079. Err = ERROR_NO_BACKUP;
  2080. goto clean0;
  2081. }
  2082. //
  2083. // Get the Device Instance Id from the backup queue. This value must be
  2084. // cleaned out from all other Reinstall Instance keys.
  2085. //
  2086. cbData = MAX_PATH;
  2087. if (!pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  2088. BackupFileQueue->BackupDeviceInstanceID,
  2089. DeviceInstanceId,
  2090. &cbData)) {
  2091. if (cbData == 0) {
  2092. Err = ERROR_NO_BACKUP;
  2093. } else {
  2094. Err = ERROR_INSUFFICIENT_BUFFER;
  2095. }
  2096. goto clean0;
  2097. }
  2098. //
  2099. // Open the Reinstall key so we can enumerate all of the instance subkeys.
  2100. //
  2101. Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  2102. pszReinstallPath,
  2103. 0,
  2104. KEY_ALL_ACCESS,
  2105. &hKeyReinstall
  2106. );
  2107. if (Err == ERROR_SUCCESS) {
  2108. cbInstanceSize = sizeof(ReinstallInstance) / sizeof(TCHAR);
  2109. Index = 0;
  2110. while (RegEnumKeyEx(hKeyReinstall,
  2111. Index++,
  2112. ReinstallInstance,
  2113. &cbInstanceSize,
  2114. NULL,
  2115. NULL,
  2116. NULL,
  2117. &ftLastWriteTime
  2118. ) == ERROR_SUCCESS) {
  2119. //
  2120. // Assume that we don't need to delete this backup instance
  2121. //
  2122. bDeleteBackupInstance = FALSE;
  2123. Err = RegOpenKeyEx(hKeyReinstall,
  2124. ReinstallInstance,
  2125. 0,
  2126. KEY_ALL_ACCESS,
  2127. &hKeyReinstallInstance
  2128. );
  2129. if (Err == ERROR_SUCCESS) {
  2130. cbData = 0;
  2131. if ((RegQueryValueEx(hKeyReinstallInstance,
  2132. pszReinstallDeviceInstanceIds,
  2133. NULL,
  2134. NULL,
  2135. NULL,
  2136. &cbData
  2137. ) == ERROR_SUCCESS) &&
  2138. (cbData)) {
  2139. DeviceInstanceIdsList = MyMalloc(cbData + sizeof(TCHAR));
  2140. if (DeviceInstanceIdsList) {
  2141. if (RegQueryValueEx(hKeyReinstallInstance,
  2142. pszReinstallDeviceInstanceIds,
  2143. NULL,
  2144. NULL,
  2145. (LPBYTE)DeviceInstanceIdsList,
  2146. &cbData) == ERROR_SUCCESS) {
  2147. //
  2148. // Walk the list of DeviceInstanceIds and check for
  2149. // a match with our device.
  2150. //
  2151. for (p = DeviceInstanceIdsList;
  2152. *p;
  2153. p += (lstrlen(p) + 1)) {
  2154. if (lstrcmpi(p, DeviceInstanceId) == 0) {
  2155. //
  2156. // We have a match! First we will check to
  2157. // see if this is the only DeviceInstanceId
  2158. // in the list. To do this take the length of
  2159. // the string and add two for the two terminating
  2160. // NULLs of a multi-sz string and compare that
  2161. // to cbData. If it is the same as (or larger
  2162. // than) cbData then this is the only string
  2163. // in the multi-sz list.
  2164. //
  2165. if ((p == DeviceInstanceIdsList) &&
  2166. (((lstrlen(DeviceInstanceIdsList) + 2) * sizeof(TCHAR)) >= cbData)) {
  2167. //
  2168. // Since there is only this one DeviceInstanceId
  2169. // in the list set bDeleteBackupInstance to TRUE
  2170. // so we can delete this entire subkey along
  2171. // with the files that are backed up for it.
  2172. //
  2173. bDeleteBackupInstance = TRUE;
  2174. } else {
  2175. //
  2176. // Since there is more than this DeviceInstanceId
  2177. // in the list we need to remove just this
  2178. // one Id from the multi-sz string and
  2179. // put the new multi-sz string back
  2180. // into the registry.
  2181. //
  2182. DWORD pLength = lstrlen(p);
  2183. PTSTR p2 = p + (pLength + 1);
  2184. memcpy(p, p2, cbData - ((ULONG_PTR)p2 - (ULONG_PTR)DeviceInstanceIdsList));
  2185. RegSetValueEx(hKeyReinstallInstance,
  2186. pszReinstallDeviceInstanceIds,
  2187. 0,
  2188. REG_MULTI_SZ,
  2189. (PBYTE)DeviceInstanceIdsList,
  2190. cbData - ((pLength + 1) * sizeof(TCHAR))
  2191. );
  2192. }
  2193. break;
  2194. }
  2195. }
  2196. }
  2197. MyFree(DeviceInstanceIdsList);
  2198. }
  2199. }
  2200. RegCloseKey(hKeyReinstallInstance);
  2201. //
  2202. // If this entire subkey and it's corresponding directory need
  2203. // to be deleted then do it now.
  2204. //
  2205. if (bDeleteBackupInstance) {
  2206. pSetupDeleteBackup(ReinstallInstance);
  2207. }
  2208. }
  2209. //
  2210. // Need to update the cbInstanceSize variable before calling
  2211. // RegEnumKeyEx again.
  2212. //
  2213. cbInstanceSize = sizeof(ReinstallInstance) / sizeof(TCHAR);
  2214. }
  2215. RegCloseKey(hKeyReinstall);
  2216. }
  2217. //
  2218. // Create the new Reinstall instance backup subkey.
  2219. //
  2220. Err = pSetupBackupCreateReinstallKey(BackupFileQueue);
  2221. clean0: ; // Nothing to do.
  2222. } except(EXCEPTION_EXECUTE_HANDLER) {
  2223. //
  2224. // if we except, assume it's due to invalid parameter
  2225. //
  2226. Err = ERROR_INVALID_PARAMETER;
  2227. }
  2228. SetLastError(Err);
  2229. return Err;
  2230. }
  2231. //
  2232. // ==========================================================
  2233. //
  2234. VOID
  2235. pSetupCleanupBackup(
  2236. IN PSP_FILE_QUEUE Queue
  2237. )
  2238. /*++
  2239. Routine Description:
  2240. This routine is called to delete any backup directories or registry entries
  2241. associated with this queue.
  2242. Arguments:
  2243. Queue file queue
  2244. Return Value:
  2245. VOID
  2246. --*/
  2247. {
  2248. TCHAR BackupInstance[MAX_PATH];
  2249. DWORD cbData;
  2250. //
  2251. // If we don't have a BackupInfID or a BackupInstanceID then the backup
  2252. // must have failed much earlier. If the backup failed then it would have
  2253. // cleaned itself up so there is no cleanup that needs to be done now.
  2254. //
  2255. if ((Queue->BackupInfID == -1) ||
  2256. (Queue->BackupInstanceID == -1)) {
  2257. return;
  2258. }
  2259. //
  2260. // Get the Backup Instance from the backup queue.
  2261. //
  2262. cbData = MAX_PATH;
  2263. if (pSetupStringTableStringFromIdEx(Queue->StringTable,
  2264. Queue->BackupInstanceID,
  2265. BackupInstance,
  2266. &cbData)) {
  2267. pSetupDeleteBackup(BackupInstance);
  2268. }
  2269. }
  2270. //
  2271. // ==========================================================
  2272. //
  2273. BOOL
  2274. PostDelayedMove(
  2275. IN PSP_FILE_QUEUE Queue,
  2276. IN PCTSTR CurrentName,
  2277. IN PCTSTR NewName, OPTIONAL
  2278. IN DWORD SecurityDesc,
  2279. IN BOOL TargetIsProtected
  2280. )
  2281. /*++
  2282. Routine Description:
  2283. Helper for DelayedMove
  2284. We don't do any delayed Moves until we know all else succeeded
  2285. Arguments:
  2286. Queue Queue that the move is applied to
  2287. CurrentName Name of file we want to move
  2288. NewName Name we want to move to
  2289. SecurityDesc Index in string table of Security Descriptor string or -1 if not present
  2290. TargetIsProtected Indicates whether target file is a protected system file
  2291. Return Value:
  2292. FALSE if error
  2293. --*/
  2294. {
  2295. PSP_DELAYMOVE_NODE DelayMoveNode;
  2296. LONG SourceFilename;
  2297. LONG TargetFilename;
  2298. DWORD Err;
  2299. if (CurrentName == NULL) {
  2300. SourceFilename = -1;
  2301. } else {
  2302. SourceFilename = pSetupStringTableAddString(Queue->StringTable,
  2303. (PTSTR)CurrentName,
  2304. STRTAB_CASE_SENSITIVE
  2305. );
  2306. if (SourceFilename == -1) {
  2307. Err = ERROR_NOT_ENOUGH_MEMORY;
  2308. goto clean0;
  2309. }
  2310. }
  2311. if (NewName == NULL) {
  2312. TargetFilename = -1;
  2313. } else {
  2314. TargetFilename = pSetupStringTableAddString(Queue->StringTable,
  2315. (PTSTR)NewName,
  2316. STRTAB_CASE_SENSITIVE
  2317. );
  2318. if (TargetFilename == -1) {
  2319. Err = ERROR_NOT_ENOUGH_MEMORY;
  2320. goto clean0;
  2321. }
  2322. }
  2323. DelayMoveNode = MyMalloc(sizeof(SP_DELAYMOVE_NODE));
  2324. if (DelayMoveNode == NULL) {
  2325. Err = ERROR_NOT_ENOUGH_MEMORY;
  2326. goto clean0;
  2327. }
  2328. DelayMoveNode->NextNode = NULL;
  2329. DelayMoveNode->SourceFilename = SourceFilename;
  2330. DelayMoveNode->TargetFilename = TargetFilename;
  2331. DelayMoveNode->SecurityDesc = SecurityDesc;
  2332. DelayMoveNode->TargetIsProtected = TargetIsProtected;
  2333. if (Queue->DelayMoveQueueTail == NULL) {
  2334. Queue->DelayMoveQueue = DelayMoveNode;
  2335. } else {
  2336. Queue->DelayMoveQueueTail->NextNode = DelayMoveNode;
  2337. }
  2338. Queue->DelayMoveQueueTail = DelayMoveNode;
  2339. Err = NO_ERROR;
  2340. clean0:
  2341. SetLastError(Err);
  2342. return (Err == NO_ERROR);
  2343. }
  2344. //
  2345. // ==========================================================
  2346. //
  2347. DWORD
  2348. DoAllDelayedMoves(
  2349. IN PSP_FILE_QUEUE Queue
  2350. )
  2351. /*++
  2352. Routine Description:
  2353. Execute the Delayed Moves previously posted
  2354. Arguments:
  2355. Queue Queue that has the list in
  2356. Return Value:
  2357. Error Status
  2358. --*/
  2359. {
  2360. PSP_DELAYMOVE_NODE DelayMoveNode;
  2361. PTSTR CurrentName;
  2362. PTSTR TargetName;
  2363. BOOL b = TRUE;
  2364. PSP_DELAYMOVE_NODE DoneQueue = NULL;
  2365. PSP_DELAYMOVE_NODE NextNode = NULL;
  2366. DWORD Err = NO_ERROR;
  2367. BOOL EnableProtectedRenames = FALSE;
  2368. for (DelayMoveNode = Queue->DelayMoveQueue ; DelayMoveNode ; DelayMoveNode = NextNode ) {
  2369. NextNode = DelayMoveNode->NextNode;
  2370. MYASSERT(DelayMoveNode->SourceFilename != -1);
  2371. CurrentName = pSetupStringTableStringFromId(Queue->StringTable, DelayMoveNode->SourceFilename);
  2372. MYASSERT(CurrentName);
  2373. if (DelayMoveNode->TargetFilename == -1) {
  2374. TargetName = NULL;
  2375. } else {
  2376. TargetName = pSetupStringTableStringFromId( Queue->StringTable, DelayMoveNode->TargetFilename );
  2377. MYASSERT(TargetName);
  2378. }
  2379. //
  2380. // Keep track of whether we've encountered any protected system files.
  2381. //
  2382. EnableProtectedRenames |= DelayMoveNode->TargetIsProtected;
  2383. #ifdef UNICODE
  2384. //
  2385. // If this is a move (instead of a delete), then set security (letting
  2386. // SCE know what the file's final name will be.
  2387. //
  2388. if((DelayMoveNode->SecurityDesc != -1) && TargetName) {
  2389. Err = pSetupCallSCE(ST_SCE_RENAME,
  2390. CurrentName,
  2391. Queue,
  2392. TargetName,
  2393. DelayMoveNode->SecurityDesc,
  2394. NULL
  2395. );
  2396. if(Err != NO_ERROR ){
  2397. //
  2398. // If we're on the first delay-move node, then we can abort.
  2399. // However, if we've already processed one or more nodes, then
  2400. // we can't abort--we must simply log an error indicating what
  2401. // happened and keep on going.
  2402. //
  2403. WriteLogEntry(Queue->LogContext,
  2404. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2405. MSG_LOG_DELAYED_MOVE_SCE_FAILED,
  2406. NULL,
  2407. CurrentName,
  2408. TargetName
  2409. );
  2410. WriteLogError(Queue->LogContext,
  2411. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2412. Err
  2413. );
  2414. if(DelayMoveNode == Queue->DelayMoveQueue) {
  2415. //
  2416. // Failure occurred on 1st node--we can abort.
  2417. //
  2418. WriteLogEntry(Queue->LogContext,
  2419. SETUP_LOG_ERROR,
  2420. MSG_LOG_OPERATION_CANCELLED,
  2421. NULL
  2422. );
  2423. break;
  2424. } else {
  2425. //
  2426. // There's no turning back--log an error and keep on going.
  2427. //
  2428. WriteLogEntry(Queue->LogContext,
  2429. SETUP_LOG_ERROR,
  2430. MSG_LOG_ERROR_IGNORED,
  2431. NULL
  2432. );
  2433. Err = NO_ERROR;
  2434. }
  2435. }
  2436. } else
  2437. #endif
  2438. {
  2439. Err = NO_ERROR;
  2440. }
  2441. //
  2442. // finally delay the move
  2443. //
  2444. if(!DelayedMove(CurrentName, TargetName)) {
  2445. Err = GetLastError();
  2446. //
  2447. // Same deal as above with SCE call--if we've already processed one
  2448. // or more delay-move nodes, we can't abort.
  2449. //
  2450. if(TargetName) {
  2451. WriteLogEntry(Queue->LogContext,
  2452. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2453. MSG_LOG_DELAYED_MOVE_FAILED,
  2454. NULL,
  2455. CurrentName,
  2456. TargetName
  2457. );
  2458. } else {
  2459. WriteLogEntry(Queue->LogContext,
  2460. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2461. MSG_LOG_DELAYED_DELETE_FAILED,
  2462. NULL,
  2463. CurrentName
  2464. );
  2465. }
  2466. WriteLogError(Queue->LogContext,
  2467. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2468. Err
  2469. );
  2470. if(DelayMoveNode == Queue->DelayMoveQueue) {
  2471. //
  2472. // Failure occurred on 1st node--we can abort.
  2473. //
  2474. WriteLogEntry(Queue->LogContext,
  2475. SETUP_LOG_ERROR,
  2476. MSG_LOG_OPERATION_CANCELLED,
  2477. NULL
  2478. );
  2479. break;
  2480. } else {
  2481. //
  2482. // There's no turning back--log an error and keep on going.
  2483. //
  2484. WriteLogEntry(Queue->LogContext,
  2485. SETUP_LOG_ERROR,
  2486. MSG_LOG_ERROR_IGNORED,
  2487. NULL
  2488. );
  2489. Err = NO_ERROR;
  2490. }
  2491. }
  2492. //
  2493. // Move node to queue containing nodes that have already been processed
  2494. //
  2495. DelayMoveNode->NextNode = DoneQueue;
  2496. DoneQueue = DelayMoveNode;
  2497. }
  2498. //
  2499. // If we have any replacement of protected system files, then we need to
  2500. // inform session manager so that it allows the replacement to occur upon
  2501. // reboot.
  2502. //
  2503. // NOTE: We don't have to worry about enabling replacement of system files
  2504. // with unsigned (hence, untrusted) versions. We only queue up unsigned
  2505. // system files for replacement if the user was explicitly warned of (and
  2506. // agreed to) the consequences.
  2507. //
  2508. // NTRAID#55485-2000/02/03-jamiehun
  2509. // Protected renames only allows "rename all" or "rename none"
  2510. //
  2511. // the session manager only allows the granularity of "allow all
  2512. // renames" or "allow no renames". If Err != NO_ERROR, then we
  2513. // might want to clear out this flag, but that means we'd negate
  2514. // any renames that were previously allowed. Yuck. So we flip a
  2515. // coin, decide to do nothing, and hope for the best if an error
  2516. // occurred. We have similar situation above -- it's all or
  2517. // nothing.
  2518. //
  2519. if((Err == NO_ERROR) && EnableProtectedRenames) {
  2520. pSetupProtectedRenamesFlag(TRUE);
  2521. }
  2522. //
  2523. // any nodes that are left are dropped
  2524. //
  2525. for ( ; DelayMoveNode ; DelayMoveNode = NextNode ) {
  2526. NextNode = DelayMoveNode->NextNode;
  2527. MyFree(DelayMoveNode);
  2528. }
  2529. Queue->DelayMoveQueue = NULL;
  2530. Queue->DelayMoveQueueTail = NULL;
  2531. //
  2532. // delete all nodes we queue'd
  2533. //
  2534. for ( ; DoneQueue ; DoneQueue = NextNode ) {
  2535. NextNode = DoneQueue->NextNode;
  2536. //
  2537. // done with node
  2538. //
  2539. MyFree(DoneQueue);
  2540. }
  2541. return Err;
  2542. }
  2543. //
  2544. // ==========================================================
  2545. //
  2546. VOID
  2547. pSetupUnwindAll(
  2548. IN PSP_FILE_QUEUE Queue,
  2549. IN BOOL Succeeded
  2550. )
  2551. /*++
  2552. Routine Description:
  2553. Processes the Unwind Queue. If Succeeded is FALSE, restores any data that was backed up
  2554. Arguments:
  2555. Queue Queue to be unwound
  2556. Succeeded Indicates if we should treat the whole operation as succeeded or failed
  2557. Return Value:
  2558. None--this routine should always succeed. (Any file errors encountered
  2559. along the way are logged in the setupapi logfile.)
  2560. --*/
  2561. {
  2562. // if Succeeded, we need to delete Temp files
  2563. // if we didn't succeed, we need to restore backups
  2564. PSP_UNWIND_NODE UnwindNode;
  2565. PSP_UNWIND_NODE ThisNode;
  2566. SP_TARGET_ENT TargetInfo;
  2567. PTSTR BackupFilename;
  2568. PTSTR TargetFilename;
  2569. PTSTR RenamedFilename;
  2570. DWORD Err = NO_ERROR;
  2571. TCHAR TempPath[MAX_PATH];
  2572. PTSTR TempNamePtr;
  2573. TCHAR TempFilename[MAX_PATH];
  2574. BOOL RestoreByRenaming;
  2575. BOOL OkToDeleteBackup;
  2576. try {
  2577. if (Succeeded == FALSE) {
  2578. //
  2579. // we need to restore backups
  2580. //
  2581. WriteLogEntry(
  2582. Queue->LogContext,
  2583. SETUP_LOG_WARNING,
  2584. MSG_LOG_UNWIND,
  2585. NULL);
  2586. for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
  2587. ThisNode = UnwindNode;
  2588. UnwindNode = UnwindNode->NextNode;
  2589. if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
  2590. BackupFilename = NULL;
  2591. TargetFilename = NULL;
  2592. RenamedFilename = NULL;
  2593. // restore backup
  2594. if(!(TargetInfo.InternalFlags & SP_TEFLG_RESTORED)) {
  2595. // get target name
  2596. TargetFilename = pSetupFormFullPath(
  2597. Queue->StringTable,
  2598. TargetInfo.TargetRoot,
  2599. TargetInfo.TargetSubDir,
  2600. TargetInfo.TargetFilename);
  2601. if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
  2602. //
  2603. // Get renamed filename
  2604. //
  2605. RenamedFilename = pSetupStringTableStringFromId(Queue->StringTable,
  2606. TargetInfo.NewTargetFilename
  2607. );
  2608. }
  2609. if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
  2610. //
  2611. // get backup name
  2612. //
  2613. BackupFilename = pSetupFormFullPath(
  2614. Queue->StringTable,
  2615. TargetInfo.BackupRoot,
  2616. TargetInfo.BackupSubDir,
  2617. TargetInfo.BackupFilename);
  2618. }
  2619. }
  2620. if(TargetFilename && (RenamedFilename || BackupFilename)) {
  2621. //
  2622. // We either renamed the original file or we backed it up.
  2623. // We need to put it back.
  2624. //
  2625. RestoreByRenaming = RenamedFilename ? TRUE : FALSE;
  2626. RestoreRenamedOrBackedUpFile(TargetFilename,
  2627. (RestoreByRenaming
  2628. ? RenamedFilename
  2629. : BackupFilename),
  2630. RestoreByRenaming,
  2631. Queue->LogContext
  2632. );
  2633. //
  2634. // If we were doing a copy (i.e., from a backup) as opposed
  2635. // to a rename, then we need to reapply timestamp and
  2636. // security.
  2637. //
  2638. if(!RestoreByRenaming) {
  2639. Err = GetSetFileTimestamp(TargetFilename,
  2640. &(ThisNode->CreateTime),
  2641. &(ThisNode->AccessTime),
  2642. &(ThisNode->WriteTime),
  2643. TRUE
  2644. );
  2645. if(Err != NO_ERROR) {
  2646. //
  2647. // We just blew away the timestamp on the file--log
  2648. // an error entry to that effect.
  2649. //
  2650. WriteLogEntry(Queue->LogContext,
  2651. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2652. MSG_LOG_BACKUP_EXISTING_RESTORE_FILETIME_FAILED,
  2653. NULL,
  2654. TargetFilename
  2655. );
  2656. WriteLogError(Queue->LogContext,
  2657. SETUP_LOG_ERROR,
  2658. Err
  2659. );
  2660. }
  2661. if(ThisNode->SecurityDesc != NULL){
  2662. Err = StampFileSecurity(TargetFilename, ThisNode->SecurityDesc);
  2663. if(Err != NO_ERROR) {
  2664. //
  2665. // We just blew away the existing security on
  2666. // the file--log an error entry to that effect.
  2667. //
  2668. WriteLogEntry(Queue->LogContext,
  2669. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2670. MSG_LOG_BACKUP_EXISTING_RESTORE_SECURITY_FAILED,
  2671. NULL,
  2672. TargetFilename
  2673. );
  2674. WriteLogError(Queue->LogContext,
  2675. SETUP_LOG_ERROR,
  2676. Err
  2677. );
  2678. }
  2679. #ifdef UNICODE
  2680. Err = pSetupCallSCE(ST_SCE_UNWIND,
  2681. TargetFilename,
  2682. NULL,
  2683. NULL,
  2684. -1,
  2685. ThisNode->SecurityDesc
  2686. );
  2687. if(Err != NO_ERROR) {
  2688. //
  2689. // We just blew away the existing security on
  2690. // the file--log an error entry to that effect.
  2691. //
  2692. WriteLogEntry(Queue->LogContext,
  2693. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2694. MSG_LOG_BACKUP_EXISTING_RESTORE_SCE_FAILED,
  2695. NULL,
  2696. TargetFilename
  2697. );
  2698. WriteLogError(Queue->LogContext,
  2699. SETUP_LOG_ERROR,
  2700. Err
  2701. );
  2702. }
  2703. #endif
  2704. }
  2705. }
  2706. //
  2707. // Now mark that we've restored this file. We'll delete
  2708. // tempfiles later
  2709. //
  2710. TargetInfo.InternalFlags |= SP_TEFLG_RESTORED;
  2711. pSetupBackupSetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo);
  2712. }
  2713. if(BackupFilename) {
  2714. MyFree(BackupFilename);
  2715. }
  2716. if(TargetFilename) {
  2717. MyFree(TargetFilename);
  2718. }
  2719. }
  2720. }
  2721. }
  2722. //
  2723. // cleanup - remove temporary files
  2724. //
  2725. for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
  2726. ThisNode = UnwindNode;
  2727. UnwindNode = UnwindNode->NextNode;
  2728. if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
  2729. // delete temporary file
  2730. if (TargetInfo.InternalFlags & SP_TEFLG_TEMPNAME) {
  2731. //
  2732. // get name of file that was used for backup
  2733. //
  2734. BackupFilename = pSetupFormFullPath(
  2735. Queue->StringTable,
  2736. TargetInfo.BackupRoot,
  2737. TargetInfo.BackupSubDir,
  2738. TargetInfo.BackupFilename);
  2739. if(BackupFilename) {
  2740. //
  2741. // If this operation was a bootfile replacement, then we
  2742. // don't want to delete the backup (if we used the renamed
  2743. // file for the backup as well). A delayed delete will
  2744. // have been queued to get rid of the file after a reboot.
  2745. //
  2746. OkToDeleteBackup = TRUE;
  2747. if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
  2748. //
  2749. // Retrieve the renamed filename to see if it's the
  2750. // same as the backup filename.
  2751. //
  2752. RenamedFilename = pSetupStringTableStringFromId(Queue->StringTable,
  2753. TargetInfo.NewTargetFilename
  2754. );
  2755. if(!lstrcmpi(BackupFilename, RenamedFilename)) {
  2756. OkToDeleteBackup = FALSE;
  2757. }
  2758. }
  2759. if(OkToDeleteBackup) {
  2760. //
  2761. // since it was temporary, delete it
  2762. //
  2763. SetFileAttributes(BackupFilename, FILE_ATTRIBUTE_NORMAL);
  2764. if(!DeleteFile(BackupFilename)) {
  2765. //
  2766. // Alright, see if we can set it up for delayed delete
  2767. // instead.
  2768. //
  2769. if(!DelayedMove(BackupFilename, NULL)) {
  2770. //
  2771. // Oh well, just write a log entry indicating that
  2772. // this file turd was left on the user's disk.
  2773. //
  2774. Err = GetLastError();
  2775. WriteLogEntry(Queue->LogContext,
  2776. SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
  2777. MSG_LOG_BACKUP_DELAYED_DELETE_FAILED,
  2778. NULL,
  2779. BackupFilename
  2780. );
  2781. WriteLogError(Queue->LogContext,
  2782. SETUP_LOG_WARNING,
  2783. Err
  2784. );
  2785. }
  2786. }
  2787. }
  2788. MyFree(BackupFilename);
  2789. }
  2790. }
  2791. pSetupResetTarget(Queue->TargetLookupTable,
  2792. ThisNode->TargetID,
  2793. NULL,
  2794. &TargetInfo,
  2795. sizeof(TargetInfo),
  2796. (LPARAM)0
  2797. );
  2798. }
  2799. // cleanup node
  2800. if (ThisNode->SecurityDesc != NULL) {
  2801. MyFree(ThisNode->SecurityDesc);
  2802. }
  2803. MyFree(ThisNode);
  2804. }
  2805. Queue->UnwindQueue = NULL;
  2806. } except (EXCEPTION_EXECUTE_HANDLER) {
  2807. //
  2808. // should generally not get here
  2809. // unless Queue is invalid
  2810. //
  2811. }
  2812. }
  2813. //
  2814. // ==========================================================
  2815. //
  2816. DWORD _SetupGetBackupInformation(
  2817. IN PSP_FILE_QUEUE Queue,
  2818. OUT PSP_BACKUP_QUEUE_PARAMS_V2 BackupParams
  2819. )
  2820. /*++
  2821. Routine Description:
  2822. Get Backup INF path - Internal version
  2823. Arguments:
  2824. Queue - pointer to queue structure (validated)
  2825. BackupParams OUT - filled with INF file path
  2826. Return Value:
  2827. TRUE if success, else FALSE
  2828. --*/
  2829. {
  2830. //
  2831. // Queue is assumed to have been validated
  2832. // BackupParams is in Native format
  2833. //
  2834. LONG BackupInfID;
  2835. ULONG BufSize = MAX_PATH;
  2836. BOOL b;
  2837. DWORD err = NO_ERROR;
  2838. LPCTSTR filename;
  2839. INT offset;
  2840. BackupInfID = Queue->BackupInfID;
  2841. if (BackupInfID != -1) {
  2842. //
  2843. // get inf from stringtable
  2844. //
  2845. b = pSetupStringTableStringFromIdEx(Queue->StringTable,
  2846. BackupInfID,
  2847. BackupParams->FullInfPath,
  2848. &BufSize);
  2849. if (b == FALSE) {
  2850. if (BufSize == 0) {
  2851. err = ERROR_NO_BACKUP;
  2852. } else {
  2853. err = ERROR_INSUFFICIENT_BUFFER;
  2854. }
  2855. goto Clean0;
  2856. }
  2857. //
  2858. // find index of filename
  2859. //
  2860. filename = pSetupGetFileTitle(BackupParams->FullInfPath);
  2861. offset = (INT)(filename - BackupParams->FullInfPath);
  2862. BackupParams->FilenameOffset = offset;
  2863. //
  2864. // If the caller passed in a SP_BACKUP_QUEUE_PARAMS_V2 structure then
  2865. // also fill in the ReinstallInstance string value.
  2866. //
  2867. if (BackupParams->cbSize >= sizeof(SP_BACKUP_QUEUE_PARAMS_V2)) {
  2868. BufSize = MAX_PATH;
  2869. if(Queue->BackupInstanceID != -1) {
  2870. b = pSetupStringTableStringFromIdEx(Queue->StringTable,
  2871. Queue->BackupInstanceID,
  2872. BackupParams->ReinstallInstance,
  2873. &BufSize);
  2874. } else {
  2875. //
  2876. // no instance ID
  2877. //
  2878. BackupParams->ReinstallInstance[0] = TEXT('\0');
  2879. }
  2880. if (b == FALSE) {
  2881. if (BufSize == 0) {
  2882. err = ERROR_NO_BACKUP;
  2883. } else {
  2884. err = ERROR_INSUFFICIENT_BUFFER;
  2885. }
  2886. goto Clean0;
  2887. }
  2888. }
  2889. } else {
  2890. //
  2891. // no backup path
  2892. //
  2893. err = ERROR_NO_BACKUP;
  2894. }
  2895. Clean0:
  2896. return err;
  2897. }
  2898. #ifdef UNICODE
  2899. //
  2900. // ANSI version in UNICODE
  2901. //
  2902. BOOL
  2903. WINAPI
  2904. SetupGetBackupInformationA(
  2905. IN HSPFILEQ QueueHandle,
  2906. OUT PSP_BACKUP_QUEUE_PARAMS_V2_A BackupParams
  2907. )
  2908. {
  2909. BOOL b;
  2910. int i;
  2911. INT c;
  2912. LPCSTR p;
  2913. SP_BACKUP_QUEUE_PARAMS_W BackupParamsW;
  2914. //
  2915. // confirm structure size
  2916. //
  2917. try {
  2918. if((BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V2_A)) &&
  2919. (BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V1_A))) {
  2920. SetLastError(ERROR_INVALID_PARAMETER);
  2921. b = FALSE;
  2922. leave; // exit try block
  2923. }
  2924. //
  2925. // call Unicode version of API
  2926. //
  2927. ZeroMemory( &BackupParamsW, sizeof(BackupParamsW) );
  2928. BackupParamsW.cbSize = sizeof(BackupParamsW);
  2929. b = SetupGetBackupInformationW(QueueHandle,&BackupParamsW);
  2930. if (b) {
  2931. //
  2932. // success, convert structure from UNICODE to ANSI
  2933. //
  2934. i = WideCharToMultiByte(
  2935. CP_ACP,
  2936. 0,
  2937. BackupParamsW.FullInfPath,
  2938. MAX_PATH,
  2939. BackupParams->FullInfPath,
  2940. MAX_PATH,
  2941. NULL,
  2942. NULL
  2943. );
  2944. if (i==0) {
  2945. //
  2946. // error occurred (LastError set to error)
  2947. //
  2948. b = FALSE;
  2949. leave; // exit try block
  2950. }
  2951. //
  2952. // we need to recalc the offset of INF filename
  2953. // taking care of internationalization
  2954. //
  2955. p = BackupParams->FullInfPath;
  2956. for(c = 0; c < BackupParamsW.FilenameOffset; c++) {
  2957. p = CharNextA(p);
  2958. }
  2959. BackupParams->FilenameOffset = (int)(p-(BackupParams->FullInfPath)); // new offset in ANSI
  2960. if (BackupParams->cbSize >= sizeof(SP_BACKUP_QUEUE_PARAMS_V2_A)) {
  2961. //
  2962. // instance
  2963. //
  2964. i = WideCharToMultiByte(
  2965. CP_ACP,
  2966. 0,
  2967. BackupParamsW.ReinstallInstance,
  2968. MAX_PATH,
  2969. BackupParams->ReinstallInstance,
  2970. MAX_PATH,
  2971. NULL,
  2972. NULL
  2973. );
  2974. if (i==0) {
  2975. //
  2976. // error occurred (LastError set to error)
  2977. //
  2978. b = FALSE;
  2979. leave; // exit try block
  2980. }
  2981. }
  2982. }
  2983. } except(EXCEPTION_EXECUTE_HANDLER) {
  2984. //
  2985. // if we except, assume it's due to invalid parameter
  2986. //
  2987. SetLastError(ERROR_INVALID_PARAMETER);
  2988. b = FALSE;
  2989. }
  2990. return b;
  2991. }
  2992. #else
  2993. //
  2994. // Unicode version in ANSI
  2995. //
  2996. BOOL
  2997. WINAPI
  2998. SetupGetBackupInformationW(
  2999. IN HSPFILEQ QueueHandle,
  3000. OUT PSP_BACKUP_QUEUE_PARAMS_V2_W BackupParams
  3001. )
  3002. {
  3003. UNREFERENCED_PARAMETER(QueueHandle);
  3004. UNREFERENCED_PARAMETER(BackupParams);
  3005. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  3006. return(FALSE);
  3007. }
  3008. #endif
  3009. //
  3010. // Native version
  3011. //
  3012. BOOL
  3013. WINAPI
  3014. SetupGetBackupInformation(
  3015. IN HSPFILEQ QueueHandle,
  3016. OUT PSP_BACKUP_QUEUE_PARAMS_V2 BackupParams
  3017. )
  3018. /*++
  3019. Routine Description:
  3020. Get Backup INF path
  3021. Arguments:
  3022. QueueHandle - handle of queue to retrieve backup INF file from
  3023. BackupParams - IN - has cbSize set, OUT - filled with INF file path
  3024. Return Value:
  3025. TRUE if success, else FALSE
  3026. --*/
  3027. {
  3028. BOOL b = TRUE;
  3029. PSP_FILE_QUEUE Queue = (PSP_FILE_QUEUE)QueueHandle;
  3030. DWORD res;
  3031. //
  3032. // first validate QueueHandle
  3033. //
  3034. try {
  3035. if(Queue->Signature != SP_FILE_QUEUE_SIG) {
  3036. b = FALSE;
  3037. }
  3038. } except(EXCEPTION_EXECUTE_HANDLER) {
  3039. b = FALSE;
  3040. }
  3041. if(!b) {
  3042. SetLastError(ERROR_INVALID_HANDLE);
  3043. goto Clean0;
  3044. }
  3045. //
  3046. // now fill in structure
  3047. // if we except, assume bad pointer
  3048. //
  3049. try {
  3050. if((BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V2)) &&
  3051. (BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V1))) {
  3052. SetLastError(ERROR_INVALID_PARAMETER);
  3053. b = FALSE;
  3054. leave; // exit try block
  3055. }
  3056. res = _SetupGetBackupInformation(Queue,BackupParams);
  3057. if (res == NO_ERROR) {
  3058. b = TRUE;
  3059. } else {
  3060. SetLastError(res);
  3061. b = FALSE;
  3062. }
  3063. } except(EXCEPTION_EXECUTE_HANDLER) {
  3064. //
  3065. // if we except, assume it's due to invalid parameter
  3066. //
  3067. SetLastError(ERROR_INVALID_PARAMETER);
  3068. b = FALSE;
  3069. }
  3070. Clean0:
  3071. return b;
  3072. }
  3073. //
  3074. // ==========================================================
  3075. //
  3076. VOID
  3077. RestoreRenamedOrBackedUpFile(
  3078. IN PCTSTR TargetFilename,
  3079. IN PCTSTR CurrentFilename,
  3080. IN BOOL RenameFile,
  3081. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3082. )
  3083. /*++
  3084. Routine Description:
  3085. This routine does its best to restore a backed-up or renamed file back to
  3086. its original name.
  3087. Arguments:
  3088. TargetFilename - filename to be restored to
  3089. CurrentFilename - file to restore
  3090. RenameFile - if TRUE, CurrentFilename was previously renamed from
  3091. TargetFilename (hence should be renamed back). If FALSE,
  3092. CurrentFilename is merely a copy, and should be copied back.
  3093. LogContext - supplies a log context used if errors are encountered.
  3094. Return Value:
  3095. None.
  3096. --*/
  3097. {
  3098. DWORD Err;
  3099. TCHAR TempPath[MAX_PATH];
  3100. PTSTR TempNamePtr;
  3101. TCHAR TempFilename[MAX_PATH];
  3102. DWORD LogTag = AllocLogInfoSlotOrLevel(LogContext,SETUP_LOG_INFO,FALSE);
  3103. WriteLogEntry(
  3104. LogContext,
  3105. LogTag,
  3106. MSG_LOG_UNWIND_FILE,
  3107. NULL,
  3108. CurrentFilename,
  3109. TargetFilename
  3110. );
  3111. //
  3112. // First, clear target attributes...
  3113. //
  3114. SetFileAttributes(TargetFilename, FILE_ATTRIBUTE_NORMAL);
  3115. if(RenameFile) {
  3116. //
  3117. // simple case, move temporary file over existing file
  3118. //
  3119. pSetupExemptFileFromProtection(
  3120. TargetFilename,
  3121. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3122. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3123. LogContext,
  3124. NULL
  3125. );
  3126. Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
  3127. } else {
  3128. pSetupExemptFileFromProtection(
  3129. TargetFilename,
  3130. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3131. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3132. LogContext,
  3133. NULL
  3134. );
  3135. Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
  3136. }
  3137. if(Err != NO_ERROR) {
  3138. //
  3139. // Can't replace the file that got copied in place of
  3140. // the original one--try to move that one to a tempname
  3141. // and schedule it for delayed deletion.
  3142. //
  3143. WriteLogEntry(LogContext,
  3144. SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
  3145. MSG_LOG_UNWIND_TRY1_FAILED,
  3146. NULL,
  3147. CurrentFilename,
  3148. TargetFilename
  3149. );
  3150. WriteLogError(LogContext,
  3151. SETUP_LOG_ERROR,
  3152. Err
  3153. );
  3154. //
  3155. // First, strip the filename off the path.
  3156. //
  3157. _tcscpy(TempPath, TargetFilename);
  3158. TempNamePtr = (PTSTR)pSetupGetFileTitle(TempPath);
  3159. *TempNamePtr = TEXT('\0');
  3160. //
  3161. // Now get a temp filename within that directory...
  3162. //
  3163. if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
  3164. //
  3165. // Uh oh!
  3166. //
  3167. Err = GetLastError();
  3168. } else if(!DoMove(TargetFilename, TempFilename)) {
  3169. Err = GetLastError();
  3170. } else {
  3171. //
  3172. // OK, we were able to rename the current file to a
  3173. // temp filename--now attempt to copy or move the
  3174. // original file back to its original name.
  3175. //
  3176. if(RenameFile) {
  3177. Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
  3178. } else {
  3179. Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
  3180. }
  3181. if(Err != NO_ERROR) {
  3182. //
  3183. // This is very bad--put the current file back (it's probably
  3184. // better to have something than nothing at all).
  3185. //
  3186. DoMove(TempFilename, TargetFilename);
  3187. }
  3188. }
  3189. if(Err == NO_ERROR) {
  3190. //
  3191. // We successfully moved the current file to a temp
  3192. // filename, and put the original file back. Now
  3193. // queue a delayed delete for the temp file.
  3194. //
  3195. if(!DelayedMove(TempFilename, NULL)) {
  3196. //
  3197. // All this means is that a file turd will get
  3198. // left on the disk--simply log an event about
  3199. // this.
  3200. //
  3201. Err = GetLastError();
  3202. WriteLogEntry(LogContext,
  3203. SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
  3204. MSG_LOG_RENAME_EXISTING_DELAYED_DELETE_FAILED,
  3205. NULL,
  3206. TargetFilename,
  3207. TempFilename
  3208. );
  3209. WriteLogError(LogContext,
  3210. SETUP_LOG_WARNING,
  3211. Err
  3212. );
  3213. }
  3214. } else {
  3215. //
  3216. // We were unable to put the original file back--we
  3217. // can't fail, so just log an error about this and
  3218. // keep on going.
  3219. //
  3220. // in the case of a backed-up file,
  3221. // we might get away with queueing the original file
  3222. // for a delayed rename and then prompting the user
  3223. // to reboot. However, that won't work for renamed
  3224. // files, because they're typically needed very
  3225. // early on in the boot (i.e., before session
  3226. // manager has had a chance to process the delayed
  3227. // rename operations).
  3228. //
  3229. WriteLogEntry(LogContext,
  3230. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  3231. (RenameFile
  3232. ? MSG_LOG_RENAME_EXISTING_RESTORE_FAILED
  3233. : MSG_LOG_BACKUP_EXISTING_RESTORE_FAILED),
  3234. NULL,
  3235. CurrentFilename,
  3236. TargetFilename
  3237. );
  3238. WriteLogError(LogContext,
  3239. SETUP_LOG_ERROR,
  3240. Err
  3241. );
  3242. }
  3243. }
  3244. if (LogTag) {
  3245. ReleaseLogInfoSlot(LogContext,LogTag);
  3246. }
  3247. }
  3248. //
  3249. // ==========================================================
  3250. //
  3251. BOOL
  3252. UnPostDelayedMove(
  3253. IN PSP_FILE_QUEUE Queue,
  3254. IN PCTSTR CurrentName,
  3255. IN PCTSTR NewName OPTIONAL
  3256. )
  3257. /*++
  3258. Routine Description:
  3259. Locates a delay-move node (either for rename or delete), and removes it
  3260. from the delay-move queue.
  3261. Arguments:
  3262. Queue Queue that the move was applied to
  3263. CurrentName Name of file to be moved
  3264. NewName Name to move file to (NULL if delayed-delete)
  3265. Return Value:
  3266. If successful, the return value is TRUE, otherwise it is FALSE.
  3267. --*/
  3268. {
  3269. PSP_DELAYMOVE_NODE CurNode, PrevNode;
  3270. PCTSTR SourceFilename, TargetFilename;
  3271. //
  3272. // Since the path string IDs in the delay-move nodes are case-sensitive, we
  3273. // don't attempt to match on ID. We instead retrieve the strings, and do
  3274. // case-insensitive string compares. Since this routine is rarely used, the
  3275. // performance hit isn't a big deal.
  3276. //
  3277. for(CurNode = Queue->DelayMoveQueue, PrevNode = NULL;
  3278. CurNode;
  3279. PrevNode = CurNode, CurNode = CurNode->NextNode) {
  3280. if(NewName) {
  3281. //
  3282. // We're searching for a delayed rename, so we must pay attention
  3283. // to the target filename.
  3284. //
  3285. if(CurNode->TargetFilename == -1) {
  3286. continue;
  3287. } else {
  3288. TargetFilename = pSetupStringTableStringFromId(Queue->StringTable, CurNode->TargetFilename);
  3289. MYASSERT(TargetFilename);
  3290. if(lstrcmpi(NewName, TargetFilename)) {
  3291. //
  3292. // Target filenames differ--move on.
  3293. //
  3294. continue;
  3295. }
  3296. }
  3297. } else {
  3298. //
  3299. // We're searching for a delayed delete.
  3300. //
  3301. if(CurNode->TargetFilename != -1) {
  3302. //
  3303. // This is a rename, not a delete--move on.
  3304. //
  3305. continue;
  3306. }
  3307. }
  3308. //
  3309. // If we get to here, then the target filenames match (if this is a
  3310. // rename), or they're both empty (if it's a delete). Now compare the
  3311. // source filenames.
  3312. //
  3313. MYASSERT(CurNode->SourceFilename != -1);
  3314. SourceFilename = pSetupStringTableStringFromId(Queue->StringTable, CurNode->SourceFilename);
  3315. MYASSERT(SourceFilename);
  3316. if(lstrcmpi(CurrentName, SourceFilename)) {
  3317. //
  3318. // Source filenames differ--move on.
  3319. //
  3320. continue;
  3321. } else {
  3322. //
  3323. // We have a match--remove the node from the delay-move queue.
  3324. //
  3325. if(PrevNode) {
  3326. PrevNode->NextNode = CurNode->NextNode;
  3327. } else {
  3328. Queue->DelayMoveQueue = CurNode->NextNode;
  3329. }
  3330. if(!CurNode->NextNode) {
  3331. MYASSERT(Queue->DelayMoveQueueTail == CurNode);
  3332. Queue->DelayMoveQueueTail = PrevNode;
  3333. }
  3334. MyFree(CurNode);
  3335. return TRUE;
  3336. }
  3337. }
  3338. //
  3339. // We didn't find a match.
  3340. //
  3341. return FALSE;
  3342. }
  3343. DWORD
  3344. pSetupDoLastKnownGoodBackup(
  3345. IN struct _SP_FILE_QUEUE *Queue, OPTIONAL
  3346. IN PCTSTR TargetFilename,
  3347. IN DWORD Flags,
  3348. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3349. )
  3350. /*++
  3351. Routine Description:
  3352. Process LastKnownGood backups into <<LastGoodDirectory>>.
  3353. If files are to be deleted on restore, write apropriate flags to HKLM\System\LastGoodRecovery\LastGood\<path/file>
  3354. Caviats:
  3355. If file is not inside <<WindowsDirectory>> or sub-directory, exit no-error.
  3356. Backup will not occur if PSPGF_NO_BACKUP is set.
  3357. If !SP_LKG_FLAG_FORCECOPY
  3358. If file is inside <<LastGoodDirectory>>, exit with error.
  3359. If file is inside <<InfDirectory>> throw warnings
  3360. If an INF inside of <<InfDirectory>> is backed up, it's PNF is backed up too.
  3361. If backup fails, we don't abort copy.
  3362. Arguments:
  3363. Queue Queue (optional) if specified, flags will be checked
  3364. TargetFilename Name of file to backup
  3365. Flags
  3366. SP_LKG_FLAG_FORCECOPY - if set, turns copy safety-guards off
  3367. SP_LKG_FLAG_DELETEIFNEW - if set, writes a delete entry for new files
  3368. SP_LKG_FLAG_DELETEEXISTING - if set, writes a delete entry for existing files
  3369. SP_LKG_FLAG_DELETEOP - if set, primary operation is trying to delete/rename file
  3370. LogContext If specified, provides preferred logging context
  3371. Return Value:
  3372. error if operation should be aborted, NO_ERROR otherwise.
  3373. --*/
  3374. {
  3375. #ifdef UNICODE
  3376. int wd_len; // windows directory len
  3377. int tf_len; // target file len
  3378. int id_len; // inf directory len
  3379. int lkgd_len; // last known good directory len
  3380. int rf_len; // relative file len (including preceeding slash)
  3381. BOOL is_inf = FALSE;
  3382. BOOL is_infdir = FALSE;
  3383. BOOL write_delete = FALSE;
  3384. BOOL no_copy = FALSE;
  3385. BOOL source_exists = FALSE;
  3386. BOOL target_exists = FALSE;
  3387. TCHAR FullTargetFilename[MAX_PATH];
  3388. TCHAR BackupTargetFilename[MAX_PATH+14];
  3389. TCHAR TempFilename[MAX_PATH];
  3390. TCHAR RegName[MAX_PATH];
  3391. PCTSTR RelativeFilename;
  3392. PCTSTR CharPtr;
  3393. PTSTR DestPtr;
  3394. PTSTR NamePart = NULL;
  3395. PTSTR ExtPart = NULL;
  3396. DWORD attr;
  3397. HANDLE hFile;
  3398. HKEY hKeyLastGood;
  3399. DWORD disposition;
  3400. LONG regres;
  3401. DWORD LastGoodFlags = 0;
  3402. DWORD rval = NO_ERROR;
  3403. PSETUP_LOG_CONTEXT LocalLogContext = NULL;
  3404. if (!LogContext) {
  3405. //
  3406. // LogContext may be obmitted if there's a Queue parameter
  3407. //
  3408. if (Queue && Queue->LogContext) {
  3409. LogContext = Queue->LogContext;
  3410. } else {
  3411. if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
  3412. LogContext = LocalLogContext;
  3413. }
  3414. }
  3415. MYASSERT(LogContext);
  3416. }
  3417. if ((GlobalSetupFlags & PSPGF_NO_BACKUP)!=0) {
  3418. //
  3419. // in a scenario where (1) we trust what we're doing and
  3420. // (2) what we're doing modifies lots of files
  3421. // or (3) what we're doing is undoable (eg, upgrading OS)
  3422. //
  3423. no_copy = TRUE;
  3424. }
  3425. #if 0
  3426. else if (Queue && !(Queue->Flags & FQF_DEVICE_INSTALL)) {
  3427. //
  3428. // in this scenario, a queue was specified, but it's not marked
  3429. // for device install
  3430. // we're not interested in this case
  3431. //
  3432. no_copy = TRUE;
  3433. }
  3434. #endif
  3435. //
  3436. // cannonicalize the Target name so user doesn't do stuff like .../TEMP/../INF
  3437. //
  3438. tf_len = (int)GetFullPathName(TargetFilename,
  3439. MAX_PATH,
  3440. FullTargetFilename,
  3441. &NamePart);
  3442. if (tf_len <= 0 || tf_len > MAX_PATH) {
  3443. //
  3444. // we don't do large paths very well
  3445. //
  3446. rval = NO_ERROR;
  3447. goto final;
  3448. }
  3449. wd_len = lstrlen(WindowsDirectory);
  3450. lkgd_len = lstrlen(LastGoodDirectory);
  3451. id_len = lstrlen(InfDirectory);
  3452. //
  3453. // see if this file is nested below <<WindowsDirectory>>
  3454. // note that such a file must be at least two characters longer
  3455. //
  3456. if((tf_len <= wd_len)
  3457. || (FullTargetFilename[wd_len] != TEXT('\\'))
  3458. || (_tcsnicmp(WindowsDirectory,FullTargetFilename,wd_len)!=0)) {
  3459. //
  3460. // this file is outside of %windir%, not handled by LKG
  3461. //
  3462. rval = NO_ERROR;
  3463. goto final;
  3464. }
  3465. if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3466. //
  3467. // sanity check for files being copied into LKG dir
  3468. //
  3469. if((tf_len > lkgd_len)
  3470. && (FullTargetFilename[lkgd_len] == TEXT('\\'))
  3471. && (_tcsnicmp(LastGoodDirectory,FullTargetFilename,lkgd_len)==0)) {
  3472. //
  3473. // this file is prefixed by LastGoodDirectory
  3474. // not allowed - throw a log message and inform caller of this mistake
  3475. // return FALSE to abort the operation
  3476. //
  3477. WriteLogEntry(LogContext,
  3478. SETUP_LOG_ERROR,
  3479. MSG_LOG_FILE_BLOCK,
  3480. NULL,
  3481. FullTargetFilename,
  3482. LastGoodDirectory
  3483. );
  3484. rval = ERROR_ACCESS_DENIED;
  3485. goto final;
  3486. }
  3487. }
  3488. if((tf_len > id_len)
  3489. && (FullTargetFilename[id_len] == TEXT('\\'))
  3490. && (_tcsnicmp(InfDirectory,FullTargetFilename,id_len)==0)
  3491. && ((NamePart-FullTargetFilename) == (id_len+1))) {
  3492. //
  3493. // the file sits in the primary INF directory
  3494. //
  3495. is_infdir = TRUE;
  3496. //
  3497. // check for name ending in ".INF" - if so, we need to backup ".PNF" too
  3498. //
  3499. ExtPart = FullTargetFilename+tf_len;
  3500. while ((ExtPart = CharPrev(NamePart,ExtPart)) != NamePart) {
  3501. if (ExtPart[0] == TEXT('.')) {
  3502. break;
  3503. }
  3504. }
  3505. if(_tcsicmp(ExtPart,TEXT(".INF"))==0) {
  3506. //
  3507. // ends in .INF
  3508. //
  3509. is_inf = TRUE;
  3510. //
  3511. // we should only get here if Force is set (ie, we've already determined
  3512. // what is being copied and all is OK). If we don't, this implies someone
  3513. // is trying a back-door INF copy. we've already logged above that they're writing
  3514. // to this directory when they shouldn't. However, if we don't do anything
  3515. // about it, culprit could render machine in bad state
  3516. // change this behavior into a "Force" behavior
  3517. //
  3518. if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3519. no_copy = FALSE; // ensure we'll go through copy logic
  3520. WriteLogEntry(LogContext,
  3521. SETUP_LOG_ERROR,
  3522. MSG_LOG_INF_WARN,
  3523. NULL,
  3524. FullTargetFilename,
  3525. InfDirectory
  3526. );
  3527. if(!(Flags&SP_LKG_FLAG_DELETEOP)) {
  3528. //
  3529. // we're invalidly trying to overwrite an INF/create an INF
  3530. //
  3531. Flags|=SP_LKG_FLAG_DELETEIFNEW;
  3532. }
  3533. }
  3534. } else if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3535. //
  3536. // writing something else into this directory - huh?
  3537. // well, if it's not an INF, we won't pick it up via INF searching
  3538. // if it's a PNF or cache, we're regenerate it
  3539. // don't fret too much, but slap wrist.
  3540. //
  3541. WriteLogEntry(LogContext,
  3542. SETUP_LOG_ERROR,
  3543. MSG_LOG_FILE_WARN,
  3544. NULL,
  3545. FullTargetFilename,
  3546. InfDirectory
  3547. );
  3548. }
  3549. }
  3550. if (no_copy) {
  3551. //
  3552. // we determined that we're not going to backup, and we've now done logging items
  3553. //
  3554. rval = NO_ERROR;
  3555. goto final;
  3556. }
  3557. //
  3558. // does source really exist?
  3559. //
  3560. if ((attr=GetFileAttributes(FullTargetFilename))!=(DWORD)(-1)) {
  3561. source_exists = TRUE;
  3562. if (Flags & SP_LKG_FLAG_DELETEEXISTING) {
  3563. write_delete = TRUE;
  3564. }
  3565. } else if (Flags & SP_LKG_FLAG_DELETEIFNEW) {
  3566. write_delete = TRUE;
  3567. } else {
  3568. //
  3569. // we're done
  3570. //
  3571. rval = NO_ERROR;
  3572. goto final;
  3573. }
  3574. //
  3575. // remap to LKG directory
  3576. //
  3577. RelativeFilename = FullTargetFilename+wd_len; // includes preceeding backslash
  3578. rf_len = tf_len-wd_len;
  3579. MYASSERT((MAX_PATH+(lkgd_len-wd_len))<=SIZECHARS(BackupTargetFilename));
  3580. lstrcpy(BackupTargetFilename,LastGoodDirectory);
  3581. lstrcpy(BackupTargetFilename+lkgd_len,RelativeFilename);
  3582. //
  3583. // does backup already exist?
  3584. //
  3585. if ((attr=GetFileAttributes(BackupTargetFilename))!=(DWORD)(-1)) {
  3586. //
  3587. // if it does, nothing useful to do.
  3588. //
  3589. rval = NO_ERROR;
  3590. goto final;
  3591. }
  3592. //
  3593. // create intermediate directories as needed
  3594. //
  3595. pSetupMakeSurePathExists(BackupTargetFilename);
  3596. //
  3597. // we need to use a temporary file first, and then move it into place
  3598. // so we don't get in the situation where we write a bad file, reboot
  3599. // and decide to use LKG.
  3600. //
  3601. if(GetTempFileName(LastGoodDirectory, TEXT("TMP"), 0, TempFilename) == 0 ) {
  3602. //
  3603. // if this fails, it could be because we haven't got right permissions
  3604. // non-fatal
  3605. //
  3606. rval = NO_ERROR;
  3607. goto final;
  3608. }
  3609. //
  3610. // after this point, aborts require cleaning up of temporary file
  3611. //
  3612. if (write_delete) {
  3613. //
  3614. // GetTempFileName created an empty place holder
  3615. // ensure it has right attributes
  3616. // before moving into place
  3617. //
  3618. SetFileAttributes(TempFilename,FILE_ATTRIBUTE_HIDDEN);
  3619. } else {
  3620. //
  3621. // copy original to this temporary file
  3622. // apply apropriate permissions
  3623. //
  3624. if(!CopyFile(FullTargetFilename, TempFilename ,FALSE)) {
  3625. //
  3626. // copy failed - non fatal
  3627. //
  3628. goto cleanup;
  3629. }
  3630. }
  3631. //
  3632. // we have a temporary file ready to be moved into place
  3633. // move it to final name, ensuring that while we were doing above, a new file
  3634. // was not already written
  3635. //
  3636. if(!MoveFileEx(TempFilename,BackupTargetFilename,MOVEFILE_WRITE_THROUGH)) {
  3637. //
  3638. // could be that a file with that name now exists, but didn't earlier
  3639. // oh well, clean up the mess and leave gracefully
  3640. //
  3641. goto cleanup;
  3642. }
  3643. if (write_delete) {
  3644. //
  3645. // if we successfully wrote an empty placeholder for a file to be deleted, we need to shadow this file with
  3646. // an entry in registry
  3647. //
  3648. regres = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  3649. REGSTR_PATH_LASTGOOD,
  3650. 0,
  3651. NULL,
  3652. REG_OPTION_NON_VOLATILE,
  3653. KEY_ALL_ACCESS,
  3654. NULL,
  3655. &hKeyLastGood,
  3656. &disposition);
  3657. if (regres == NO_ERROR) {
  3658. //
  3659. // copy string, remapping slash's from '\\' to '/'
  3660. //
  3661. CharPtr = RelativeFilename+1; // after initial '\'
  3662. DestPtr = RegName;
  3663. while(*CharPtr) {
  3664. PCTSTR Next = _tcschr(CharPtr,TEXT('\\'));
  3665. if (!Next) {
  3666. Next = CharPtr + lstrlen(CharPtr);
  3667. }
  3668. if(Next-CharPtr) {
  3669. CopyMemory(DestPtr,CharPtr,(Next-CharPtr)*sizeof(TCHAR));
  3670. DestPtr+=(Next-CharPtr);
  3671. CharPtr = Next;
  3672. }
  3673. if (*CharPtr == TEXT('\\')) {
  3674. *DestPtr = TEXT('/');
  3675. DestPtr++;
  3676. CharPtr++;
  3677. }
  3678. }
  3679. *DestPtr = TEXT('\0');
  3680. //
  3681. // write key, name = modified relative path, value = flags
  3682. //
  3683. LastGoodFlags = LASTGOOD_OPERATION_DELETE;
  3684. regres = RegSetValueEx(hKeyLastGood,
  3685. RegName,
  3686. 0,
  3687. REG_DWORD,
  3688. (PBYTE)&LastGoodFlags,
  3689. sizeof(LastGoodFlags));
  3690. RegCloseKey(hKeyLastGood);
  3691. }
  3692. }
  3693. //
  3694. // ok, now we've populated the LKG directory with this file
  3695. //
  3696. if (is_inf) {
  3697. //
  3698. // if we backed up an INF that's in primary INF directory, we should also backup existing PNF
  3699. // if we're writing an entry to delete INF, we'll always write an entry to delete PNF
  3700. //
  3701. MYASSERT(ExtPart);
  3702. lstrcpy(ExtPart,TEXT(".PNF"));
  3703. if(pSetupDoLastKnownGoodBackup(NULL,
  3704. FullTargetFilename,
  3705. SP_LKG_FLAG_FORCECOPY|SP_LKG_FLAG_DELETEIFNEW|(write_delete?SP_LKG_FLAG_DELETEEXISTING:0),
  3706. LogContext) != NO_ERROR) {
  3707. //
  3708. // should never fail
  3709. //
  3710. MYASSERT(FALSE);
  3711. }
  3712. }
  3713. //
  3714. // done!
  3715. //
  3716. rval = NO_ERROR;
  3717. goto final;
  3718. cleanup:
  3719. //
  3720. // cleanup in the case where we've already created temporary file
  3721. //
  3722. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3723. DeleteFile(TempFilename);
  3724. rval = NO_ERROR;
  3725. final:
  3726. if(LocalLogContext) {
  3727. DeleteLogContext(LocalLogContext);
  3728. }
  3729. return rval;
  3730. #else
  3731. //
  3732. // ANSI - not supported
  3733. //
  3734. return NO_ERROR;
  3735. #endif
  3736. }
  3737. #ifdef UNICODE
  3738. BOOL
  3739. pSetupRestoreLastKnownGoodFile(
  3740. IN PCTSTR TargetFilename,
  3741. IN DWORD Flags,
  3742. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3743. )
  3744. /*++
  3745. Routine Description:
  3746. Restore a single LKG file
  3747. The assumption here is that if this API was called, we detected something
  3748. really bad that needs to be fixed immediately
  3749. Arguments:
  3750. TargetFilename Name of file to restore
  3751. Flags
  3752. LogContext If specified, provides preferred logging context
  3753. Return Value:
  3754. TRUE if file was successfully restored
  3755. --*/
  3756. {
  3757. int wd_len; // windows directory len
  3758. int tf_len; // target file len
  3759. int lkgd_len; // last known good directory len
  3760. int rf_len; // relative file len (including preceeding slash)
  3761. TCHAR FullTargetFilename[MAX_PATH];
  3762. TCHAR BackupTargetFilename[MAX_PATH+14];
  3763. TCHAR TempFilename[MAX_PATH];
  3764. TCHAR TempPathname[MAX_PATH];
  3765. TCHAR RegName[MAX_PATH];
  3766. PCTSTR RelativeFilename;
  3767. PTSTR NamePart = NULL;
  3768. BOOL rflag = FALSE;
  3769. PSETUP_LOG_CONTEXT LocalLogContext = NULL;
  3770. LONG regres;
  3771. HKEY hKeyLastGood;
  3772. PCTSTR CharPtr;
  3773. PTSTR DestPtr;
  3774. DWORD RegType;
  3775. DWORD RegSize;
  3776. DWORD LastGoodFlags = 0;
  3777. if (!LogContext) {
  3778. //
  3779. // LogContext may be obmitted if there's a Queue parameter
  3780. //
  3781. if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
  3782. LogContext = LocalLogContext;
  3783. }
  3784. MYASSERT(LogContext);
  3785. }
  3786. //
  3787. // cannonicalize the Target name so user doesn't do stuff like .../TEMP/../INF
  3788. //
  3789. tf_len = (int)GetFullPathName(TargetFilename,
  3790. MAX_PATH,
  3791. FullTargetFilename,
  3792. &NamePart);
  3793. if (tf_len <= 0 || tf_len > MAX_PATH) {
  3794. //
  3795. // we don't do large paths very well
  3796. //
  3797. goto final;
  3798. }
  3799. wd_len = lstrlen(WindowsDirectory);
  3800. lkgd_len = lstrlen(LastGoodDirectory);
  3801. //
  3802. // see if this file is nested below <<WindowsDirectory>>
  3803. // note that such a file must be at least two characters longer
  3804. //
  3805. if((tf_len <= wd_len)
  3806. || (FullTargetFilename[wd_len] != TEXT('\\'))
  3807. || (_tcsnicmp(WindowsDirectory,FullTargetFilename,wd_len)!=0)) {
  3808. //
  3809. // this file is outside of %windir%, not handled by LKG
  3810. //
  3811. goto final;
  3812. }
  3813. //
  3814. // remap to LKG directory
  3815. //
  3816. RelativeFilename = FullTargetFilename+wd_len; // includes preceeding backslash
  3817. rf_len = tf_len-wd_len;
  3818. MYASSERT((MAX_PATH+(lkgd_len-wd_len))<=SIZECHARS(BackupTargetFilename));
  3819. lstrcpy(BackupTargetFilename,LastGoodDirectory);
  3820. lstrcpy(BackupTargetFilename+lkgd_len,RelativeFilename);
  3821. //
  3822. // does backup already exist?
  3823. //
  3824. if (GetFileAttributes(BackupTargetFilename)==(DWORD)(-1)) {
  3825. //
  3826. // if not, nothing we can do
  3827. //
  3828. goto final;
  3829. }
  3830. //
  3831. // find LKG flags to see what we need to do
  3832. //
  3833. regres = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3834. REGSTR_PATH_LASTGOOD,
  3835. 0,
  3836. KEY_READ,
  3837. &hKeyLastGood);
  3838. if (regres == NO_ERROR) {
  3839. //
  3840. // copy string, remapping slash's from '\\' to '/'
  3841. //
  3842. CharPtr = RelativeFilename+1; // after initial '\'
  3843. DestPtr = RegName;
  3844. while(*CharPtr) {
  3845. PCTSTR Next = _tcschr(CharPtr,TEXT('\\'));
  3846. if (!Next) {
  3847. Next = CharPtr + lstrlen(CharPtr);
  3848. }
  3849. if(Next-CharPtr) {
  3850. CopyMemory(DestPtr,CharPtr,(Next-CharPtr)*sizeof(TCHAR));
  3851. DestPtr+=(Next-CharPtr);
  3852. CharPtr = Next;
  3853. }
  3854. if (*CharPtr == TEXT('\\')) {
  3855. *DestPtr = TEXT('/');
  3856. DestPtr++;
  3857. CharPtr++;
  3858. }
  3859. }
  3860. *DestPtr = TEXT('\0');
  3861. RegSize = sizeof(LastGoodFlags);
  3862. regres = RegQueryValueEx(hKeyLastGood,
  3863. RegName,
  3864. NULL,
  3865. &RegType,
  3866. (PBYTE)&LastGoodFlags,
  3867. &RegSize);
  3868. if((regres != NO_ERROR)
  3869. || (RegType != REG_DWORD)
  3870. || (RegSize != sizeof(DWORD))) {
  3871. //
  3872. // default action is copy
  3873. //
  3874. LastGoodFlags = 0;
  3875. }
  3876. RegCloseKey(hKeyLastGood);
  3877. }
  3878. //
  3879. // base directory of target file
  3880. //
  3881. lstrcpyn(TempPathname, FullTargetFilename, MAX_PATH);
  3882. *((PTSTR)pSetupGetFileTitle(TempPathname)) = TEXT('\0');
  3883. if (LastGoodFlags & LASTGOOD_OPERATION_DELETE) {
  3884. //
  3885. // delete
  3886. //
  3887. if(GetFileAttributes(FullTargetFilename)==(DWORD)(-1)) {
  3888. //
  3889. // already deleted
  3890. //
  3891. rflag = TRUE;
  3892. goto final;
  3893. }
  3894. pSetupExemptFileFromProtection(
  3895. FullTargetFilename,
  3896. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3897. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3898. LogContext,
  3899. NULL
  3900. );
  3901. //
  3902. // try the simple way first
  3903. //
  3904. SetFileAttributes(FullTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3905. if(!DeleteFile(FullTargetFilename)) {
  3906. //
  3907. // can't delete target directly
  3908. //
  3909. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, BackupTargetFilename)) {
  3910. //
  3911. // can't create backup temp, nothing we can do
  3912. //
  3913. goto final;
  3914. }
  3915. //
  3916. // move existing file into a temp backup
  3917. //
  3918. if(!MoveFileEx(FullTargetFilename,BackupTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3919. //
  3920. // this failed too for some reason
  3921. //
  3922. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3923. DeleteFile(BackupTargetFilename);
  3924. goto final;
  3925. }
  3926. //
  3927. // now do something with the bad file
  3928. // we don't care if this fails
  3929. //
  3930. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3931. if(!DeleteFile(BackupTargetFilename)) {
  3932. MoveFileEx(BackupTargetFilename,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
  3933. }
  3934. }
  3935. } else {
  3936. //
  3937. // restore back to LKG file
  3938. //
  3939. //
  3940. // create intermediate directories as needed as part of the restore
  3941. //
  3942. pSetupMakeSurePathExists(FullTargetFilename);
  3943. //
  3944. // create a temporary filename to copy to
  3945. // before moving restored file into place
  3946. //
  3947. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, TempFilename)) {
  3948. //
  3949. // can't create temp, nothing we can do
  3950. //
  3951. goto final;
  3952. }
  3953. if(!CopyFile(BackupTargetFilename,TempFilename,FALSE)) {
  3954. //
  3955. // failed to copy to temporary file
  3956. //
  3957. DeleteFile(TempFilename);
  3958. goto final;
  3959. }
  3960. //
  3961. // simple case, move temporary file over existing file
  3962. //
  3963. pSetupExemptFileFromProtection(
  3964. FullTargetFilename,
  3965. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3966. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3967. LogContext,
  3968. NULL
  3969. );
  3970. SetFileAttributes(FullTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3971. if(!MoveFileEx(TempFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3972. //
  3973. // we failed to overwrite file, need slightly different stratagy
  3974. //
  3975. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, BackupTargetFilename)) {
  3976. //
  3977. // can't create backup temp, nothing we can do
  3978. //
  3979. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3980. DeleteFile(TempFilename);
  3981. goto final;
  3982. }
  3983. //
  3984. // move existing file into a temp backup
  3985. //
  3986. if(!MoveFileEx(FullTargetFilename,BackupTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3987. //
  3988. // this failed too for some reason
  3989. //
  3990. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3991. DeleteFile(BackupTargetFilename);
  3992. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3993. DeleteFile(TempFilename);
  3994. goto final;
  3995. }
  3996. //
  3997. // we moved existing file out of place, now move new file into place
  3998. //
  3999. if(!MoveFileEx(TempFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  4000. //
  4001. // huh? Ok, that failed, try to recover
  4002. //
  4003. MoveFileEx(BackupTargetFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH);
  4004. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  4005. DeleteFile(TempFilename);
  4006. goto final;
  4007. }
  4008. //
  4009. // now do something with the bad file
  4010. // we don't care if this fails
  4011. //
  4012. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  4013. if(!DeleteFile(BackupTargetFilename)) {
  4014. MoveFileEx(BackupTargetFilename,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
  4015. }
  4016. }
  4017. }
  4018. //
  4019. // done!
  4020. //
  4021. rflag = TRUE;
  4022. final:
  4023. if(LocalLogContext) {
  4024. DeleteLogContext(LocalLogContext);
  4025. }
  4026. return rflag;
  4027. }
  4028. #endif
  4029. #ifdef UNICODE
  4030. WINSETUPAPI
  4031. BOOL
  4032. WINAPI
  4033. SetupPrepareQueueForRestoreA(
  4034. IN HSPFILEQ QueueHandle,
  4035. IN PCSTR BackupPath,
  4036. IN DWORD RestoreFlags
  4037. )
  4038. /*++
  4039. See SetupPrepareQueueForRestore
  4040. --*/
  4041. {
  4042. BOOL f;
  4043. DWORD rc;
  4044. PCWSTR UnicodeBackupPath;
  4045. if(BackupPath) {
  4046. rc = pSetupCaptureAndConvertAnsiArg(BackupPath, &UnicodeBackupPath);
  4047. if(rc != NO_ERROR) {
  4048. SetLastError(rc);
  4049. return FALSE;
  4050. }
  4051. } else {
  4052. SetLastError(ERROR_INVALID_PARAMETER);
  4053. return FALSE;
  4054. }
  4055. f = SetupPrepareQueueForRestore(QueueHandle,UnicodeBackupPath,RestoreFlags);
  4056. rc = GetLastError();
  4057. MyFree(UnicodeBackupPath);
  4058. SetLastError(rc);
  4059. return f;
  4060. }
  4061. #else
  4062. WINSETUPAPI
  4063. BOOL
  4064. WINAPI
  4065. SetupPrepareQueueForRestoreW(
  4066. IN HSPFILEQ QueueHandle,
  4067. IN PCWSTR BackupPath,
  4068. IN DWORD RestoreFlags
  4069. )
  4070. /*++
  4071. ANSI stub
  4072. --*/
  4073. {
  4074. UNREFERENCED_PARAMETER(QueueHandle);
  4075. UNREFERENCED_PARAMETER(BackupPath);
  4076. UNREFERENCED_PARAMETER(RestoreFlags);
  4077. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  4078. return(FALSE);
  4079. }
  4080. #endif
  4081. WINSETUPAPI
  4082. BOOL
  4083. WINAPI
  4084. SetupPrepareQueueForRestore(
  4085. IN HSPFILEQ QueueHandle,
  4086. IN PCTSTR BackupPath,
  4087. IN DWORD RestoreFlags
  4088. )
  4089. /*++
  4090. Routine Description:
  4091. Initializes restore directory
  4092. Arguments:
  4093. QueueHandle - file queue to modify
  4094. BackupPath - original backup directory to use for restore
  4095. RestoreFlags - options
  4096. Return Value:
  4097. TRUE if success, else FALSE
  4098. --*/
  4099. {
  4100. BOOL b = TRUE;
  4101. DWORD rc;
  4102. BOOL f = FALSE;
  4103. PSP_FILE_QUEUE Queue = (PSP_FILE_QUEUE)QueueHandle;
  4104. LONG RestorePathID;
  4105. //
  4106. // validate string pointer
  4107. //
  4108. if(!BackupPath) {
  4109. rc = ERROR_INVALID_PARAMETER;
  4110. goto clean;
  4111. }
  4112. //
  4113. // validate flags (currently not implemented)
  4114. //
  4115. if(RestoreFlags) {
  4116. rc = ERROR_INVALID_PARAMETER;
  4117. goto clean;
  4118. }
  4119. //
  4120. // validate QueueHandle
  4121. //
  4122. try {
  4123. if(Queue->Signature != SP_FILE_QUEUE_SIG) {
  4124. b = FALSE;
  4125. }
  4126. } except(EXCEPTION_EXECUTE_HANDLER) {
  4127. b = FALSE;
  4128. }
  4129. if(!b) {
  4130. rc = ERROR_INVALID_HANDLE;
  4131. goto clean;
  4132. }
  4133. try {
  4134. //
  4135. // if a restore point has previously been set, return error
  4136. //
  4137. if(Queue->RestorePathID != -1) {
  4138. rc = ERROR_ALREADY_EXISTS;
  4139. leave;
  4140. }
  4141. RestorePathID = pSetupStringTableAddString(Queue->StringTable,
  4142. (PTSTR)BackupPath ,
  4143. STRTAB_CASE_SENSITIVE);
  4144. if (RestorePathID == -1) {
  4145. rc = ERROR_NOT_ENOUGH_MEMORY;
  4146. leave;
  4147. }
  4148. //
  4149. // done - just need to set the restore-path
  4150. //
  4151. Queue->RestorePathID = RestorePathID;
  4152. WriteLogEntry(Queue->LogContext,
  4153. SETUP_LOG_WARNING,
  4154. MSG_LOG_RESTORE,
  4155. NULL,
  4156. BackupPath
  4157. );
  4158. } except(EXCEPTION_EXECUTE_HANDLER) {
  4159. rc = ERROR_INVALID_DATA;
  4160. }
  4161. f = TRUE;
  4162. rc = NO_ERROR;
  4163. clean:
  4164. //
  4165. // no cleanup required
  4166. //
  4167. SetLastError(rc);
  4168. return f;
  4169. }
  4170. #define SP_TEFLG_BITS_TO_RESET ( SP_TEFLG_SAVED \
  4171. | SP_TEFLG_TEMPNAME \
  4172. | SP_TEFLG_ORIGNAME \
  4173. | SP_TEFLG_MODIFIED \
  4174. | SP_TEFLG_MOVED \
  4175. | SP_TEFLG_BACKUPQUEUE \
  4176. | SP_TEFLG_RESTORED \
  4177. | SP_TEFLG_UNWIND \
  4178. | SP_TEFLG_SKIPPED \
  4179. | SP_TEFLG_INUSE \
  4180. | SP_TEFLG_RENAMEEXISTING )
  4181. BOOL
  4182. pSetupResetTarget(
  4183. IN PVOID StringTable,
  4184. IN LONG StringId,
  4185. IN PCTSTR String, OPTIONAL
  4186. IN PVOID ExtraData,
  4187. IN UINT ExtraDataSize,
  4188. IN LPARAM lParam
  4189. )
  4190. /*++
  4191. Routine Description:
  4192. This routine resets the SP_TARGET_ENT data stored with a string table entry
  4193. in a file queue's TargetLookupTable. This routine may be used as the
  4194. callback function to iterate such entries via pSetupStringTableEnum.
  4195. Arguments:
  4196. StringTable - Supplies a handle to the string table being enumerated
  4197. StringId - Supplies the ID of the current string
  4198. String - Optionally, supplies a pointer to the current string (this will
  4199. always be filled in when this routine is used as a callback for
  4200. pSetupStringTableEnum, but other callers may omit it, as it isn't
  4201. needed).
  4202. ExtraData - Supplies a pointer to the SP_TARGET_ENT data associatd with
  4203. the string
  4204. ExtraDataSize - Supplies the size of the buffer pointed to by ExtraData--
  4205. should always be sizeof(SP_TARGET_ENT)
  4206. lParam - unused
  4207. Return Value:
  4208. This routine always returns TRUE, so that all string entries will be
  4209. enumerated.
  4210. --*/
  4211. {
  4212. PSP_TARGET_ENT TargetInfo;
  4213. BOOL b;
  4214. UNREFERENCED_PARAMETER(String);
  4215. UNREFERENCED_PARAMETER(lParam);
  4216. MYASSERT(ExtraData);
  4217. MYASSERT(ExtraDataSize == sizeof(SP_TARGET_ENT));
  4218. //
  4219. // Clear the bits that will get re-generated when the queue is committed
  4220. // again.
  4221. //
  4222. ((PSP_TARGET_ENT)ExtraData)->InternalFlags &= ~SP_TEFLG_BITS_TO_RESET;
  4223. //
  4224. // Also need to reset the NewTargetFilename
  4225. //
  4226. ((PSP_TARGET_ENT)ExtraData)->NewTargetFilename = -1;
  4227. //
  4228. // Store the modified data back to the string table entry
  4229. //
  4230. b = pSetupStringTableSetExtraData(StringTable,
  4231. StringId,
  4232. ExtraData,
  4233. ExtraDataSize
  4234. );
  4235. //
  4236. // This should never fail
  4237. //
  4238. MYASSERT(b);
  4239. return TRUE;
  4240. }