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

4970 lines
153 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 into our backup
  1838. // we get out CatProblem and szCatFileName
  1839. //
  1840. // if all seems ok, copy file from szCatFileName to backupdir\OriginalCatalogName
  1841. //
  1842. Err = _VerifyFile(
  1843. FileQ->LogContext,
  1844. &(FileQ->hCatAdmin),
  1845. NULL,
  1846. CurrentCatName, // eg "OEMx.CAT"
  1847. NULL,0, // we're not verifying against another catalog image
  1848. OemOrigName, // eg "mydisk.inf"
  1849. szInfFileName, // eg "....\OEMx.INF"
  1850. NULL, // return: problem info
  1851. NULL, // return: problem file
  1852. FALSE, // has to be FALSE because we're getting full path
  1853. ((PSP_FILE_QUEUE)TempQueueHandle)->ValidationPlatform, // alt platform info
  1854. VERIFY_FILE_IGNORE_SELFSIGNED | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK,
  1855. CatSourcePath, // return: catalog file, full path
  1856. NULL, // return: number of catalogs considered
  1857. NULL,
  1858. NULL
  1859. );
  1860. if (Err == NO_ERROR && CatSourcePath[0]) {
  1861. //
  1862. // we have a catalog file of interest to copy
  1863. //
  1864. lstrcpy(CatBackupPath,BackupPath);
  1865. if (!pSetupConcatenatePaths(CatBackupPath, InfOriginalFileInformation.OriginalCatalogName, MAX_PATH, NULL)) {
  1866. //
  1867. // non-fatal
  1868. //
  1869. CatSourcePath[0]=0;
  1870. CatBackupPath[0]=0;
  1871. }
  1872. }
  1873. }
  1874. }
  1875. }
  1876. }
  1877. if (pInfInformation != NULL) {
  1878. MyFree(pInfInformation);
  1879. pInfInformation = NULL;
  1880. }
  1881. }
  1882. if ( pSetupConcatenatePaths(BackupPath, OemOrigName, MAX_PATH, NULL) == FALSE ) {
  1883. Err = ERROR_INVALID_HANDLE;
  1884. goto clean0;
  1885. }
  1886. pSetupMakeSurePathExists(BackupPath);
  1887. SetFileAttributes(BackupPath,FILE_ATTRIBUTE_NORMAL);
  1888. Err = CopyFile(szInfFileName, BackupPath ,FALSE) ? NO_ERROR : GetLastError();
  1889. if (Err != NO_ERROR) {
  1890. goto clean0;
  1891. }
  1892. if(CatSourcePath[0] && CatBackupPath[0]) {
  1893. //
  1894. // if we copied Inf file, try to copy catalog file
  1895. // if we don't succeed, don't consider this a fatal error
  1896. //
  1897. SetFileAttributes(CatBackupPath,FILE_ATTRIBUTE_NORMAL);
  1898. CopyFile(CatSourcePath, CatBackupPath ,FALSE);
  1899. }
  1900. //
  1901. // Add string indicating Backup INF location to file Queue
  1902. // for later retrieval
  1903. //
  1904. BackupInfID = pSetupStringTableAddString(FileQ->StringTable,
  1905. BackupPath,
  1906. STRTAB_CASE_SENSITIVE);
  1907. if (BackupInfID == -1) {
  1908. Err = ERROR_NOT_ENOUGH_MEMORY;
  1909. goto clean0;
  1910. }
  1911. //
  1912. // Save the full inf backup path since we need to put it in the registry
  1913. // below.
  1914. //
  1915. lstrcpy(ReinstallString, BackupPath);
  1916. //
  1917. // Also backup the PNF file
  1918. //
  1919. // WARNING: We reuse the szInfFileName and BackupPath variables at this point
  1920. // so if you add code that needs them then add it above.
  1921. //
  1922. szInfFileNameExt = _tcsrchr(szInfFileName,TEXT('.'));
  1923. MYASSERT(szInfFileNameExt);
  1924. BackupPathExt = _tcsrchr(BackupPath,TEXT('.'));
  1925. MYASSERT(BackupPathExt);
  1926. if (szInfFileNameExt && BackupPathExt) {
  1927. lstrcpy(szInfFileNameExt,pszPnfSuffix);
  1928. lstrcpy(BackupPathExt,pszPnfSuffix);
  1929. SetFileAttributes(BackupPath,FILE_ATTRIBUTE_NORMAL);
  1930. CopyFile(szInfFileName, BackupPath, FALSE);
  1931. }
  1932. //
  1933. // add items we may need to backup
  1934. // (ie the copy queue of TempQueueHandle is converted to a backup queue of FileQueue)
  1935. //
  1936. if ( pSetupBackupAppendFiles(FileQueue, SubDir, BackupFlags, TempQueueHandle) != NO_ERROR ) {
  1937. Err = GetLastError();
  1938. goto clean0;
  1939. }
  1940. //
  1941. // Create the Reinstall registry key that points to the backup directory we
  1942. // just made.
  1943. //
  1944. if (pSetupBackupGetReinstallKeyStrings(FileQ,
  1945. TempInfoSet,
  1946. &TempInfoData,
  1947. DeviceID
  1948. ) != NO_ERROR) {
  1949. Err = GetLastError();
  1950. goto clean0;
  1951. }
  1952. Err = NO_ERROR;
  1953. //
  1954. // update BackupInfID so that INF name can be queried later. We will set
  1955. // these values at the very end since the fact that FileQ->BackupInfID is
  1956. // not -1 means the backup initialization has been successful.
  1957. //
  1958. FileQ->BackupInfID = BackupInfID;
  1959. FileQ->BackupInstanceID = BackupInstanceID;
  1960. //
  1961. // Set the FQF_DEVICE_BACKUP flag so that we know this is a device install
  1962. // backup.
  1963. //
  1964. FileQ->Flags |= FQF_DEVICE_BACKUP;
  1965. clean0:
  1966. //
  1967. // If we encountered an error during our backup initialization then we want
  1968. // to clean out the backup directory and Reinstall subkey.
  1969. //
  1970. if ((Err != NO_ERROR) &&
  1971. (BackupInstanceID != -1)) {
  1972. if (pSetupStringTableStringFromIdEx(FileQ->StringTable,
  1973. BackupInstanceID,
  1974. BackupInstance,
  1975. NULL)) {
  1976. pSetupDeleteBackup(BackupInstance);
  1977. }
  1978. }
  1979. //
  1980. // delete temporary structures used
  1981. //
  1982. if (pDeviceInfoSet != NULL ) {
  1983. UnlockDeviceInfoSet(pDeviceInfoSet);
  1984. }
  1985. if ( TempInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE ) {
  1986. SetupDiDestroyDeviceInfoList(TempInfoSet);
  1987. }
  1988. if ( TempQueueHandle != (HSPFILEQ)INVALID_HANDLE_VALUE ) {
  1989. SetupCloseFileQueue(TempQueueHandle);
  1990. }
  1991. if(ChangedThreadLogContext) {
  1992. //
  1993. // restore thread log context if we changed (cleared) it
  1994. //
  1995. SetThreadLogContext(SavedLogContext,NULL);
  1996. }
  1997. DeleteLogContext(LocalLogContext);
  1998. SetLastError(Err);
  1999. return Err;
  2000. }
  2001. //
  2002. // ==========================================================
  2003. //
  2004. DWORD
  2005. pSetupCompleteBackup(
  2006. IN OUT HSPFILEQ FileQueue
  2007. )
  2008. /*++
  2009. Routine Description:
  2010. This routine is called after we have successfully finished installing a
  2011. device. At this point the new backup that we have created is valid and
  2012. so any old backups that this device was using need to be removed.
  2013. To do this the code will enumerate all of the backup instances under the
  2014. Reinstall key and scan their DeviceInstanceIds multi-sz value. If it finds
  2015. this DeviceInstanceId in the list then it will remove it. If the list is
  2016. empty after this removal then the entire backup instance and its
  2017. cooresponding backup directory will be deleted.
  2018. Arguments:
  2019. FileQueue Backup queue is filled with files that need copying
  2020. Return Value:
  2021. Error Status
  2022. --*/
  2023. {
  2024. PSP_FILE_QUEUE BackupFileQueue = (PSP_FILE_QUEUE)FileQueue;
  2025. HKEY hKeyReinstall;
  2026. HKEY hKeyReinstallInstance;
  2027. DWORD Index;
  2028. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  2029. TCHAR ReinstallInstance[MAX_PATH];
  2030. FILETIME ftLastWriteTime;
  2031. DWORD cbData, cbInstanceSize;
  2032. BOOL bDeleteBackupInstance;
  2033. DWORD Err = NO_ERROR;
  2034. PTSTR DeviceInstanceIdsList, p;
  2035. try {
  2036. //
  2037. // If we don't have a BackupInfID then the backup failed, so don't bother
  2038. // cleaning out the old backup information or creating the new Reinstall
  2039. // instance key.
  2040. //
  2041. if (BackupFileQueue->BackupInfID == -1) {
  2042. Err = ERROR_NO_BACKUP;
  2043. goto clean0;
  2044. }
  2045. //
  2046. // Get the Device Instance Id from the backup queue. This value must be
  2047. // cleaned out from all other Reinstall Instance keys.
  2048. //
  2049. cbData = MAX_PATH;
  2050. if (!pSetupStringTableStringFromIdEx(BackupFileQueue->StringTable,
  2051. BackupFileQueue->BackupDeviceInstanceID,
  2052. DeviceInstanceId,
  2053. &cbData)) {
  2054. if (cbData == 0) {
  2055. Err = ERROR_NO_BACKUP;
  2056. } else {
  2057. Err = ERROR_INSUFFICIENT_BUFFER;
  2058. }
  2059. goto clean0;
  2060. }
  2061. //
  2062. // Open the Reinstall key so we can enumerate all of the instance subkeys.
  2063. //
  2064. Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  2065. pszReinstallPath,
  2066. 0,
  2067. KEY_ALL_ACCESS,
  2068. &hKeyReinstall
  2069. );
  2070. if (Err == ERROR_SUCCESS) {
  2071. cbInstanceSize = sizeof(ReinstallInstance) / sizeof(TCHAR);
  2072. Index = 0;
  2073. while (RegEnumKeyEx(hKeyReinstall,
  2074. Index++,
  2075. ReinstallInstance,
  2076. &cbInstanceSize,
  2077. NULL,
  2078. NULL,
  2079. NULL,
  2080. &ftLastWriteTime
  2081. ) == ERROR_SUCCESS) {
  2082. //
  2083. // Assume that we don't need to delete this backup instance
  2084. //
  2085. bDeleteBackupInstance = FALSE;
  2086. Err = RegOpenKeyEx(hKeyReinstall,
  2087. ReinstallInstance,
  2088. 0,
  2089. KEY_ALL_ACCESS,
  2090. &hKeyReinstallInstance
  2091. );
  2092. if (Err == ERROR_SUCCESS) {
  2093. cbData = 0;
  2094. if ((RegQueryValueEx(hKeyReinstallInstance,
  2095. pszReinstallDeviceInstanceIds,
  2096. NULL,
  2097. NULL,
  2098. NULL,
  2099. &cbData
  2100. ) == ERROR_SUCCESS) &&
  2101. (cbData)) {
  2102. DeviceInstanceIdsList = MyMalloc(cbData + sizeof(TCHAR));
  2103. if (DeviceInstanceIdsList) {
  2104. if (RegQueryValueEx(hKeyReinstallInstance,
  2105. pszReinstallDeviceInstanceIds,
  2106. NULL,
  2107. NULL,
  2108. (LPBYTE)DeviceInstanceIdsList,
  2109. &cbData) == ERROR_SUCCESS) {
  2110. //
  2111. // Walk the list of DeviceInstanceIds and check for
  2112. // a match with our device.
  2113. //
  2114. for (p = DeviceInstanceIdsList;
  2115. *p;
  2116. p += (lstrlen(p) + 1)) {
  2117. if (lstrcmpi(p, DeviceInstanceId) == 0) {
  2118. //
  2119. // We have a match! First we will check to
  2120. // see if this is the only DeviceInstanceId
  2121. // in the list. To do this take the length of
  2122. // the string and add two for the two terminating
  2123. // NULLs of a multi-sz string and compare that
  2124. // to cbData. If it is the same as (or larger
  2125. // than) cbData then this is the only string
  2126. // in the multi-sz list.
  2127. //
  2128. if ((p == DeviceInstanceIdsList) &&
  2129. (((lstrlen(DeviceInstanceIdsList) + 2) * sizeof(TCHAR)) >= cbData)) {
  2130. //
  2131. // Since there is only this one DeviceInstanceId
  2132. // in the list set bDeleteBackupInstance to TRUE
  2133. // so we can delete this entire subkey along
  2134. // with the files that are backed up for it.
  2135. //
  2136. bDeleteBackupInstance = TRUE;
  2137. } else {
  2138. //
  2139. // Since there is more than this DeviceInstanceId
  2140. // in the list we need to remove just this
  2141. // one Id from the multi-sz string and
  2142. // put the new multi-sz string back
  2143. // into the registry.
  2144. //
  2145. DWORD pLength = lstrlen(p);
  2146. PTSTR p2 = p + (pLength + 1);
  2147. memcpy(p, p2, cbData - ((ULONG_PTR)p2 - (ULONG_PTR)DeviceInstanceIdsList));
  2148. RegSetValueEx(hKeyReinstallInstance,
  2149. pszReinstallDeviceInstanceIds,
  2150. 0,
  2151. REG_MULTI_SZ,
  2152. (PBYTE)DeviceInstanceIdsList,
  2153. cbData - ((pLength + 1) * sizeof(TCHAR))
  2154. );
  2155. }
  2156. break;
  2157. }
  2158. }
  2159. }
  2160. MyFree(DeviceInstanceIdsList);
  2161. }
  2162. }
  2163. RegCloseKey(hKeyReinstallInstance);
  2164. //
  2165. // If this entire subkey and it's corresponding directory need
  2166. // to be deleted then do it now.
  2167. //
  2168. if (bDeleteBackupInstance) {
  2169. pSetupDeleteBackup(ReinstallInstance);
  2170. }
  2171. }
  2172. //
  2173. // Need to update the cbInstanceSize variable before calling
  2174. // RegEnumKeyEx again.
  2175. //
  2176. cbInstanceSize = sizeof(ReinstallInstance) / sizeof(TCHAR);
  2177. }
  2178. RegCloseKey(hKeyReinstall);
  2179. }
  2180. //
  2181. // Create the new Reinstall instance backup subkey.
  2182. //
  2183. Err = pSetupBackupCreateReinstallKey(BackupFileQueue);
  2184. clean0: ; // Nothing to do.
  2185. } except(EXCEPTION_EXECUTE_HANDLER) {
  2186. //
  2187. // if we except, assume it's due to invalid parameter
  2188. //
  2189. Err = ERROR_INVALID_PARAMETER;
  2190. }
  2191. SetLastError(Err);
  2192. return Err;
  2193. }
  2194. //
  2195. // ==========================================================
  2196. //
  2197. VOID
  2198. pSetupCleanupBackup(
  2199. IN PSP_FILE_QUEUE Queue
  2200. )
  2201. /*++
  2202. Routine Description:
  2203. This routine is called to delete any backup directories or registry entries
  2204. associated with this queue.
  2205. Arguments:
  2206. Queue file queue
  2207. Return Value:
  2208. VOID
  2209. --*/
  2210. {
  2211. TCHAR BackupInstance[MAX_PATH];
  2212. DWORD cbData;
  2213. //
  2214. // If we don't have a BackupInfID or a BackupInstanceID then the backup
  2215. // must have failed much earlier. If the backup failed then it would have
  2216. // cleaned itself up so there is no cleanup that needs to be done now.
  2217. //
  2218. if ((Queue->BackupInfID == -1) ||
  2219. (Queue->BackupInstanceID == -1)) {
  2220. return;
  2221. }
  2222. //
  2223. // Get the Backup Instance from the backup queue.
  2224. //
  2225. cbData = MAX_PATH;
  2226. if (pSetupStringTableStringFromIdEx(Queue->StringTable,
  2227. Queue->BackupInstanceID,
  2228. BackupInstance,
  2229. &cbData)) {
  2230. pSetupDeleteBackup(BackupInstance);
  2231. }
  2232. }
  2233. //
  2234. // ==========================================================
  2235. //
  2236. BOOL
  2237. PostDelayedMove(
  2238. IN PSP_FILE_QUEUE Queue,
  2239. IN PCTSTR CurrentName,
  2240. IN PCTSTR NewName, OPTIONAL
  2241. IN DWORD SecurityDesc,
  2242. IN BOOL TargetIsProtected
  2243. )
  2244. /*++
  2245. Routine Description:
  2246. Helper for DelayedMove
  2247. We don't do any delayed Moves until we know all else succeeded
  2248. Arguments:
  2249. Queue Queue that the move is applied to
  2250. CurrentName Name of file we want to move
  2251. NewName Name we want to move to
  2252. SecurityDesc Index in string table of Security Descriptor string or -1 if not present
  2253. TargetIsProtected Indicates whether target file is a protected system file
  2254. Return Value:
  2255. FALSE if error
  2256. --*/
  2257. {
  2258. PSP_DELAYMOVE_NODE DelayMoveNode;
  2259. LONG SourceFilename;
  2260. LONG TargetFilename;
  2261. DWORD Err;
  2262. if (CurrentName == NULL) {
  2263. SourceFilename = -1;
  2264. } else {
  2265. SourceFilename = pSetupStringTableAddString(Queue->StringTable,
  2266. (PTSTR)CurrentName,
  2267. STRTAB_CASE_SENSITIVE
  2268. );
  2269. if (SourceFilename == -1) {
  2270. Err = ERROR_NOT_ENOUGH_MEMORY;
  2271. goto clean0;
  2272. }
  2273. }
  2274. if (NewName == NULL) {
  2275. TargetFilename = -1;
  2276. } else {
  2277. TargetFilename = pSetupStringTableAddString(Queue->StringTable,
  2278. (PTSTR)NewName,
  2279. STRTAB_CASE_SENSITIVE
  2280. );
  2281. if (TargetFilename == -1) {
  2282. Err = ERROR_NOT_ENOUGH_MEMORY;
  2283. goto clean0;
  2284. }
  2285. }
  2286. DelayMoveNode = MyMalloc(sizeof(SP_DELAYMOVE_NODE));
  2287. if (DelayMoveNode == NULL) {
  2288. Err = ERROR_NOT_ENOUGH_MEMORY;
  2289. goto clean0;
  2290. }
  2291. DelayMoveNode->NextNode = NULL;
  2292. DelayMoveNode->SourceFilename = SourceFilename;
  2293. DelayMoveNode->TargetFilename = TargetFilename;
  2294. DelayMoveNode->SecurityDesc = SecurityDesc;
  2295. DelayMoveNode->TargetIsProtected = TargetIsProtected;
  2296. if (Queue->DelayMoveQueueTail == NULL) {
  2297. Queue->DelayMoveQueue = DelayMoveNode;
  2298. } else {
  2299. Queue->DelayMoveQueueTail->NextNode = DelayMoveNode;
  2300. }
  2301. Queue->DelayMoveQueueTail = DelayMoveNode;
  2302. Err = NO_ERROR;
  2303. clean0:
  2304. SetLastError(Err);
  2305. return (Err == NO_ERROR);
  2306. }
  2307. //
  2308. // ==========================================================
  2309. //
  2310. DWORD
  2311. DoAllDelayedMoves(
  2312. IN PSP_FILE_QUEUE Queue
  2313. )
  2314. /*++
  2315. Routine Description:
  2316. Execute the Delayed Moves previously posted
  2317. Arguments:
  2318. Queue Queue that has the list in
  2319. Return Value:
  2320. Error Status
  2321. --*/
  2322. {
  2323. PSP_DELAYMOVE_NODE DelayMoveNode;
  2324. PTSTR CurrentName;
  2325. PTSTR TargetName;
  2326. BOOL b = TRUE;
  2327. PSP_DELAYMOVE_NODE DoneQueue = NULL;
  2328. PSP_DELAYMOVE_NODE NextNode = NULL;
  2329. DWORD Err = NO_ERROR;
  2330. BOOL EnableProtectedRenames = FALSE;
  2331. for (DelayMoveNode = Queue->DelayMoveQueue ; DelayMoveNode ; DelayMoveNode = NextNode ) {
  2332. NextNode = DelayMoveNode->NextNode;
  2333. MYASSERT(DelayMoveNode->SourceFilename != -1);
  2334. CurrentName = pSetupStringTableStringFromId(Queue->StringTable, DelayMoveNode->SourceFilename);
  2335. MYASSERT(CurrentName);
  2336. if (DelayMoveNode->TargetFilename == -1) {
  2337. TargetName = NULL;
  2338. } else {
  2339. TargetName = pSetupStringTableStringFromId( Queue->StringTable, DelayMoveNode->TargetFilename );
  2340. MYASSERT(TargetName);
  2341. }
  2342. //
  2343. // Keep track of whether we've encountered any protected system files.
  2344. //
  2345. EnableProtectedRenames |= DelayMoveNode->TargetIsProtected;
  2346. #ifdef UNICODE
  2347. //
  2348. // If this is a move (instead of a delete), then set security (letting
  2349. // SCE know what the file's final name will be.
  2350. //
  2351. if((DelayMoveNode->SecurityDesc != -1) && TargetName) {
  2352. Err = pSetupCallSCE(ST_SCE_RENAME,
  2353. CurrentName,
  2354. Queue,
  2355. TargetName,
  2356. DelayMoveNode->SecurityDesc,
  2357. NULL
  2358. );
  2359. if(Err != NO_ERROR ){
  2360. //
  2361. // If we're on the first delay-move node, then we can abort.
  2362. // However, if we've already processed one or more nodes, then
  2363. // we can't abort--we must simply log an error indicating what
  2364. // happened and keep on going.
  2365. //
  2366. WriteLogEntry(Queue->LogContext,
  2367. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2368. MSG_LOG_DELAYED_MOVE_SCE_FAILED,
  2369. NULL,
  2370. CurrentName,
  2371. TargetName
  2372. );
  2373. WriteLogError(Queue->LogContext,
  2374. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2375. Err
  2376. );
  2377. if(DelayMoveNode == Queue->DelayMoveQueue) {
  2378. //
  2379. // Failure occurred on 1st node--we can abort.
  2380. //
  2381. WriteLogEntry(Queue->LogContext,
  2382. SETUP_LOG_ERROR,
  2383. MSG_LOG_OPERATION_CANCELLED,
  2384. NULL
  2385. );
  2386. break;
  2387. } else {
  2388. //
  2389. // There's no turning back--log an error and keep on going.
  2390. //
  2391. WriteLogEntry(Queue->LogContext,
  2392. SETUP_LOG_ERROR,
  2393. MSG_LOG_ERROR_IGNORED,
  2394. NULL
  2395. );
  2396. Err = NO_ERROR;
  2397. }
  2398. }
  2399. } else
  2400. #endif
  2401. {
  2402. Err = NO_ERROR;
  2403. }
  2404. //
  2405. // finally delay the move
  2406. //
  2407. if(!DelayedMove(CurrentName, TargetName)) {
  2408. Err = GetLastError();
  2409. //
  2410. // Same deal as above with SCE call--if we've already processed one
  2411. // or more delay-move nodes, we can't abort.
  2412. //
  2413. if(TargetName) {
  2414. WriteLogEntry(Queue->LogContext,
  2415. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2416. MSG_LOG_DELAYED_MOVE_FAILED,
  2417. NULL,
  2418. CurrentName,
  2419. TargetName
  2420. );
  2421. } else {
  2422. WriteLogEntry(Queue->LogContext,
  2423. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2424. MSG_LOG_DELAYED_DELETE_FAILED,
  2425. NULL,
  2426. CurrentName
  2427. );
  2428. }
  2429. WriteLogError(Queue->LogContext,
  2430. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2431. Err
  2432. );
  2433. if(DelayMoveNode == Queue->DelayMoveQueue) {
  2434. //
  2435. // Failure occurred on 1st node--we can abort.
  2436. //
  2437. WriteLogEntry(Queue->LogContext,
  2438. SETUP_LOG_ERROR,
  2439. MSG_LOG_OPERATION_CANCELLED,
  2440. NULL
  2441. );
  2442. break;
  2443. } else {
  2444. //
  2445. // There's no turning back--log an error and keep on going.
  2446. //
  2447. WriteLogEntry(Queue->LogContext,
  2448. SETUP_LOG_ERROR,
  2449. MSG_LOG_ERROR_IGNORED,
  2450. NULL
  2451. );
  2452. Err = NO_ERROR;
  2453. }
  2454. }
  2455. //
  2456. // Move node to queue containing nodes that have already been processed
  2457. //
  2458. DelayMoveNode->NextNode = DoneQueue;
  2459. DoneQueue = DelayMoveNode;
  2460. }
  2461. //
  2462. // If we have any replacement of protected system files, then we need to
  2463. // inform session manager so that it allows the replacement to occur upon
  2464. // reboot.
  2465. //
  2466. // NOTE: We don't have to worry about enabling replacement of system files
  2467. // with unsigned (hence, untrusted) versions. We only queue up unsigned
  2468. // system files for replacement if the user was explicitly warned of (and
  2469. // agreed to) the consequences.
  2470. //
  2471. // NTRAID#55485-2000/02/03-jamiehun
  2472. // Protected renames only allows "rename all" or "rename none"
  2473. //
  2474. // the session manager only allows the granularity of "allow all
  2475. // renames" or "allow no renames". If Err != NO_ERROR, then we
  2476. // might want to clear out this flag, but that means we'd negate
  2477. // any renames that were previously allowed. Yuck. So we flip a
  2478. // coin, decide to do nothing, and hope for the best if an error
  2479. // occurred. We have similar situation above -- it's all or
  2480. // nothing.
  2481. //
  2482. if((Err == NO_ERROR) && EnableProtectedRenames) {
  2483. pSetupProtectedRenamesFlag(TRUE);
  2484. }
  2485. //
  2486. // any nodes that are left are dropped
  2487. //
  2488. for ( ; DelayMoveNode ; DelayMoveNode = NextNode ) {
  2489. NextNode = DelayMoveNode->NextNode;
  2490. MyFree(DelayMoveNode);
  2491. }
  2492. Queue->DelayMoveQueue = NULL;
  2493. Queue->DelayMoveQueueTail = NULL;
  2494. //
  2495. // delete all nodes we queue'd
  2496. //
  2497. for ( ; DoneQueue ; DoneQueue = NextNode ) {
  2498. NextNode = DoneQueue->NextNode;
  2499. //
  2500. // done with node
  2501. //
  2502. MyFree(DoneQueue);
  2503. }
  2504. return Err;
  2505. }
  2506. //
  2507. // ==========================================================
  2508. //
  2509. VOID
  2510. pSetupUnwindAll(
  2511. IN PSP_FILE_QUEUE Queue,
  2512. IN BOOL Succeeded
  2513. )
  2514. /*++
  2515. Routine Description:
  2516. Processes the Unwind Queue. If Succeeded is FALSE, restores any data that was backed up
  2517. Arguments:
  2518. Queue Queue to be unwound
  2519. Succeeded Indicates if we should treat the whole operation as succeeded or failed
  2520. Return Value:
  2521. None--this routine should always succeed. (Any file errors encountered
  2522. along the way are logged in the setupapi logfile.)
  2523. --*/
  2524. {
  2525. // if Succeeded, we need to delete Temp files
  2526. // if we didn't succeed, we need to restore backups
  2527. PSP_UNWIND_NODE UnwindNode;
  2528. PSP_UNWIND_NODE ThisNode;
  2529. SP_TARGET_ENT TargetInfo;
  2530. PTSTR BackupFilename;
  2531. PTSTR TargetFilename;
  2532. PTSTR RenamedFilename;
  2533. DWORD Err = NO_ERROR;
  2534. TCHAR TempPath[MAX_PATH];
  2535. PTSTR TempNamePtr;
  2536. TCHAR TempFilename[MAX_PATH];
  2537. BOOL RestoreByRenaming;
  2538. BOOL OkToDeleteBackup;
  2539. try {
  2540. if (Succeeded == FALSE) {
  2541. //
  2542. // we need to restore backups
  2543. //
  2544. WriteLogEntry(
  2545. Queue->LogContext,
  2546. SETUP_LOG_WARNING,
  2547. MSG_LOG_UNWIND,
  2548. NULL);
  2549. for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
  2550. ThisNode = UnwindNode;
  2551. UnwindNode = UnwindNode->NextNode;
  2552. if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
  2553. BackupFilename = NULL;
  2554. TargetFilename = NULL;
  2555. RenamedFilename = NULL;
  2556. // restore backup
  2557. if(!(TargetInfo.InternalFlags & SP_TEFLG_RESTORED)) {
  2558. // get target name
  2559. TargetFilename = pSetupFormFullPath(
  2560. Queue->StringTable,
  2561. TargetInfo.TargetRoot,
  2562. TargetInfo.TargetSubDir,
  2563. TargetInfo.TargetFilename);
  2564. if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
  2565. //
  2566. // Get renamed filename
  2567. //
  2568. RenamedFilename = pSetupStringTableStringFromId(Queue->StringTable,
  2569. TargetInfo.NewTargetFilename
  2570. );
  2571. }
  2572. if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
  2573. //
  2574. // get backup name
  2575. //
  2576. BackupFilename = pSetupFormFullPath(
  2577. Queue->StringTable,
  2578. TargetInfo.BackupRoot,
  2579. TargetInfo.BackupSubDir,
  2580. TargetInfo.BackupFilename);
  2581. }
  2582. }
  2583. if(TargetFilename && (RenamedFilename || BackupFilename)) {
  2584. //
  2585. // We either renamed the original file or we backed it up.
  2586. // We need to put it back.
  2587. //
  2588. RestoreByRenaming = RenamedFilename ? TRUE : FALSE;
  2589. RestoreRenamedOrBackedUpFile(TargetFilename,
  2590. (RestoreByRenaming
  2591. ? RenamedFilename
  2592. : BackupFilename),
  2593. RestoreByRenaming,
  2594. Queue->LogContext
  2595. );
  2596. //
  2597. // If we were doing a copy (i.e., from a backup) as opposed
  2598. // to a rename, then we need to reapply timestamp and
  2599. // security.
  2600. //
  2601. if(!RestoreByRenaming) {
  2602. Err = GetSetFileTimestamp(TargetFilename,
  2603. &(ThisNode->CreateTime),
  2604. &(ThisNode->AccessTime),
  2605. &(ThisNode->WriteTime),
  2606. TRUE
  2607. );
  2608. if(Err != NO_ERROR) {
  2609. //
  2610. // We just blew away the timestamp on the file--log
  2611. // an error entry to that effect.
  2612. //
  2613. WriteLogEntry(Queue->LogContext,
  2614. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2615. MSG_LOG_BACKUP_EXISTING_RESTORE_FILETIME_FAILED,
  2616. NULL,
  2617. TargetFilename
  2618. );
  2619. WriteLogError(Queue->LogContext,
  2620. SETUP_LOG_ERROR,
  2621. Err
  2622. );
  2623. }
  2624. if(ThisNode->SecurityDesc != NULL){
  2625. Err = StampFileSecurity(TargetFilename, ThisNode->SecurityDesc);
  2626. if(Err != NO_ERROR) {
  2627. //
  2628. // We just blew away the existing security on
  2629. // the file--log an error entry to that effect.
  2630. //
  2631. WriteLogEntry(Queue->LogContext,
  2632. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2633. MSG_LOG_BACKUP_EXISTING_RESTORE_SECURITY_FAILED,
  2634. NULL,
  2635. TargetFilename
  2636. );
  2637. WriteLogError(Queue->LogContext,
  2638. SETUP_LOG_ERROR,
  2639. Err
  2640. );
  2641. }
  2642. #ifdef UNICODE
  2643. Err = pSetupCallSCE(ST_SCE_UNWIND,
  2644. TargetFilename,
  2645. NULL,
  2646. NULL,
  2647. -1,
  2648. ThisNode->SecurityDesc
  2649. );
  2650. if(Err != NO_ERROR) {
  2651. //
  2652. // We just blew away the existing security on
  2653. // the file--log an error entry to that effect.
  2654. //
  2655. WriteLogEntry(Queue->LogContext,
  2656. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  2657. MSG_LOG_BACKUP_EXISTING_RESTORE_SCE_FAILED,
  2658. NULL,
  2659. TargetFilename
  2660. );
  2661. WriteLogError(Queue->LogContext,
  2662. SETUP_LOG_ERROR,
  2663. Err
  2664. );
  2665. }
  2666. #endif
  2667. }
  2668. }
  2669. //
  2670. // Now mark that we've restored this file. We'll delete
  2671. // tempfiles later
  2672. //
  2673. TargetInfo.InternalFlags |= SP_TEFLG_RESTORED;
  2674. pSetupBackupSetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo);
  2675. }
  2676. if(BackupFilename) {
  2677. MyFree(BackupFilename);
  2678. }
  2679. if(TargetFilename) {
  2680. MyFree(TargetFilename);
  2681. }
  2682. }
  2683. }
  2684. }
  2685. //
  2686. // cleanup - remove temporary files
  2687. //
  2688. for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
  2689. ThisNode = UnwindNode;
  2690. UnwindNode = UnwindNode->NextNode;
  2691. if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
  2692. // delete temporary file
  2693. if (TargetInfo.InternalFlags & SP_TEFLG_TEMPNAME) {
  2694. //
  2695. // get name of file that was used for backup
  2696. //
  2697. BackupFilename = pSetupFormFullPath(
  2698. Queue->StringTable,
  2699. TargetInfo.BackupRoot,
  2700. TargetInfo.BackupSubDir,
  2701. TargetInfo.BackupFilename);
  2702. if(BackupFilename) {
  2703. //
  2704. // If this operation was a bootfile replacement, then we
  2705. // don't want to delete the backup (if we used the renamed
  2706. // file for the backup as well). A delayed delete will
  2707. // have been queued to get rid of the file after a reboot.
  2708. //
  2709. OkToDeleteBackup = TRUE;
  2710. if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
  2711. //
  2712. // Retrieve the renamed filename to see if it's the
  2713. // same as the backup filename.
  2714. //
  2715. RenamedFilename = pSetupStringTableStringFromId(Queue->StringTable,
  2716. TargetInfo.NewTargetFilename
  2717. );
  2718. if(!lstrcmpi(BackupFilename, RenamedFilename)) {
  2719. OkToDeleteBackup = FALSE;
  2720. }
  2721. }
  2722. if(OkToDeleteBackup) {
  2723. //
  2724. // since it was temporary, delete it
  2725. //
  2726. SetFileAttributes(BackupFilename, FILE_ATTRIBUTE_NORMAL);
  2727. if(!DeleteFile(BackupFilename)) {
  2728. //
  2729. // Alright, see if we can set it up for delayed delete
  2730. // instead.
  2731. //
  2732. if(!DelayedMove(BackupFilename, NULL)) {
  2733. //
  2734. // Oh well, just write a log entry indicating that
  2735. // this file turd was left on the user's disk.
  2736. //
  2737. Err = GetLastError();
  2738. WriteLogEntry(Queue->LogContext,
  2739. SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
  2740. MSG_LOG_BACKUP_DELAYED_DELETE_FAILED,
  2741. NULL,
  2742. BackupFilename
  2743. );
  2744. WriteLogError(Queue->LogContext,
  2745. SETUP_LOG_WARNING,
  2746. Err
  2747. );
  2748. }
  2749. }
  2750. }
  2751. MyFree(BackupFilename);
  2752. }
  2753. }
  2754. pSetupResetTarget(Queue->TargetLookupTable,
  2755. ThisNode->TargetID,
  2756. NULL,
  2757. &TargetInfo,
  2758. sizeof(TargetInfo),
  2759. (LPARAM)0
  2760. );
  2761. }
  2762. // cleanup node
  2763. if (ThisNode->SecurityDesc != NULL) {
  2764. MyFree(ThisNode->SecurityDesc);
  2765. }
  2766. MyFree(ThisNode);
  2767. }
  2768. Queue->UnwindQueue = NULL;
  2769. } except (EXCEPTION_EXECUTE_HANDLER) {
  2770. //
  2771. // should generally not get here
  2772. // unless Queue is invalid
  2773. //
  2774. }
  2775. }
  2776. //
  2777. // ==========================================================
  2778. //
  2779. DWORD _SetupGetBackupInformation(
  2780. IN PSP_FILE_QUEUE Queue,
  2781. OUT PSP_BACKUP_QUEUE_PARAMS_V2 BackupParams
  2782. )
  2783. /*++
  2784. Routine Description:
  2785. Get Backup INF path - Internal version
  2786. Arguments:
  2787. Queue - pointer to queue structure (validated)
  2788. BackupParams OUT - filled with INF file path
  2789. Return Value:
  2790. TRUE if success, else FALSE
  2791. --*/
  2792. {
  2793. //
  2794. // Queue is assumed to have been validated
  2795. // BackupParams is in Native format
  2796. //
  2797. LONG BackupInfID;
  2798. ULONG BufSize = MAX_PATH;
  2799. BOOL b;
  2800. DWORD err = NO_ERROR;
  2801. LPCTSTR filename;
  2802. INT offset;
  2803. BackupInfID = Queue->BackupInfID;
  2804. if (BackupInfID != -1) {
  2805. //
  2806. // get inf from stringtable
  2807. //
  2808. b = pSetupStringTableStringFromIdEx(Queue->StringTable,
  2809. BackupInfID,
  2810. BackupParams->FullInfPath,
  2811. &BufSize);
  2812. if (b == FALSE) {
  2813. if (BufSize == 0) {
  2814. err = ERROR_NO_BACKUP;
  2815. } else {
  2816. err = ERROR_INSUFFICIENT_BUFFER;
  2817. }
  2818. goto Clean0;
  2819. }
  2820. //
  2821. // find index of filename
  2822. //
  2823. filename = pSetupGetFileTitle(BackupParams->FullInfPath);
  2824. offset = (INT)(filename - BackupParams->FullInfPath);
  2825. BackupParams->FilenameOffset = offset;
  2826. //
  2827. // If the caller passed in a SP_BACKUP_QUEUE_PARAMS_V2 structure then
  2828. // also fill in the ReinstallInstance string value.
  2829. //
  2830. if (BackupParams->cbSize >= sizeof(SP_BACKUP_QUEUE_PARAMS_V2)) {
  2831. BufSize = MAX_PATH;
  2832. if(Queue->BackupInstanceID != -1) {
  2833. b = pSetupStringTableStringFromIdEx(Queue->StringTable,
  2834. Queue->BackupInstanceID,
  2835. BackupParams->ReinstallInstance,
  2836. &BufSize);
  2837. } else {
  2838. //
  2839. // no instance ID
  2840. //
  2841. BackupParams->ReinstallInstance[0] = TEXT('\0');
  2842. }
  2843. if (b == FALSE) {
  2844. if (BufSize == 0) {
  2845. err = ERROR_NO_BACKUP;
  2846. } else {
  2847. err = ERROR_INSUFFICIENT_BUFFER;
  2848. }
  2849. goto Clean0;
  2850. }
  2851. }
  2852. } else {
  2853. //
  2854. // no backup path
  2855. //
  2856. err = ERROR_NO_BACKUP;
  2857. }
  2858. Clean0:
  2859. return err;
  2860. }
  2861. #ifdef UNICODE
  2862. //
  2863. // ANSI version in UNICODE
  2864. //
  2865. BOOL
  2866. WINAPI
  2867. SetupGetBackupInformationA(
  2868. IN HSPFILEQ QueueHandle,
  2869. OUT PSP_BACKUP_QUEUE_PARAMS_V2_A BackupParams
  2870. )
  2871. {
  2872. BOOL b;
  2873. int i;
  2874. INT c;
  2875. LPCSTR p;
  2876. SP_BACKUP_QUEUE_PARAMS_W BackupParamsW;
  2877. //
  2878. // confirm structure size
  2879. //
  2880. try {
  2881. if((BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V2_A)) &&
  2882. (BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V1_A))) {
  2883. SetLastError(ERROR_INVALID_PARAMETER);
  2884. b = FALSE;
  2885. leave; // exit try block
  2886. }
  2887. //
  2888. // call Unicode version of API
  2889. //
  2890. ZeroMemory( &BackupParamsW, sizeof(BackupParamsW) );
  2891. BackupParamsW.cbSize = sizeof(BackupParamsW);
  2892. b = SetupGetBackupInformationW(QueueHandle,&BackupParamsW);
  2893. if (b) {
  2894. //
  2895. // success, convert structure from UNICODE to ANSI
  2896. //
  2897. i = WideCharToMultiByte(
  2898. CP_ACP,
  2899. 0,
  2900. BackupParamsW.FullInfPath,
  2901. MAX_PATH,
  2902. BackupParams->FullInfPath,
  2903. MAX_PATH,
  2904. NULL,
  2905. NULL
  2906. );
  2907. if (i==0) {
  2908. //
  2909. // error occurred (LastError set to error)
  2910. //
  2911. b = FALSE;
  2912. leave; // exit try block
  2913. }
  2914. //
  2915. // we need to recalc the offset of INF filename
  2916. // taking care of internationalization
  2917. //
  2918. p = BackupParams->FullInfPath;
  2919. for(c = 0; c < BackupParamsW.FilenameOffset; c++) {
  2920. p = CharNextA(p);
  2921. }
  2922. BackupParams->FilenameOffset = (int)(p-(BackupParams->FullInfPath)); // new offset in ANSI
  2923. if (BackupParams->cbSize >= sizeof(SP_BACKUP_QUEUE_PARAMS_V2_A)) {
  2924. //
  2925. // instance
  2926. //
  2927. i = WideCharToMultiByte(
  2928. CP_ACP,
  2929. 0,
  2930. BackupParamsW.ReinstallInstance,
  2931. MAX_PATH,
  2932. BackupParams->ReinstallInstance,
  2933. MAX_PATH,
  2934. NULL,
  2935. NULL
  2936. );
  2937. if (i==0) {
  2938. //
  2939. // error occurred (LastError set to error)
  2940. //
  2941. b = FALSE;
  2942. leave; // exit try block
  2943. }
  2944. }
  2945. }
  2946. } except(EXCEPTION_EXECUTE_HANDLER) {
  2947. //
  2948. // if we except, assume it's due to invalid parameter
  2949. //
  2950. SetLastError(ERROR_INVALID_PARAMETER);
  2951. b = FALSE;
  2952. }
  2953. return b;
  2954. }
  2955. #else
  2956. //
  2957. // Unicode version in ANSI
  2958. //
  2959. BOOL
  2960. WINAPI
  2961. SetupGetBackupInformationW(
  2962. IN HSPFILEQ QueueHandle,
  2963. OUT PSP_BACKUP_QUEUE_PARAMS_V2_W BackupParams
  2964. )
  2965. {
  2966. UNREFERENCED_PARAMETER(QueueHandle);
  2967. UNREFERENCED_PARAMETER(BackupParams);
  2968. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  2969. return(FALSE);
  2970. }
  2971. #endif
  2972. //
  2973. // Native version
  2974. //
  2975. BOOL
  2976. WINAPI
  2977. SetupGetBackupInformation(
  2978. IN HSPFILEQ QueueHandle,
  2979. OUT PSP_BACKUP_QUEUE_PARAMS_V2 BackupParams
  2980. )
  2981. /*++
  2982. Routine Description:
  2983. Get Backup INF path
  2984. Arguments:
  2985. QueueHandle - handle of queue to retrieve backup INF file from
  2986. BackupParams - IN - has cbSize set, OUT - filled with INF file path
  2987. Return Value:
  2988. TRUE if success, else FALSE
  2989. --*/
  2990. {
  2991. BOOL b = TRUE;
  2992. PSP_FILE_QUEUE Queue = (PSP_FILE_QUEUE)QueueHandle;
  2993. DWORD res;
  2994. //
  2995. // first validate QueueHandle
  2996. //
  2997. try {
  2998. if(Queue->Signature != SP_FILE_QUEUE_SIG) {
  2999. b = FALSE;
  3000. }
  3001. } except(EXCEPTION_EXECUTE_HANDLER) {
  3002. b = FALSE;
  3003. }
  3004. if(!b) {
  3005. SetLastError(ERROR_INVALID_HANDLE);
  3006. goto Clean0;
  3007. }
  3008. //
  3009. // now fill in structure
  3010. // if we except, assume bad pointer
  3011. //
  3012. try {
  3013. if((BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V2)) &&
  3014. (BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_V1))) {
  3015. SetLastError(ERROR_INVALID_PARAMETER);
  3016. b = FALSE;
  3017. leave; // exit try block
  3018. }
  3019. res = _SetupGetBackupInformation(Queue,BackupParams);
  3020. if (res == NO_ERROR) {
  3021. b = TRUE;
  3022. } else {
  3023. SetLastError(res);
  3024. b = FALSE;
  3025. }
  3026. } except(EXCEPTION_EXECUTE_HANDLER) {
  3027. //
  3028. // if we except, assume it's due to invalid parameter
  3029. //
  3030. SetLastError(ERROR_INVALID_PARAMETER);
  3031. b = FALSE;
  3032. }
  3033. Clean0:
  3034. return b;
  3035. }
  3036. //
  3037. // ==========================================================
  3038. //
  3039. VOID
  3040. RestoreRenamedOrBackedUpFile(
  3041. IN PCTSTR TargetFilename,
  3042. IN PCTSTR CurrentFilename,
  3043. IN BOOL RenameFile,
  3044. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3045. )
  3046. /*++
  3047. Routine Description:
  3048. This routine does its best to restore a backed-up or renamed file back to
  3049. its original name.
  3050. Arguments:
  3051. TargetFilename - filename to be restored to
  3052. CurrentFilename - file to restore
  3053. RenameFile - if TRUE, CurrentFilename was previously renamed from
  3054. TargetFilename (hence should be renamed back). If FALSE,
  3055. CurrentFilename is merely a copy, and should be copied back.
  3056. LogContext - supplies a log context used if errors are encountered.
  3057. Return Value:
  3058. None.
  3059. --*/
  3060. {
  3061. DWORD Err;
  3062. TCHAR TempPath[MAX_PATH];
  3063. PTSTR TempNamePtr;
  3064. TCHAR TempFilename[MAX_PATH];
  3065. DWORD LogTag = AllocLogInfoSlotOrLevel(LogContext,SETUP_LOG_INFO,FALSE);
  3066. WriteLogEntry(
  3067. LogContext,
  3068. LogTag,
  3069. MSG_LOG_UNWIND_FILE,
  3070. NULL,
  3071. CurrentFilename,
  3072. TargetFilename
  3073. );
  3074. //
  3075. // First, clear target attributes...
  3076. //
  3077. SetFileAttributes(TargetFilename, FILE_ATTRIBUTE_NORMAL);
  3078. if(RenameFile) {
  3079. //
  3080. // simple case, move temporary file over existing file
  3081. //
  3082. pSetupExemptFileFromProtection(
  3083. TargetFilename,
  3084. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3085. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3086. LogContext,
  3087. NULL
  3088. );
  3089. Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
  3090. } else {
  3091. pSetupExemptFileFromProtection(
  3092. TargetFilename,
  3093. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3094. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3095. LogContext,
  3096. NULL
  3097. );
  3098. Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
  3099. }
  3100. if(Err != NO_ERROR) {
  3101. //
  3102. // Can't replace the file that got copied in place of
  3103. // the original one--try to move that one to a tempname
  3104. // and schedule it for delayed deletion.
  3105. //
  3106. WriteLogEntry(LogContext,
  3107. SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
  3108. MSG_LOG_UNWIND_TRY1_FAILED,
  3109. NULL,
  3110. CurrentFilename,
  3111. TargetFilename
  3112. );
  3113. WriteLogError(LogContext,
  3114. SETUP_LOG_ERROR,
  3115. Err
  3116. );
  3117. //
  3118. // First, strip the filename off the path.
  3119. //
  3120. _tcscpy(TempPath, TargetFilename);
  3121. TempNamePtr = (PTSTR)pSetupGetFileTitle(TempPath);
  3122. *TempNamePtr = TEXT('\0');
  3123. //
  3124. // Now get a temp filename within that directory...
  3125. //
  3126. if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
  3127. //
  3128. // Uh oh!
  3129. //
  3130. Err = GetLastError();
  3131. } else if(!DoMove(TargetFilename, TempFilename)) {
  3132. Err = GetLastError();
  3133. } else {
  3134. //
  3135. // OK, we were able to rename the current file to a
  3136. // temp filename--now attempt to copy or move the
  3137. // original file back to its original name.
  3138. //
  3139. if(RenameFile) {
  3140. Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
  3141. } else {
  3142. Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
  3143. }
  3144. if(Err != NO_ERROR) {
  3145. //
  3146. // This is very bad--put the current file back (it's probably
  3147. // better to have something than nothing at all).
  3148. //
  3149. DoMove(TempFilename, TargetFilename);
  3150. }
  3151. }
  3152. if(Err == NO_ERROR) {
  3153. //
  3154. // We successfully moved the current file to a temp
  3155. // filename, and put the original file back. Now
  3156. // queue a delayed delete for the temp file.
  3157. //
  3158. if(!DelayedMove(TempFilename, NULL)) {
  3159. //
  3160. // All this means is that a file turd will get
  3161. // left on the disk--simply log an event about
  3162. // this.
  3163. //
  3164. Err = GetLastError();
  3165. WriteLogEntry(LogContext,
  3166. SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
  3167. MSG_LOG_RENAME_EXISTING_DELAYED_DELETE_FAILED,
  3168. NULL,
  3169. TargetFilename,
  3170. TempFilename
  3171. );
  3172. WriteLogError(LogContext,
  3173. SETUP_LOG_WARNING,
  3174. Err
  3175. );
  3176. }
  3177. } else {
  3178. //
  3179. // We were unable to put the original file back--we
  3180. // can't fail, so just log an error about this and
  3181. // keep on going.
  3182. //
  3183. // in the case of a backed-up file,
  3184. // we might get away with queueing the original file
  3185. // for a delayed rename and then prompting the user
  3186. // to reboot. However, that won't work for renamed
  3187. // files, because they're typically needed very
  3188. // early on in the boot (i.e., before session
  3189. // manager has had a chance to process the delayed
  3190. // rename operations).
  3191. //
  3192. WriteLogEntry(LogContext,
  3193. SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
  3194. (RenameFile
  3195. ? MSG_LOG_RENAME_EXISTING_RESTORE_FAILED
  3196. : MSG_LOG_BACKUP_EXISTING_RESTORE_FAILED),
  3197. NULL,
  3198. CurrentFilename,
  3199. TargetFilename
  3200. );
  3201. WriteLogError(LogContext,
  3202. SETUP_LOG_ERROR,
  3203. Err
  3204. );
  3205. }
  3206. }
  3207. if (LogTag) {
  3208. ReleaseLogInfoSlot(LogContext,LogTag);
  3209. }
  3210. }
  3211. //
  3212. // ==========================================================
  3213. //
  3214. BOOL
  3215. UnPostDelayedMove(
  3216. IN PSP_FILE_QUEUE Queue,
  3217. IN PCTSTR CurrentName,
  3218. IN PCTSTR NewName OPTIONAL
  3219. )
  3220. /*++
  3221. Routine Description:
  3222. Locates a delay-move node (either for rename or delete), and removes it
  3223. from the delay-move queue.
  3224. Arguments:
  3225. Queue Queue that the move was applied to
  3226. CurrentName Name of file to be moved
  3227. NewName Name to move file to (NULL if delayed-delete)
  3228. Return Value:
  3229. If successful, the return value is TRUE, otherwise it is FALSE.
  3230. --*/
  3231. {
  3232. PSP_DELAYMOVE_NODE CurNode, PrevNode;
  3233. PCTSTR SourceFilename, TargetFilename;
  3234. //
  3235. // Since the path string IDs in the delay-move nodes are case-sensitive, we
  3236. // don't attempt to match on ID. We instead retrieve the strings, and do
  3237. // case-insensitive string compares. Since this routine is rarely used, the
  3238. // performance hit isn't a big deal.
  3239. //
  3240. for(CurNode = Queue->DelayMoveQueue, PrevNode = NULL;
  3241. CurNode;
  3242. PrevNode = CurNode, CurNode = CurNode->NextNode) {
  3243. if(NewName) {
  3244. //
  3245. // We're searching for a delayed rename, so we must pay attention
  3246. // to the target filename.
  3247. //
  3248. if(CurNode->TargetFilename == -1) {
  3249. continue;
  3250. } else {
  3251. TargetFilename = pSetupStringTableStringFromId(Queue->StringTable, CurNode->TargetFilename);
  3252. MYASSERT(TargetFilename);
  3253. if(lstrcmpi(NewName, TargetFilename)) {
  3254. //
  3255. // Target filenames differ--move on.
  3256. //
  3257. continue;
  3258. }
  3259. }
  3260. } else {
  3261. //
  3262. // We're searching for a delayed delete.
  3263. //
  3264. if(CurNode->TargetFilename != -1) {
  3265. //
  3266. // This is a rename, not a delete--move on.
  3267. //
  3268. continue;
  3269. }
  3270. }
  3271. //
  3272. // If we get to here, then the target filenames match (if this is a
  3273. // rename), or they're both empty (if it's a delete). Now compare the
  3274. // source filenames.
  3275. //
  3276. MYASSERT(CurNode->SourceFilename != -1);
  3277. SourceFilename = pSetupStringTableStringFromId(Queue->StringTable, CurNode->SourceFilename);
  3278. MYASSERT(SourceFilename);
  3279. if(lstrcmpi(CurrentName, SourceFilename)) {
  3280. //
  3281. // Source filenames differ--move on.
  3282. //
  3283. continue;
  3284. } else {
  3285. //
  3286. // We have a match--remove the node from the delay-move queue.
  3287. //
  3288. if(PrevNode) {
  3289. PrevNode->NextNode = CurNode->NextNode;
  3290. } else {
  3291. Queue->DelayMoveQueue = CurNode->NextNode;
  3292. }
  3293. if(!CurNode->NextNode) {
  3294. MYASSERT(Queue->DelayMoveQueueTail == CurNode);
  3295. Queue->DelayMoveQueueTail = PrevNode;
  3296. }
  3297. MyFree(CurNode);
  3298. return TRUE;
  3299. }
  3300. }
  3301. //
  3302. // We didn't find a match.
  3303. //
  3304. return FALSE;
  3305. }
  3306. DWORD
  3307. pSetupDoLastKnownGoodBackup(
  3308. IN struct _SP_FILE_QUEUE *Queue, OPTIONAL
  3309. IN PCTSTR TargetFilename,
  3310. IN DWORD Flags,
  3311. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3312. )
  3313. /*++
  3314. Routine Description:
  3315. Process LastKnownGood backups into <<LastGoodDirectory>>.
  3316. If files are to be deleted on restore, write apropriate flags to HKLM\System\LastGoodRecovery\LastGood\<path/file>
  3317. Caviats:
  3318. If file is not inside <<WindowsDirectory>> or sub-directory, exit no-error.
  3319. Backup will not occur if PSPGF_NO_BACKUP is set.
  3320. If !SP_LKG_FLAG_FORCECOPY
  3321. If file is inside <<LastGoodDirectory>>, exit with error.
  3322. If file is inside <<InfDirectory>> throw warnings
  3323. If an INF inside of <<InfDirectory>> is backed up, it's PNF is backed up too.
  3324. If backup fails, we don't abort copy.
  3325. Arguments:
  3326. Queue Queue (optional) if specified, flags will be checked
  3327. TargetFilename Name of file to backup
  3328. Flags
  3329. SP_LKG_FLAG_FORCECOPY - if set, turns copy safety-guards off
  3330. SP_LKG_FLAG_DELETEIFNEW - if set, writes a delete entry for new files
  3331. SP_LKG_FLAG_DELETEEXISTING - if set, writes a delete entry for existing files
  3332. SP_LKG_FLAG_DELETEOP - if set, primary operation is trying to delete/rename file
  3333. LogContext If specified, provides preferred logging context
  3334. Return Value:
  3335. error if operation should be aborted, NO_ERROR otherwise.
  3336. --*/
  3337. {
  3338. #ifdef UNICODE
  3339. int wd_len; // windows directory len
  3340. int tf_len; // target file len
  3341. int id_len; // inf directory len
  3342. int lkgd_len; // last known good directory len
  3343. int rf_len; // relative file len (including preceeding slash)
  3344. BOOL is_inf = FALSE;
  3345. BOOL is_infdir = FALSE;
  3346. BOOL write_delete = FALSE;
  3347. BOOL no_copy = FALSE;
  3348. BOOL source_exists = FALSE;
  3349. BOOL target_exists = FALSE;
  3350. TCHAR FullTargetFilename[MAX_PATH];
  3351. TCHAR BackupTargetFilename[MAX_PATH+14];
  3352. TCHAR TempFilename[MAX_PATH];
  3353. TCHAR RegName[MAX_PATH];
  3354. PCTSTR RelativeFilename;
  3355. PCTSTR CharPtr;
  3356. PTSTR DestPtr;
  3357. PTSTR NamePart = NULL;
  3358. PTSTR ExtPart = NULL;
  3359. DWORD attr;
  3360. HANDLE hFile;
  3361. HKEY hKeyLastGood;
  3362. DWORD disposition;
  3363. LONG regres;
  3364. DWORD LastGoodFlags = 0;
  3365. DWORD rval = NO_ERROR;
  3366. PSETUP_LOG_CONTEXT LocalLogContext = NULL;
  3367. if (!LogContext) {
  3368. //
  3369. // LogContext may be obmitted if there's a Queue parameter
  3370. //
  3371. if (Queue && Queue->LogContext) {
  3372. LogContext = Queue->LogContext;
  3373. } else {
  3374. if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
  3375. LogContext = LocalLogContext;
  3376. }
  3377. }
  3378. MYASSERT(LogContext);
  3379. }
  3380. if ((GlobalSetupFlags & PSPGF_NO_BACKUP)!=0) {
  3381. //
  3382. // in a scenario where (1) we trust what we're doing and
  3383. // (2) what we're doing modifies lots of files
  3384. // or (3) what we're doing is undoable (eg, upgrading OS)
  3385. //
  3386. no_copy = TRUE;
  3387. }
  3388. #if 0
  3389. else if (Queue && !(Queue->Flags & FQF_DEVICE_INSTALL)) {
  3390. //
  3391. // in this scenario, a queue was specified, but it's not marked
  3392. // for device install
  3393. // we're not interested in this case
  3394. //
  3395. no_copy = TRUE;
  3396. }
  3397. #endif
  3398. //
  3399. // cannonicalize the Target name so user doesn't do stuff like .../TEMP/../INF
  3400. //
  3401. tf_len = (int)GetFullPathName(TargetFilename,
  3402. MAX_PATH,
  3403. FullTargetFilename,
  3404. &NamePart);
  3405. if (tf_len <= 0 || tf_len > MAX_PATH) {
  3406. //
  3407. // we don't do large paths very well
  3408. //
  3409. rval = NO_ERROR;
  3410. goto final;
  3411. }
  3412. wd_len = lstrlen(WindowsDirectory);
  3413. lkgd_len = lstrlen(LastGoodDirectory);
  3414. id_len = lstrlen(InfDirectory);
  3415. //
  3416. // see if this file is nested below <<WindowsDirectory>>
  3417. // note that such a file must be at least two characters longer
  3418. //
  3419. if((tf_len <= wd_len)
  3420. || (FullTargetFilename[wd_len] != TEXT('\\'))
  3421. || (_tcsnicmp(WindowsDirectory,FullTargetFilename,wd_len)!=0)) {
  3422. //
  3423. // this file is outside of %windir%, not handled by LKG
  3424. //
  3425. rval = NO_ERROR;
  3426. goto final;
  3427. }
  3428. if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3429. //
  3430. // sanity check for files being copied into LKG dir
  3431. //
  3432. if((tf_len > lkgd_len)
  3433. && (FullTargetFilename[lkgd_len] == TEXT('\\'))
  3434. && (_tcsnicmp(LastGoodDirectory,FullTargetFilename,lkgd_len)==0)) {
  3435. //
  3436. // this file is prefixed by LastGoodDirectory
  3437. // not allowed - throw a log message and inform caller of this mistake
  3438. // return FALSE to abort the operation
  3439. //
  3440. WriteLogEntry(LogContext,
  3441. SETUP_LOG_ERROR,
  3442. MSG_LOG_FILE_BLOCK,
  3443. NULL,
  3444. FullTargetFilename,
  3445. LastGoodDirectory
  3446. );
  3447. rval = ERROR_ACCESS_DENIED;
  3448. goto final;
  3449. }
  3450. }
  3451. if((tf_len > id_len)
  3452. && (FullTargetFilename[id_len] == TEXT('\\'))
  3453. && (_tcsnicmp(InfDirectory,FullTargetFilename,id_len)==0)
  3454. && ((NamePart-FullTargetFilename) == (id_len+1))) {
  3455. //
  3456. // the file sits in the primary INF directory
  3457. //
  3458. is_infdir = TRUE;
  3459. //
  3460. // check for name ending in ".INF" - if so, we need to backup ".PNF" too
  3461. //
  3462. ExtPart = FullTargetFilename+tf_len;
  3463. while ((ExtPart = CharPrev(NamePart,ExtPart)) != NamePart) {
  3464. if (ExtPart[0] == TEXT('.')) {
  3465. break;
  3466. }
  3467. }
  3468. if(lstrcmpi(ExtPart,TEXT(".INF"))==0) {
  3469. //
  3470. // ends in .INF
  3471. //
  3472. is_inf = TRUE;
  3473. //
  3474. // we should only get here if Force is set (ie, we've already determined
  3475. // what is being copied and all is OK). If we don't, this implies someone
  3476. // is trying a back-door INF copy. we've already logged above that they're writing
  3477. // to this directory when they shouldn't. However, if we don't do anything
  3478. // about it, culprit could render machine in bad state
  3479. // change this behavior into a "Force" behavior
  3480. //
  3481. if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3482. no_copy = FALSE; // ensure we'll go through copy logic
  3483. WriteLogEntry(LogContext,
  3484. SETUP_LOG_ERROR,
  3485. MSG_LOG_INF_WARN,
  3486. NULL,
  3487. FullTargetFilename,
  3488. InfDirectory
  3489. );
  3490. if(!(Flags&SP_LKG_FLAG_DELETEOP)) {
  3491. //
  3492. // we're invalidly trying to overwrite an INF/create an INF
  3493. //
  3494. Flags|=SP_LKG_FLAG_DELETEIFNEW;
  3495. }
  3496. }
  3497. } else if (!(Flags&SP_LKG_FLAG_FORCECOPY)) {
  3498. //
  3499. // writing something else into this directory - huh?
  3500. // well, if it's not an INF, we won't pick it up via INF searching
  3501. // if it's a PNF or cache, we're regenerate it
  3502. // don't fret too much, but slap wrist.
  3503. //
  3504. WriteLogEntry(LogContext,
  3505. SETUP_LOG_ERROR,
  3506. MSG_LOG_FILE_WARN,
  3507. NULL,
  3508. FullTargetFilename,
  3509. InfDirectory
  3510. );
  3511. }
  3512. }
  3513. if (no_copy) {
  3514. //
  3515. // we determined that we're not going to backup, and we've now done logging items
  3516. //
  3517. rval = NO_ERROR;
  3518. goto final;
  3519. }
  3520. //
  3521. // does source really exist?
  3522. //
  3523. if ((attr=GetFileAttributes(FullTargetFilename))!=(DWORD)(-1)) {
  3524. source_exists = TRUE;
  3525. if (Flags & SP_LKG_FLAG_DELETEEXISTING) {
  3526. write_delete = TRUE;
  3527. }
  3528. } else if (Flags & SP_LKG_FLAG_DELETEIFNEW) {
  3529. write_delete = TRUE;
  3530. } else {
  3531. //
  3532. // we're done
  3533. //
  3534. rval = NO_ERROR;
  3535. goto final;
  3536. }
  3537. //
  3538. // remap to LKG directory
  3539. //
  3540. RelativeFilename = FullTargetFilename+wd_len; // includes preceeding backslash
  3541. rf_len = tf_len-wd_len;
  3542. MYASSERT((MAX_PATH+(lkgd_len-wd_len))<=SIZECHARS(BackupTargetFilename));
  3543. lstrcpy(BackupTargetFilename,LastGoodDirectory);
  3544. lstrcpy(BackupTargetFilename+lkgd_len,RelativeFilename);
  3545. //
  3546. // does backup already exist?
  3547. //
  3548. if ((attr=GetFileAttributes(BackupTargetFilename))!=(DWORD)(-1)) {
  3549. //
  3550. // if it does, nothing useful to do.
  3551. //
  3552. rval = NO_ERROR;
  3553. goto final;
  3554. }
  3555. //
  3556. // create intermediate directories as needed
  3557. //
  3558. pSetupMakeSurePathExists(BackupTargetFilename);
  3559. //
  3560. // we need to use a temporary file first, and then move it into place
  3561. // so we don't get in the situation where we write a bad file, reboot
  3562. // and decide to use LKG.
  3563. //
  3564. if(GetTempFileName(LastGoodDirectory, TEXT("TMP"), 0, TempFilename) == 0 ) {
  3565. //
  3566. // if this fails, it could be because we haven't got right permissions
  3567. // non-fatal
  3568. //
  3569. rval = NO_ERROR;
  3570. goto final;
  3571. }
  3572. //
  3573. // after this point, aborts require cleaning up of temporary file
  3574. //
  3575. if (write_delete) {
  3576. //
  3577. // GetTempFileName created an empty place holder
  3578. // ensure it has right attributes
  3579. // before moving into place
  3580. //
  3581. SetFileAttributes(TempFilename,FILE_ATTRIBUTE_HIDDEN);
  3582. } else {
  3583. //
  3584. // copy original to this temporary file
  3585. // apply apropriate permissions
  3586. //
  3587. if(!CopyFile(FullTargetFilename, TempFilename ,FALSE)) {
  3588. //
  3589. // copy failed - non fatal
  3590. //
  3591. goto cleanup;
  3592. }
  3593. }
  3594. //
  3595. // we have a temporary file ready to be moved into place
  3596. // move it to final name, ensuring that while we were doing above, a new file
  3597. // was not already written
  3598. //
  3599. if(!MoveFileEx(TempFilename,BackupTargetFilename,MOVEFILE_WRITE_THROUGH)) {
  3600. //
  3601. // could be that a file with that name now exists, but didn't earlier
  3602. // oh well, clean up the mess and leave gracefully
  3603. //
  3604. goto cleanup;
  3605. }
  3606. if (write_delete) {
  3607. //
  3608. // if we successfully wrote an empty placeholder for a file to be deleted, we need to shadow this file with
  3609. // an entry in registry
  3610. //
  3611. regres = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  3612. REGSTR_PATH_LASTGOOD,
  3613. 0,
  3614. NULL,
  3615. REG_OPTION_NON_VOLATILE,
  3616. KEY_ALL_ACCESS,
  3617. NULL,
  3618. &hKeyLastGood,
  3619. &disposition);
  3620. if (regres == NO_ERROR) {
  3621. //
  3622. // copy string, remapping slash's from '\\' to '/'
  3623. //
  3624. CharPtr = RelativeFilename+1; // after initial '\'
  3625. DestPtr = RegName;
  3626. while(*CharPtr) {
  3627. PCTSTR Next = _tcschr(CharPtr,TEXT('\\'));
  3628. if (!Next) {
  3629. Next = CharPtr + lstrlen(CharPtr);
  3630. }
  3631. if(Next-CharPtr) {
  3632. CopyMemory(DestPtr,CharPtr,(Next-CharPtr)*sizeof(TCHAR));
  3633. DestPtr+=(Next-CharPtr);
  3634. CharPtr = Next;
  3635. }
  3636. if (*CharPtr == TEXT('\\')) {
  3637. *DestPtr = TEXT('/');
  3638. DestPtr++;
  3639. CharPtr++;
  3640. }
  3641. }
  3642. *DestPtr = TEXT('\0');
  3643. //
  3644. // write key, name = modified relative path, value = flags
  3645. //
  3646. LastGoodFlags = LASTGOOD_OPERATION_DELETE;
  3647. regres = RegSetValueEx(hKeyLastGood,
  3648. RegName,
  3649. 0,
  3650. REG_DWORD,
  3651. (PBYTE)&LastGoodFlags,
  3652. sizeof(LastGoodFlags));
  3653. RegCloseKey(hKeyLastGood);
  3654. }
  3655. }
  3656. //
  3657. // ok, now we've populated the LKG directory with this file
  3658. //
  3659. if (is_inf) {
  3660. //
  3661. // if we backed up an INF that's in primary INF directory, we should also backup existing PNF
  3662. // if we're writing an entry to delete INF, we'll always write an entry to delete PNF
  3663. //
  3664. MYASSERT(ExtPart);
  3665. lstrcpy(ExtPart,TEXT(".PNF"));
  3666. if(pSetupDoLastKnownGoodBackup(NULL,
  3667. FullTargetFilename,
  3668. SP_LKG_FLAG_FORCECOPY|SP_LKG_FLAG_DELETEIFNEW|(write_delete?SP_LKG_FLAG_DELETEEXISTING:0),
  3669. LogContext) != NO_ERROR) {
  3670. //
  3671. // should never fail
  3672. //
  3673. MYASSERT(FALSE);
  3674. }
  3675. }
  3676. //
  3677. // done!
  3678. //
  3679. rval = NO_ERROR;
  3680. goto final;
  3681. cleanup:
  3682. //
  3683. // cleanup in the case where we've already created temporary file
  3684. //
  3685. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3686. DeleteFile(TempFilename);
  3687. rval = NO_ERROR;
  3688. final:
  3689. if(LocalLogContext) {
  3690. DeleteLogContext(LocalLogContext);
  3691. }
  3692. return rval;
  3693. #else
  3694. //
  3695. // ANSI - not supported
  3696. //
  3697. return NO_ERROR;
  3698. #endif
  3699. }
  3700. #ifdef UNICODE
  3701. BOOL
  3702. pSetupRestoreLastKnownGoodFile(
  3703. IN PCTSTR TargetFilename,
  3704. IN DWORD Flags,
  3705. IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
  3706. )
  3707. /*++
  3708. Routine Description:
  3709. Restore a single LKG file
  3710. The assumption here is that if this API was called, we detected something
  3711. really bad that needs to be fixed immediately
  3712. Arguments:
  3713. TargetFilename Name of file to restore
  3714. Flags
  3715. LogContext If specified, provides preferred logging context
  3716. Return Value:
  3717. TRUE if file was successfully restored
  3718. --*/
  3719. {
  3720. int wd_len; // windows directory len
  3721. int tf_len; // target file len
  3722. int lkgd_len; // last known good directory len
  3723. int rf_len; // relative file len (including preceeding slash)
  3724. TCHAR FullTargetFilename[MAX_PATH];
  3725. TCHAR BackupTargetFilename[MAX_PATH+14];
  3726. TCHAR TempFilename[MAX_PATH];
  3727. TCHAR TempPathname[MAX_PATH];
  3728. TCHAR RegName[MAX_PATH];
  3729. PCTSTR RelativeFilename;
  3730. PTSTR NamePart = NULL;
  3731. BOOL rflag = FALSE;
  3732. PSETUP_LOG_CONTEXT LocalLogContext = NULL;
  3733. LONG regres;
  3734. HKEY hKeyLastGood;
  3735. PCTSTR CharPtr;
  3736. PTSTR DestPtr;
  3737. DWORD RegType;
  3738. DWORD RegSize;
  3739. DWORD LastGoodFlags;
  3740. if (!LogContext) {
  3741. //
  3742. // LogContext may be obmitted if there's a Queue parameter
  3743. //
  3744. if(CreateLogContext(NULL,TRUE,&LocalLogContext)==NO_ERROR) {
  3745. LogContext = LocalLogContext;
  3746. }
  3747. MYASSERT(LogContext);
  3748. }
  3749. //
  3750. // cannonicalize the Target name so user doesn't do stuff like .../TEMP/../INF
  3751. //
  3752. tf_len = (int)GetFullPathName(TargetFilename,
  3753. MAX_PATH,
  3754. FullTargetFilename,
  3755. &NamePart);
  3756. if (tf_len <= 0 || tf_len > MAX_PATH) {
  3757. //
  3758. // we don't do large paths very well
  3759. //
  3760. goto final;
  3761. }
  3762. wd_len = lstrlen(WindowsDirectory);
  3763. lkgd_len = lstrlen(LastGoodDirectory);
  3764. //
  3765. // see if this file is nested below <<WindowsDirectory>>
  3766. // note that such a file must be at least two characters longer
  3767. //
  3768. if((tf_len <= wd_len)
  3769. || (FullTargetFilename[wd_len] != TEXT('\\'))
  3770. || (_tcsnicmp(WindowsDirectory,FullTargetFilename,wd_len)!=0)) {
  3771. //
  3772. // this file is outside of %windir%, not handled by LKG
  3773. //
  3774. goto final;
  3775. }
  3776. //
  3777. // remap to LKG directory
  3778. //
  3779. RelativeFilename = FullTargetFilename+wd_len; // includes preceeding backslash
  3780. rf_len = tf_len-wd_len;
  3781. MYASSERT((MAX_PATH+(lkgd_len-wd_len))<=SIZECHARS(BackupTargetFilename));
  3782. lstrcpy(BackupTargetFilename,LastGoodDirectory);
  3783. lstrcpy(BackupTargetFilename+lkgd_len,RelativeFilename);
  3784. //
  3785. // does backup already exist?
  3786. //
  3787. if (GetFileAttributes(BackupTargetFilename)==(DWORD)(-1)) {
  3788. //
  3789. // if not, nothing we can do
  3790. //
  3791. goto final;
  3792. }
  3793. //
  3794. // find LKG flags to see what we need to do
  3795. //
  3796. regres = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3797. REGSTR_PATH_LASTGOOD,
  3798. 0,
  3799. KEY_READ,
  3800. &hKeyLastGood);
  3801. if (regres == NO_ERROR) {
  3802. //
  3803. // copy string, remapping slash's from '\\' to '/'
  3804. //
  3805. CharPtr = RelativeFilename+1; // after initial '\'
  3806. DestPtr = RegName;
  3807. while(*CharPtr) {
  3808. PCTSTR Next = _tcschr(CharPtr,TEXT('\\'));
  3809. if (!Next) {
  3810. Next = CharPtr + lstrlen(CharPtr);
  3811. }
  3812. if(Next-CharPtr) {
  3813. CopyMemory(DestPtr,CharPtr,(Next-CharPtr)*sizeof(TCHAR));
  3814. DestPtr+=(Next-CharPtr);
  3815. CharPtr = Next;
  3816. }
  3817. if (*CharPtr == TEXT('\\')) {
  3818. *DestPtr = TEXT('/');
  3819. DestPtr++;
  3820. CharPtr++;
  3821. }
  3822. }
  3823. *DestPtr = TEXT('\0');
  3824. RegSize = sizeof(LastGoodFlags);
  3825. regres = RegQueryValueEx(hKeyLastGood,
  3826. RegName,
  3827. NULL,
  3828. &RegType,
  3829. (PBYTE)&LastGoodFlags,
  3830. &RegSize);
  3831. if((regres != NO_ERROR)
  3832. || (RegType != REG_DWORD)
  3833. || (RegSize != sizeof(DWORD))) {
  3834. //
  3835. // default action is copy
  3836. //
  3837. LastGoodFlags = 0;
  3838. }
  3839. RegCloseKey(hKeyLastGood);
  3840. }
  3841. //
  3842. // base directory of target file
  3843. //
  3844. lstrcpyn(TempPathname, FullTargetFilename, MAX_PATH);
  3845. *((PTSTR)pSetupGetFileTitle(TempPathname)) = TEXT('\0');
  3846. if (LastGoodFlags & LASTGOOD_OPERATION_DELETE) {
  3847. //
  3848. // delete
  3849. //
  3850. if(GetFileAttributes(FullTargetFilename)==(DWORD)(-1)) {
  3851. //
  3852. // already deleted
  3853. //
  3854. rflag = TRUE;
  3855. goto final;
  3856. }
  3857. pSetupExemptFileFromProtection(
  3858. FullTargetFilename,
  3859. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3860. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3861. LogContext,
  3862. NULL
  3863. );
  3864. //
  3865. // try the simple way first
  3866. //
  3867. SetFileAttributes(FullTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3868. if(!DeleteFile(FullTargetFilename)) {
  3869. //
  3870. // can't delete target directly
  3871. //
  3872. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, BackupTargetFilename)) {
  3873. //
  3874. // can't create backup temp, nothing we can do
  3875. //
  3876. goto final;
  3877. }
  3878. //
  3879. // move existing file into a temp backup
  3880. //
  3881. if(!MoveFileEx(FullTargetFilename,BackupTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3882. //
  3883. // this failed too for some reason
  3884. //
  3885. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3886. DeleteFile(BackupTargetFilename);
  3887. goto final;
  3888. }
  3889. //
  3890. // now do something with the bad file
  3891. // we don't care if this fails
  3892. //
  3893. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3894. if(!DeleteFile(BackupTargetFilename)) {
  3895. MoveFileEx(BackupTargetFilename,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
  3896. }
  3897. }
  3898. } else {
  3899. //
  3900. // restore back to LKG file
  3901. //
  3902. //
  3903. // create intermediate directories as needed as part of the restore
  3904. //
  3905. pSetupMakeSurePathExists(FullTargetFilename);
  3906. //
  3907. // create a temporary filename to copy to
  3908. // before moving restored file into place
  3909. //
  3910. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, TempFilename)) {
  3911. //
  3912. // can't create temp, nothing we can do
  3913. //
  3914. goto final;
  3915. }
  3916. if(!CopyFile(BackupTargetFilename,TempFilename,FALSE)) {
  3917. //
  3918. // failed to copy to temporary file
  3919. //
  3920. DeleteFile(TempFilename);
  3921. goto final;
  3922. }
  3923. //
  3924. // simple case, move temporary file over existing file
  3925. //
  3926. pSetupExemptFileFromProtection(
  3927. FullTargetFilename,
  3928. SFC_ACTION_ADDED | SFC_ACTION_REMOVED | SFC_ACTION_MODIFIED
  3929. | SFC_ACTION_RENAMED_OLD_NAME |SFC_ACTION_RENAMED_NEW_NAME,
  3930. LogContext,
  3931. NULL
  3932. );
  3933. SetFileAttributes(FullTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3934. if(!MoveFileEx(TempFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3935. //
  3936. // we failed to overwrite file, need slightly different stratagy
  3937. //
  3938. if(!GetTempFileName(TempPathname, TEXT("SETP"), 0, BackupTargetFilename)) {
  3939. //
  3940. // can't create backup temp, nothing we can do
  3941. //
  3942. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3943. DeleteFile(TempFilename);
  3944. goto final;
  3945. }
  3946. //
  3947. // move existing file into a temp backup
  3948. //
  3949. if(!MoveFileEx(FullTargetFilename,BackupTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3950. //
  3951. // this failed too for some reason
  3952. //
  3953. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3954. DeleteFile(BackupTargetFilename);
  3955. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3956. DeleteFile(TempFilename);
  3957. goto final;
  3958. }
  3959. //
  3960. // we moved existing file out of place, now move new file into place
  3961. //
  3962. if(!MoveFileEx(TempFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
  3963. //
  3964. // huh? Ok, that failed, try to recover
  3965. //
  3966. MoveFileEx(BackupTargetFilename,FullTargetFilename,MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH);
  3967. SetFileAttributes(TempFilename, FILE_ATTRIBUTE_NORMAL);
  3968. DeleteFile(TempFilename);
  3969. goto final;
  3970. }
  3971. //
  3972. // now do something with the bad file
  3973. // we don't care if this fails
  3974. //
  3975. SetFileAttributes(BackupTargetFilename, FILE_ATTRIBUTE_NORMAL);
  3976. if(!DeleteFile(BackupTargetFilename)) {
  3977. MoveFileEx(BackupTargetFilename,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
  3978. }
  3979. }
  3980. }
  3981. //
  3982. // done!
  3983. //
  3984. rflag = TRUE;
  3985. final:
  3986. if(LocalLogContext) {
  3987. DeleteLogContext(LocalLogContext);
  3988. }
  3989. return rflag;
  3990. }
  3991. #endif
  3992. #ifdef UNICODE
  3993. WINSETUPAPI
  3994. BOOL
  3995. WINAPI
  3996. SetupPrepareQueueForRestoreA(
  3997. IN HSPFILEQ QueueHandle,
  3998. IN PCSTR BackupPath,
  3999. IN DWORD RestoreFlags
  4000. )
  4001. /*++
  4002. See SetupPrepareQueueForRestore
  4003. --*/
  4004. {
  4005. BOOL f;
  4006. DWORD rc;
  4007. PCWSTR UnicodeBackupPath;
  4008. if(BackupPath) {
  4009. rc = pSetupCaptureAndConvertAnsiArg(BackupPath, &UnicodeBackupPath);
  4010. if(rc != NO_ERROR) {
  4011. SetLastError(rc);
  4012. return FALSE;
  4013. }
  4014. } else {
  4015. SetLastError(ERROR_INVALID_PARAMETER);
  4016. return FALSE;
  4017. }
  4018. f = SetupPrepareQueueForRestore(QueueHandle,UnicodeBackupPath,RestoreFlags);
  4019. rc = GetLastError();
  4020. MyFree(UnicodeBackupPath);
  4021. SetLastError(rc);
  4022. return f;
  4023. }
  4024. #else
  4025. WINSETUPAPI
  4026. BOOL
  4027. WINAPI
  4028. SetupPrepareQueueForRestoreW(
  4029. IN HSPFILEQ QueueHandle,
  4030. IN PCWSTR BackupPath,
  4031. IN DWORD RestoreFlags
  4032. )
  4033. /*++
  4034. ANSI stub
  4035. --*/
  4036. {
  4037. UNREFERENCED_PARAMETER(QueueHandle);
  4038. UNREFERENCED_PARAMETER(BackupPath);
  4039. UNREFERENCED_PARAMETER(RestoreFlags);
  4040. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  4041. return(FALSE);
  4042. }
  4043. #endif
  4044. WINSETUPAPI
  4045. BOOL
  4046. WINAPI
  4047. SetupPrepareQueueForRestore(
  4048. IN HSPFILEQ QueueHandle,
  4049. IN PCTSTR BackupPath,
  4050. IN DWORD RestoreFlags
  4051. )
  4052. /*++
  4053. Routine Description:
  4054. Initializes restore directory
  4055. Arguments:
  4056. QueueHandle - file queue to modify
  4057. BackupPath - original backup directory to use for restore
  4058. RestoreFlags - options
  4059. Return Value:
  4060. TRUE if success, else FALSE
  4061. --*/
  4062. {
  4063. BOOL b = TRUE;
  4064. DWORD rc;
  4065. BOOL f = FALSE;
  4066. PSP_FILE_QUEUE Queue = (PSP_FILE_QUEUE)QueueHandle;
  4067. LONG RestorePathID;
  4068. //
  4069. // validate string pointer
  4070. //
  4071. if(!BackupPath) {
  4072. rc = ERROR_INVALID_PARAMETER;
  4073. goto clean;
  4074. }
  4075. //
  4076. // validate flags (currently not implemented)
  4077. //
  4078. if(RestoreFlags) {
  4079. rc = ERROR_INVALID_PARAMETER;
  4080. goto clean;
  4081. }
  4082. //
  4083. // validate QueueHandle
  4084. //
  4085. try {
  4086. if(Queue->Signature != SP_FILE_QUEUE_SIG) {
  4087. b = FALSE;
  4088. }
  4089. } except(EXCEPTION_EXECUTE_HANDLER) {
  4090. b = FALSE;
  4091. }
  4092. if(!b) {
  4093. rc = ERROR_INVALID_HANDLE;
  4094. goto clean;
  4095. }
  4096. try {
  4097. //
  4098. // if a restore point has previously been set, return error
  4099. //
  4100. if(Queue->RestorePathID != -1) {
  4101. rc = ERROR_ALREADY_EXISTS;
  4102. leave;
  4103. }
  4104. RestorePathID = pSetupStringTableAddString(Queue->StringTable,
  4105. (PTSTR)BackupPath ,
  4106. STRTAB_CASE_SENSITIVE);
  4107. if (RestorePathID == -1) {
  4108. rc = ERROR_NOT_ENOUGH_MEMORY;
  4109. leave;
  4110. }
  4111. //
  4112. // done - just need to set the restore-path
  4113. //
  4114. Queue->RestorePathID = RestorePathID;
  4115. WriteLogEntry(Queue->LogContext,
  4116. SETUP_LOG_WARNING,
  4117. MSG_LOG_RESTORE,
  4118. NULL,
  4119. BackupPath
  4120. );
  4121. } except(EXCEPTION_EXECUTE_HANDLER) {
  4122. rc = ERROR_INVALID_DATA;
  4123. }
  4124. f = TRUE;
  4125. rc = NO_ERROR;
  4126. clean:
  4127. //
  4128. // no cleanup required
  4129. //
  4130. SetLastError(rc);
  4131. return f;
  4132. }
  4133. #define SP_TEFLG_BITS_TO_RESET ( SP_TEFLG_SAVED \
  4134. | SP_TEFLG_TEMPNAME \
  4135. | SP_TEFLG_ORIGNAME \
  4136. | SP_TEFLG_MODIFIED \
  4137. | SP_TEFLG_MOVED \
  4138. | SP_TEFLG_BACKUPQUEUE \
  4139. | SP_TEFLG_RESTORED \
  4140. | SP_TEFLG_UNWIND \
  4141. | SP_TEFLG_SKIPPED \
  4142. | SP_TEFLG_INUSE \
  4143. | SP_TEFLG_RENAMEEXISTING )
  4144. BOOL
  4145. pSetupResetTarget(
  4146. IN PVOID StringTable,
  4147. IN LONG StringId,
  4148. IN PCTSTR String, OPTIONAL
  4149. IN PVOID ExtraData,
  4150. IN UINT ExtraDataSize,
  4151. IN LPARAM lParam
  4152. )
  4153. /*++
  4154. Routine Description:
  4155. This routine resets the SP_TARGET_ENT data stored with a string table entry
  4156. in a file queue's TargetLookupTable. This routine may be used as the
  4157. callback function to iterate such entries via pSetupStringTableEnum.
  4158. Arguments:
  4159. StringTable - Supplies a handle to the string table being enumerated
  4160. StringId - Supplies the ID of the current string
  4161. String - Optionally, supplies a pointer to the current string (this will
  4162. always be filled in when this routine is used as a callback for
  4163. pSetupStringTableEnum, but other callers may omit it, as it isn't
  4164. needed).
  4165. ExtraData - Supplies a pointer to the SP_TARGET_ENT data associatd with
  4166. the string
  4167. ExtraDataSize - Supplies the size of the buffer pointed to by ExtraData--
  4168. should always be sizeof(SP_TARGET_ENT)
  4169. lParam - unused
  4170. Return Value:
  4171. This routine always returns TRUE, so that all string entries will be
  4172. enumerated.
  4173. --*/
  4174. {
  4175. PSP_TARGET_ENT TargetInfo;
  4176. BOOL b;
  4177. UNREFERENCED_PARAMETER(String);
  4178. UNREFERENCED_PARAMETER(lParam);
  4179. MYASSERT(ExtraData);
  4180. MYASSERT(ExtraDataSize == sizeof(SP_TARGET_ENT));
  4181. //
  4182. // Clear the bits that will get re-generated when the queue is committed
  4183. // again.
  4184. //
  4185. ((PSP_TARGET_ENT)ExtraData)->InternalFlags &= ~SP_TEFLG_BITS_TO_RESET;
  4186. //
  4187. // Also need to reset the NewTargetFilename
  4188. //
  4189. ((PSP_TARGET_ENT)ExtraData)->NewTargetFilename = -1;
  4190. //
  4191. // Store the modified data back to the string table entry
  4192. //
  4193. b = pSetupStringTableSetExtraData(StringTable,
  4194. StringId,
  4195. ExtraData,
  4196. ExtraDataSize
  4197. );
  4198. //
  4199. // This should never fail
  4200. //
  4201. MYASSERT(b);
  4202. return TRUE;
  4203. }