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.

4383 lines
108 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. win9xupg.c
  5. Abstract:
  6. Code for detecting a Win9x installation, and code to blow away any
  7. existing Win9x files.
  8. Author:
  9. Jim Schmidt (jimschm) 24-Feb-1997
  10. Revision History:
  11. Marc R. Whitten (marcw) 28-Feb-1997
  12. Moved Win9x copy and delete functions from spcopy.c to this module
  13. Added drive letter mapping code.
  14. Jim Schmidt (JimSchm) November, December 2000
  15. Win9x uninstall work
  16. Jay Krell (a-JayK) December 2000
  17. Win9x uninstall work (cab)
  18. --*/
  19. #include "spprecmp.h"
  20. #pragma hdrstop
  21. #include "ntddscsi.h"
  22. #include "spwin9xuninstall.h"
  23. #include "spcab.h"
  24. #include "spmemory.h"
  25. #include "spprintf.h"
  26. #include "spcabp.h"
  27. #include "bootvar.h"
  28. #include "spwin.h"
  29. extern BOOLEAN DriveAssignFromA; //NEC98
  30. #define STRING_VALUE(s) REG_SZ,(s),(wcslen((s))+1)*sizeof(WCHAR)
  31. #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
  32. typedef enum {
  33. BACKUP_DOESNT_EXIST,
  34. BACKUP_IN_PROGRESS,
  35. BACKUP_SKIPPED_BY_USER,
  36. BACKUP_COMPLETE
  37. } JOURNALSTATUS;
  38. // in spdskreg.c
  39. BOOL
  40. SpBuildDiskRegistry(
  41. VOID
  42. );
  43. VOID
  44. SpGetPartitionStartingOffsetAndLength(
  45. IN DWORD DiskIndex,
  46. IN PDISK_REGION Region,
  47. IN BOOL ExtendedPartition,
  48. OUT PLARGE_INTEGER Offset,
  49. OUT PLARGE_INTEGER Length
  50. );
  51. VOID
  52. SpDumpDiskRegistry(
  53. VOID
  54. );
  55. // in spsetup.c
  56. VOID
  57. SpCompleteBootListConfig(
  58. WCHAR DriveLetter
  59. );
  60. // in win31upg.c
  61. WCHAR
  62. SpExtractDriveLetter(
  63. IN PWSTR PathComponent
  64. );
  65. BOOLEAN
  66. SpIsWin9xMsdosSys(
  67. IN PDISK_REGION Region,
  68. OUT PSTR* Win9xPath
  69. );
  70. VOID
  71. SpAssignDriveLettersToMatchWin9x (
  72. IN PVOID WinntSif
  73. );
  74. VOID
  75. SppMoveWin9xFilesWorker (
  76. IN PVOID WinntSif,
  77. IN PCWSTR MoveSection,
  78. IN BOOLEAN Rollback
  79. );
  80. VOID
  81. SppDeleteWin9xFilesWorker (
  82. IN PVOID WinntSif,
  83. IN PCWSTR FileSection, OPTIONAL
  84. IN PCWSTR DirSection, OPTIONAL
  85. IN BOOLEAN Rollback
  86. );
  87. PDISK_REGION
  88. SppRegionFromFullNtName (
  89. IN PWSTR NtName,
  90. IN PartitionOrdinalType OrdinalType,
  91. OUT PWSTR *Path OPTIONAL
  92. );
  93. BOOLEAN
  94. SppCreateTextModeBootEntry (
  95. IN PWSTR LoadIdentifierString,
  96. IN PWSTR OsLoadOptions, OPTIONAL
  97. IN BOOLEAN Deafult
  98. );
  99. BOOLEAN
  100. SppDelEmptyDir (
  101. IN PCWSTR NtPath
  102. );
  103. ENUMNONNTUPRADETYPE
  104. SpLocateWin95(
  105. OUT PDISK_REGION *InstallRegion,
  106. OUT PWSTR *InstallPath,
  107. OUT PDISK_REGION *SystemPartitionRegion
  108. )
  109. /*++
  110. Routine Description:
  111. Determine whether we are to continue a Win95 upgrade.
  112. This is based solely on values found in the parameters file.
  113. Arguments:
  114. InstallRegion - Returns a pointer to the region to install to.
  115. InstallPath - Returns a pointer to a buffer containing the path
  116. on the partition to install to. The caller must free this
  117. buffer with SpMemFree().
  118. SystemPartitionRegion - Returns a pointer to the region for the
  119. system partition (ie, C:).
  120. Return Value:
  121. UpgradeWin95 if we are supposed to upgrade win95
  122. NoWinUpgrade if not.
  123. --*/
  124. {
  125. PWSTR Win95Drive;
  126. PWSTR p;
  127. PWSTR Sysroot;
  128. PDISK_REGION CColonRegion;
  129. ENUMNONNTUPRADETYPE UpgradeType = UpgradeWin95;
  130. //
  131. // Changed sequence the test migration flag and migrate drive letters,
  132. // to not migrate drive letters when fresh from Win9x on NEC98.
  133. //
  134. //
  135. // Test the migration flag.
  136. //
  137. p = SpGetSectionKeyIndex(WinntSifHandle,SIF_DATA,WINNT_D_WIN95UPGRADE_W,0);
  138. Win95Drive = SpGetSectionKeyIndex(WinntSifHandle,SIF_DATA,WINNT_D_WIN32_DRIVE_W,0);
  139. Sysroot = SpGetSectionKeyIndex(WinntSifHandle,SIF_DATA,WINNT_D_WIN32_PATH_W,0);
  140. if (!IsNEC_98) {
  141. CColonRegion = SpPtValidSystemPartition();
  142. }
  143. if(!p || _wcsicmp(p,WINNT_A_YES_W) || !Win95Drive || (!IsNEC_98 && !CColonRegion) || !Sysroot) {
  144. UpgradeType = NoWinUpgrade;
  145. }
  146. //
  147. // NEC98 must not migrate drive letters, when fresh setup.
  148. // The drive letter is started from A: on Win9x, but should be started from C:
  149. // on Win2000 fresh setup.
  150. //
  151. // NOTE : Also don't migrate the drive letters for clean installs
  152. // from winnt32.exe on Win9x machines, since the drive letter migration
  153. // is a bogus migration. We don't tell the mountmgr to reserve the drive letter
  154. // and when user creates and deletes a partition then we might end up assigning
  155. // the existing drive letter to new partition, which is really bad.
  156. //
  157. if(UpgradeType == NoWinUpgrade) {
  158. return UpgradeType;
  159. }
  160. //
  161. // First, make sure drive letters are correct.
  162. //
  163. SpAssignDriveLettersToMatchWin9x(WinntSifHandle);
  164. if(!IsNEC_98 && (UpgradeType == NoWinUpgrade)) {
  165. return(UpgradeType);
  166. }
  167. //
  168. // Migration enabled and everything looks OK.
  169. //
  170. *InstallRegion = SpRegionFromDosName(Win95Drive);
  171. *InstallPath = Sysroot;
  172. //
  173. // On NEC98, SystemPartitionRegion must be same as InstallRegion.
  174. //
  175. *SystemPartitionRegion = (!IsNEC_98) ? CColonRegion : *InstallRegion;
  176. return(UpgradeType);
  177. }
  178. #if 0
  179. BOOLEAN
  180. SpLocateWin95(
  181. IN PVOID WinntSif
  182. )
  183. /*++
  184. Routine Description:
  185. SpLocateWin95 looks for any installation of Windows 95 on any
  186. hard disk drive, and returns TRUE if one is found. When the user
  187. initiates setup from boot floppies, we alert them that they have
  188. an option to migrate.
  189. Arguments:
  190. none
  191. Return Value:
  192. TRUE if we are migrating Win95.
  193. FALSE otherwise.
  194. --*/
  195. {
  196. PDISK_REGION CColonRegion;
  197. PDISK_REGION Region;
  198. PUCHAR Win9xPath;
  199. //
  200. // If setup was initiated from WINNT95, don't bother telling user
  201. // about the migrate option--they obviously know.
  202. //
  203. if (Winnt95Setup)
  204. return TRUE;
  205. //
  206. // Look at boot sector for Win95 stuff
  207. //
  208. CLEAR_CLIENT_SCREEN();
  209. SpDisplayStatusText(SP_STAT_LOOKING_FOR_WIN95,DEFAULT_STATUS_ATTRIBUTE);
  210. //
  211. // See if there is a valid C: already. If not, then we can't have Win95.
  212. //
  213. CColonRegion = SpPtValidSystemPartition();
  214. if(!CColonRegion) {
  215. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: no C:, no Win95!\n"));
  216. return(FALSE);
  217. }
  218. //
  219. // Check the filesystem. If not FAT, then we don't have Win95.
  220. //
  221. if(CColonRegion->Filesystem != FilesystemFat) {
  222. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: C: is not FAT, no Win95!\n"));
  223. return(FALSE);
  224. }
  225. //
  226. // Check to see if there is enough free space, etc on C:.
  227. // If not, don't call attention to the migrate option, because
  228. // it won't work.
  229. //
  230. if(!SpPtValidateCColonFormat(WinntSif,NULL,CColonRegion,TRUE,NULL,NULL)) {
  231. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: C: not acceptable, no Win95!\n"));
  232. return(FALSE);
  233. }
  234. //
  235. // If msdos.sys is not the Win95 flavor, we do not have Win95
  236. // on drive C.
  237. //
  238. if(!SpIsWin9xMsdosSys(CColonRegion, &Win9xPath) )
  239. return FALSE;
  240. SpMemFree(Win9xPath);
  241. //
  242. // By this time, we've found a FAT C drive, it has the Win95
  243. // version of msdos.sys, and it has a valid config.sys. We
  244. // now conclude that this drive has Win95 on it!
  245. //
  246. // If we were initiated from WINNT32, don't tell the user
  247. // about this option.
  248. //
  249. //
  250. // We don't tell the user even if they ran 16-bit
  251. // WINNT. The only time this will run is if the user throws
  252. // in a boot floppy.
  253. //
  254. if (!WinntSetup)
  255. SpTellUserAboutMigrationOption(); // may not return!
  256. return TRUE;
  257. }
  258. VOID
  259. SpTellUserAboutMigrationOption ()
  260. {
  261. ULONG ValidKeys[3] = { KEY_F3,ASCI_CR,0 };
  262. ULONG Mnemonics[2] = { MnemonicContinueSetup,0 };
  263. while(1) {
  264. SpDisplayScreen(SP_SCRN_WIN95_MIGRATION,
  265. 3,
  266. HEADER_HEIGHT+1,
  267. );
  268. SpDisplayStatusOptions(
  269. DEFAULT_STATUS_ATTRIBUTE,
  270. SP_STAT_ENTER_EQUALS_CONTINUE,
  271. SP_STAT_F3_EQUALS_EXIT,
  272. 0
  273. );
  274. switch(SpWaitValidKey(ValidKeys,NULL,Mnemonics)) {
  275. case KEY_F3:
  276. SpConfirmExit();
  277. break;
  278. default:
  279. //
  280. // must be Enter=continue
  281. //
  282. return;
  283. }
  284. }
  285. return;
  286. }
  287. #endif
  288. BOOLEAN
  289. SpIsWin4Dir(
  290. IN PDISK_REGION Region,
  291. IN PWSTR PathComponent
  292. )
  293. /*++
  294. Routine Description:
  295. To find out if the directory indicated on the region contains a
  296. Microsoft Windows 95 (or later) installation. We do this by looking
  297. for a set of files in the SYSTEM subdirectory that don't exist under
  298. Win3.x, and under NT are located in the SYSTEM32 subdirectory.
  299. Arguments:
  300. Region - supplies pointer to disk region descriptor for region
  301. containing the directory to be checked.
  302. PathComponent - supplies a component of the dos path to search
  303. on the region. This is assumes to be in the form x:\dir.
  304. If it is not in this form, this routine will fail.
  305. Return Value:
  306. TRUE if this path contains a Microsoft Windows 4.x installation.
  307. FALSE otherwise.
  308. --*/
  309. {
  310. PWSTR files[] = { L"SHELL32.DLL", L"USER32.DLL", L"KERNEL32.DLL", L"GDI32.DLL" };
  311. PWCHAR OpenPath;
  312. BOOLEAN rc;
  313. //
  314. // Assume failure.
  315. //
  316. rc = FALSE;
  317. //
  318. // If the partition is not FAT, then ignore it.
  319. //
  320. if(Region->PartitionedSpace &&
  321. ((Region->Filesystem == FilesystemFat) || (Region->Filesystem == FilesystemFat32))) {
  322. OpenPath = SpMemAlloc(512*sizeof(WCHAR));
  323. //
  324. // Form the name of the partition.
  325. //
  326. SpNtNameFromRegion(Region,OpenPath,512*sizeof(WCHAR),PartitionOrdinalCurrent);
  327. //
  328. // Slap on the directory part of the path component.
  329. //
  330. SpConcatenatePaths(
  331. OpenPath,
  332. PathComponent + (SpExtractDriveLetter(PathComponent) ? 2 : 0)
  333. );
  334. //
  335. // Append the SYSTEM subdirectory to the path.
  336. //
  337. SpConcatenatePaths(OpenPath, L"SYSTEM");
  338. //
  339. // Determine whether all the required files are present.
  340. //
  341. rc = SpNFilesExist(OpenPath,files,ELEMENT_COUNT(files),FALSE);
  342. SpMemFree(OpenPath);
  343. }
  344. return(rc);
  345. }
  346. // needed for Win3.1 detection
  347. BOOLEAN
  348. SpIsWin9xMsdosSys(
  349. IN PDISK_REGION Region,
  350. OUT PSTR* Win9xPath
  351. )
  352. {
  353. WCHAR OpenPath[512];
  354. HANDLE FileHandle,SectionHandle;
  355. ULONG FileSize;
  356. PVOID ViewBase;
  357. PUCHAR pFile,pFileEnd,pLineEnd;
  358. ULONG i;
  359. NTSTATUS Status;
  360. ULONG LineLen,KeyLen;
  361. PCHAR Keyword = "[Paths]";
  362. PSTR p;
  363. ULONG cbText;
  364. //
  365. // Form name of config.sys.
  366. //
  367. SpNtNameFromRegion(Region,OpenPath,sizeof(OpenPath),PartitionOrdinalCurrent);
  368. SpConcatenatePaths(OpenPath,L"msdos.sys");
  369. //
  370. // Open and map the file.
  371. //
  372. FileHandle = 0;
  373. Status = SpOpenAndMapFile(
  374. OpenPath,
  375. &FileHandle,
  376. &SectionHandle,
  377. &ViewBase,
  378. &FileSize,
  379. FALSE
  380. );
  381. if(!NT_SUCCESS(Status)) {
  382. return(FALSE);
  383. }
  384. pFile = ViewBase;
  385. pFileEnd = pFile + FileSize;
  386. //
  387. // This code must guard access to the msdos.sys buffer because the
  388. // buffer is memory mapped (an i/o error would raise an exception).
  389. // This code could be structured better, as it now works by returning
  390. // from the try body -- but performance isn't an issue so this is acceptable
  391. // because it is so darned convenient.
  392. //
  393. try {
  394. KeyLen = strlen(Keyword);
  395. //
  396. // Search for the [Paths] section
  397. //
  398. while (pFile < pFileEnd) {
  399. if (!_strnicmp(pFile, Keyword, KeyLen)) {
  400. break;
  401. }
  402. pFile++;
  403. }
  404. //
  405. // did we find the section
  406. //
  407. if (pFile >= pFileEnd) {
  408. return FALSE;
  409. }
  410. //
  411. // parse the [Paths] section
  412. //
  413. pFile += KeyLen;
  414. while(1) {
  415. //
  416. // Skip whitespace. If at end of file, then this is not a Win9x msdos.sys.
  417. //
  418. while((pFile < pFileEnd) && strchr(" \r\n\t",*pFile)) {
  419. pFile++;
  420. }
  421. if(pFile == pFileEnd) {
  422. return(FALSE);
  423. }
  424. //
  425. // Find the end of the current line.
  426. //
  427. pLineEnd = pFile;
  428. while((pLineEnd < pFileEnd) && !strchr("\r\n",*pLineEnd)) {
  429. pLineEnd++;
  430. }
  431. LineLen = pLineEnd - pFile;
  432. Keyword = "WinDir";
  433. KeyLen = strlen( Keyword );
  434. if( _strnicmp(pFile,Keyword,KeyLen) ) {
  435. pFile = pLineEnd;
  436. continue;
  437. }
  438. pFile += KeyLen;
  439. while((pFile < pFileEnd) && strchr(" =\r\n\t",*pFile)) {
  440. pFile++;
  441. }
  442. if(pFile == pFileEnd) {
  443. return(FALSE);
  444. }
  445. KeyLen = (ULONG)(pLineEnd - pFile);
  446. p = SpMemAlloc( KeyLen + 1 );
  447. for( i = 0; i < KeyLen; i++ ) {
  448. *(p + i) = *(pFile + i );
  449. }
  450. *(p + i ) = '\0';
  451. *Win9xPath = p;
  452. return(TRUE);
  453. }
  454. } finally {
  455. SpUnmapFile(SectionHandle,ViewBase);
  456. ZwClose(FileHandle);
  457. }
  458. }
  459. /*++
  460. Routine Description:
  461. SpOpenWin9xDat file is a wrapper routine for opening one of the unicode DAT
  462. files used for certain win9x file lists.
  463. Arguments:
  464. DatFile - The name of the Dat file to enum.
  465. WinntSif - A pointer to a valid SIF handle object. This is used to
  466. retrieve information on the location of the DAT file named
  467. above.
  468. Return Value:
  469. A valid handle if the file was successfully opened, INVALID_HANDLE_VALUE
  470. otherwise.
  471. --*/
  472. HANDLE SpOpenWin9xDatFile (
  473. IN PCWSTR DatFile,
  474. IN PVOID WinntSif
  475. )
  476. {
  477. HANDLE rFile;
  478. NTSTATUS status;
  479. UNICODE_STRING datFileU;
  480. OBJECT_ATTRIBUTES oa;
  481. IO_STATUS_BLOCK ioStatusBlock;
  482. PDISK_REGION win9xTempRegion;
  483. PWSTR win9xTempDir;
  484. WCHAR ntName[ACTUAL_MAX_PATH];
  485. if (DatFile[0] && DatFile[1] == L':') {
  486. //
  487. // Convert a DOS path into an NT path
  488. //
  489. if (!SpNtNameFromDosPath (
  490. DatFile,
  491. ntName,
  492. sizeof (ntName),
  493. PartitionOrdinalCurrent
  494. )) {
  495. KdPrintEx((
  496. DPFLTR_SETUP_ID,
  497. DPFLTR_ERROR_LEVEL,
  498. "SETUP: Cannot convert path %ws to an NT path\n",
  499. DatFile
  500. ));
  501. return INVALID_HANDLE_VALUE;
  502. }
  503. } else {
  504. //
  505. // The location of the win9x.sif file is in the Win9xSif key of the [data] section.
  506. //
  507. win9xTempDir = SpGetSectionKeyIndex(WinntSif,SIF_DATA,WINNT_D_WIN9XTEMPDIR_W,0);
  508. if (!win9xTempDir) {
  509. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not get Win9x temp dir..\n"));
  510. return INVALID_HANDLE_VALUE;
  511. }
  512. //
  513. // Get the region from the dos name..
  514. //
  515. win9xTempRegion = SpRegionFromDosName (win9xTempDir);
  516. if (!win9xTempRegion) {
  517. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRegionFromDosName failed for %ws\n", win9xTempDir));
  518. return INVALID_HANDLE_VALUE;
  519. }
  520. //
  521. // Get the NT name from the disk region..
  522. //
  523. SpNtNameFromRegion(
  524. win9xTempRegion,
  525. (PWSTR)TemporaryBuffer,
  526. sizeof(TemporaryBuffer),
  527. PartitionOrdinalCurrent
  528. );
  529. //
  530. // build the complete NT path to the win9x sif file..
  531. //
  532. SpConcatenatePaths((PWSTR) TemporaryBuffer, &win9xTempDir[2]);
  533. SpConcatenatePaths((PWSTR) TemporaryBuffer, DatFile);
  534. wcsncpy ( ntName,
  535. TemporaryBuffer,
  536. MAX_COPY_SIZE(ntName));
  537. ntName[MAX_COPY_SIZE(ntName)] = L'\0';
  538. }
  539. //
  540. // Open the file.
  541. //
  542. RtlInitUnicodeString(&datFileU,ntName);
  543. InitializeObjectAttributes(&oa,&datFileU,OBJ_CASE_INSENSITIVE,NULL,NULL);
  544. status = ZwCreateFile(
  545. &rFile,
  546. FILE_GENERIC_READ,
  547. &oa,
  548. &ioStatusBlock,
  549. NULL,
  550. FILE_ATTRIBUTE_NORMAL,
  551. FILE_SHARE_READ,
  552. FILE_OPEN,
  553. 0,
  554. NULL,
  555. 0
  556. );
  557. if(!NT_SUCCESS(status)) {
  558. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpOpenWin9xDatFile: unable to open file %ws (%lx)\n",DatFile,status));
  559. return INVALID_HANDLE_VALUE;
  560. }
  561. return rFile;
  562. }
  563. typedef struct {
  564. HANDLE FileHandle;
  565. PWSTR EndOfFile;
  566. HANDLE FileSection;
  567. PWSTR NextLine;
  568. PWSTR UnMapAddress;
  569. WCHAR CurLine[MAX_PATH];
  570. } WIN9XDATFILEENUM, * PWIN9XDATFILEENUM;
  571. /*++
  572. Routine Description:
  573. SpAbortWin9xFileEnum aborts the current win9x DAT file enumeration.
  574. Arguments:
  575. None.
  576. Return Value:
  577. --*/
  578. VOID
  579. SpAbortWin9xFileEnum (
  580. IN PWIN9XDATFILEENUM Enum
  581. )
  582. {
  583. SpUnmapFile(Enum -> FileSection,Enum -> UnMapAddress);
  584. ZwClose(Enum -> FileHandle);
  585. }
  586. /*++
  587. Routine Description:
  588. SpEnumNextWin9xFile is fills in the enumeration structure with the next
  589. available data from the DAT file being enumerated.
  590. Arguments:
  591. Enum - A pointer to a valid enumeration structure for the file currently
  592. being enumerated.
  593. Return Value:
  594. TRUE if there was more data to enumerate, FALSE otherwise.
  595. --*/
  596. BOOL
  597. SpEnumNextWin9xFile (
  598. IN PWIN9XDATFILEENUM Enum
  599. )
  600. {
  601. PWSTR endOfLine;
  602. BOOL result = FALSE;
  603. PWSTR src;
  604. PWSTR dest;
  605. for (;;) {
  606. //
  607. // Does another line exist?
  608. //
  609. endOfLine = Enum->NextLine;
  610. if (endOfLine >= Enum->EndOfFile) {
  611. // no more data in file
  612. break;
  613. }
  614. //
  615. // Parse the next line
  616. //
  617. src = endOfLine;
  618. while (endOfLine < Enum->EndOfFile &&
  619. *endOfLine != L'\r' &&
  620. *endOfLine != L'\n'
  621. ) {
  622. endOfLine++;
  623. }
  624. // next line starts after \r\n, \r or \n
  625. Enum->NextLine = endOfLine;
  626. if (Enum->NextLine < Enum->EndOfFile && *Enum->NextLine == L'\r') {
  627. Enum->NextLine++;
  628. }
  629. if (Enum->NextLine < Enum->EndOfFile && *Enum->NextLine == L'\n') {
  630. Enum->NextLine++;
  631. }
  632. if (endOfLine - src > (MAX_PATH - 1)) {
  633. WCHAR chEnd = *endOfLine;
  634. *endOfLine = '\0';
  635. KdPrintEx ((
  636. DPFLTR_SETUP_ID,
  637. DPFLTR_ERROR_LEVEL,
  638. "SETUP: Ignoring a configuration file line that is too long - %ws\n",
  639. src
  640. ));
  641. *endOfLine = chEnd;
  642. continue;
  643. }
  644. //
  645. // Copy the line into the enum struct buffer
  646. //
  647. if (src == endOfLine) {
  648. // ignore blank lines
  649. continue;
  650. }
  651. dest = Enum->CurLine;
  652. do {
  653. *dest++ = *src++;
  654. } while (src < endOfLine);
  655. *dest = 0;
  656. result = TRUE;
  657. break;
  658. }
  659. if (!result) {
  660. //
  661. // No more files to enum.
  662. //
  663. SpAbortWin9xFileEnum(Enum);
  664. return FALSE;
  665. }
  666. return result;
  667. }
  668. /*++
  669. Routine Description:
  670. SpEnumFirstWin9xFile is responsible for initializing the enumeration of a
  671. win9x data file. The function then calls EnumNextWin9xFile to fill in the
  672. rest of the necessary fields of the enumeration structure.
  673. Arguments:
  674. Enum - A pointer to a WIN9XDATFILEENUM structure. It is initialized by
  675. this function.
  676. WinntSif - A pointer to a valid Sif File. It is used to retrieve
  677. information about the location of the dat file to be enumerated.
  678. DatFile - The name of the DAT file to be enumerated.
  679. Return Value:
  680. TRUE if the enumeration was succesffuly initalized and there was something
  681. to enumerate, FALSE otherwise.
  682. --*/
  683. BOOL
  684. SpEnumFirstWin9xFile (
  685. IN PWIN9XDATFILEENUM Enum,
  686. IN PVOID WinntSif,
  687. IN PCWSTR DatFile
  688. )
  689. {
  690. NTSTATUS status;
  691. UINT fileSize;
  692. //
  693. // Open the dat file..
  694. //
  695. Enum -> FileHandle = SpOpenWin9xDatFile (DatFile, WinntSif);
  696. if (Enum -> FileHandle == INVALID_HANDLE_VALUE) {
  697. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error opening %ws data file..\n",DatFile));
  698. return FALSE;
  699. }
  700. //
  701. // Get the file size.
  702. //
  703. status = SpGetFileSize (Enum->FileHandle, &fileSize);
  704. if(!NT_SUCCESS(status)) {
  705. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error getting file size.\n"));
  706. ZwClose (Enum -> FileHandle);
  707. return FALSE;
  708. }
  709. //
  710. // Map the file.
  711. //
  712. status = SpMapEntireFile(
  713. Enum -> FileHandle,
  714. &(Enum -> FileSection),
  715. &(Enum -> NextLine),
  716. TRUE
  717. );
  718. if(!NT_SUCCESS(status)) {
  719. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error attempting to map file.\n"));
  720. ZwClose (Enum -> FileHandle);
  721. return FALSE;
  722. }
  723. Enum->EndOfFile = (PWSTR) ((PBYTE) Enum->NextLine + fileSize);
  724. Enum->UnMapAddress = Enum->NextLine;
  725. //
  726. // Pass Unicode Signature..
  727. //
  728. Enum -> NextLine += 1;
  729. //
  730. // Call EnumNext.
  731. //
  732. return SpEnumNextWin9xFile (Enum);
  733. }
  734. BOOLEAN
  735. SppWriteToFile (
  736. IN HANDLE FileHandle,
  737. IN PVOID Data,
  738. IN UINT DataSize,
  739. IN OUT PLARGE_INTEGER WritePos OPTIONAL
  740. )
  741. {
  742. NTSTATUS status;
  743. IO_STATUS_BLOCK ioStatusBlock;
  744. if (!DataSize) {
  745. return TRUE;
  746. }
  747. status = ZwWriteFile (
  748. FileHandle,
  749. NULL,
  750. NULL,
  751. NULL,
  752. &ioStatusBlock,
  753. Data,
  754. DataSize,
  755. WritePos,
  756. NULL
  757. );
  758. if (!NT_SUCCESS (status)) {
  759. KdPrintEx ((
  760. DPFLTR_SETUP_ID,
  761. DPFLTR_ERROR_LEVEL,
  762. "SETUP: SppWriteToFile failed with status %x\n",
  763. status
  764. ));
  765. } else if (WritePos) {
  766. ASSERT (ioStatusBlock.Information == DataSize);
  767. WritePos->QuadPart += (LONGLONG) DataSize;
  768. }
  769. return NT_SUCCESS (status);
  770. }
  771. BOOLEAN
  772. SppReadFromFile (
  773. IN HANDLE FileHandle,
  774. OUT PVOID Data,
  775. IN UINT DataBufferSize,
  776. OUT PINT BytesRead,
  777. IN OUT PLARGE_INTEGER ReadPos OPTIONAL
  778. )
  779. {
  780. NTSTATUS status;
  781. IO_STATUS_BLOCK ioStatusBlock;
  782. ioStatusBlock.Information = 0;
  783. status = ZwReadFile (
  784. FileHandle,
  785. NULL,
  786. NULL,
  787. NULL,
  788. &ioStatusBlock,
  789. Data,
  790. DataBufferSize,
  791. ReadPos,
  792. NULL
  793. );
  794. if (status != STATUS_END_OF_FILE && !NT_SUCCESS (status)) {
  795. KdPrintEx ((
  796. DPFLTR_SETUP_ID,
  797. DPFLTR_ERROR_LEVEL,
  798. "SETUP: SppReadFromFile failed with status %x\n",
  799. status
  800. ));
  801. return FALSE;
  802. }
  803. *BytesRead = ioStatusBlock.Information;
  804. if (ReadPos) {
  805. ReadPos->QuadPart += (LONGLONG) ioStatusBlock.Information;
  806. }
  807. return TRUE;
  808. }
  809. BOOLEAN
  810. SppCloseBackupImage (
  811. IN BACKUP_IMAGE_HANDLE BackupImageHandle,
  812. IN PBACKUP_IMAGE_HEADER ImageHeader, OPTIONAL
  813. IN PWSTR JournalFile OPTIONAL
  814. )
  815. {
  816. BOOLEAN result = FALSE;
  817. NTSTATUS status;
  818. IO_STATUS_BLOCK ioStatusBlock;
  819. OBJECT_ATTRIBUTES obja = { 0 };
  820. UNICODE_STRING unicodeString = { 0 };
  821. HANDLE journalHandle = NULL;
  822. JOURNALSTATUS journalStatus;
  823. if (BackupImageHandle != INVALID_HANDLE_VALUE
  824. && BackupImageHandle != NULL
  825. ) {
  826. PVOID CabHandle = BackupImageHandle->CabHandle;
  827. if (CabHandle != NULL && CabHandle != INVALID_HANDLE_VALUE
  828. ) {
  829. BackupImageHandle->CabHandle = NULL;
  830. ASSERT(BackupImageHandle->CloseCabinet != NULL);
  831. result = BackupImageHandle->CloseCabinet(CabHandle) ? TRUE : FALSE; // ?: to convert BOOL<->BOOLEAN
  832. }
  833. }
  834. if (result) {
  835. //
  836. // If JournalFile is specified, mark it as complete.
  837. //
  838. if (JournalFile) {
  839. SpDeleteFile (JournalFile, NULL, NULL);
  840. INIT_OBJA (&obja, &unicodeString, JournalFile);
  841. status = ZwCreateFile (
  842. &journalHandle,
  843. SYNCHRONIZE | FILE_GENERIC_WRITE,
  844. &obja,
  845. &ioStatusBlock,
  846. NULL,
  847. FILE_ATTRIBUTE_NORMAL,
  848. 0,
  849. FILE_CREATE,
  850. FILE_SYNCHRONOUS_IO_NONALERT|FILE_WRITE_THROUGH,
  851. NULL,
  852. 0
  853. );
  854. if (NT_SUCCESS(status)) {
  855. journalStatus = BACKUP_COMPLETE;
  856. SppWriteToFile (journalHandle, &journalStatus, sizeof (journalStatus), NULL);
  857. ZwClose (journalHandle);
  858. } else {
  859. KdPrintEx ((
  860. DPFLTR_SETUP_ID,
  861. DPFLTR_ERROR_LEVEL,
  862. "SETUP: Unable to create %ws\n",
  863. JournalFile
  864. ));
  865. return FALSE;
  866. }
  867. }
  868. }
  869. return result;
  870. }
  871. VOID
  872. SpAppendToBaseName(
  873. PWSTR String,
  874. PCWSTR StringToAppend
  875. )
  876. /*++
  877. String is assumed to have enough room already.
  878. --*/
  879. {
  880. const PWSTR Dot = wcsrchr(String, '.');
  881. if (Dot != NULL) {
  882. const SIZE_T StringToAppendLen = wcslen(StringToAppend);
  883. RtlMoveMemory(Dot + 1 + StringToAppendLen, Dot + 1, (wcslen(Dot + 1) + 1) * sizeof(WCHAR));
  884. RtlMoveMemory(Dot, StringToAppend, StringToAppendLen * sizeof(WCHAR));
  885. *(Dot + StringToAppendLen) = '.';
  886. } else {
  887. wcscat(String, StringToAppend);
  888. }
  889. }
  890. BACKUP_IMAGE_HANDLE
  891. SppOpenBackupImage (
  892. IN BOOLEAN Create,
  893. OUT PBACKUP_IMAGE_HEADER Header,
  894. OUT PLARGE_INTEGER ImagePos, OPTIONAL
  895. OUT PWSTR JournalFile, OPTIONAL
  896. IN TCOMP CompressionType,
  897. OUT BOOLEAN *InvalidHandleMeansFail OPTIONAL
  898. )
  899. {
  900. PVOID CabHandle;
  901. NTSTATUS status = STATUS_SUCCESS;
  902. IO_STATUS_BLOCK ioStatusBlock;
  903. OBJECT_ATTRIBUTES obja = { 0 };
  904. UNICODE_STRING unicodeString = { 0 };
  905. HANDLE journalHandle = NULL;
  906. JOURNALSTATUS journalStatus;
  907. PWSTR p = NULL;
  908. BOOL success = FALSE;
  909. BACKUP_IMAGE_HANDLE imageHandle = INVALID_HANDLE_VALUE;
  910. BOOL Success = FALSE;
  911. PWSTR subDir = NULL;
  912. PDISK_REGION region = NULL;
  913. PWSTR backupDir = NULL;
  914. PWSTR ntRoot = NULL;
  915. PWSTR backupFileOb = NULL;
  916. PWSTR backupJournalOb = NULL;
  917. UINT dontCare;
  918. PWSTR backupLeafFile = NULL;
  919. BOOL backupDirIsRoot = FALSE;
  920. PWSTR backupImage = NULL;
  921. WCHAR CompressionTypeString[sizeof(CompressionType) * 8];
  922. if (InvalidHandleMeansFail) {
  923. *InvalidHandleMeansFail = TRUE;
  924. }
  925. //
  926. // Alloc buffers
  927. //
  928. ntRoot = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  929. backupDir = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  930. backupFileOb = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  931. backupJournalOb = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  932. backupImage = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  933. if (!ntRoot
  934. || !backupDir
  935. || !backupFileOb
  936. || !backupJournalOb
  937. || !backupImage
  938. ) {
  939. goto cleanup;
  940. }
  941. //
  942. // Obtain the backup image path from winnt.sif. The return ptr points
  943. // to the SIF parse data structures.
  944. //
  945. ASSERT (WinntSifHandle);
  946. p = SpGetSectionKeyIndex (
  947. WinntSifHandle,
  948. WINNT_DATA_W,
  949. WINNT_D_BACKUP_IMAGE_W,
  950. 0
  951. );
  952. if (!p) {
  953. if (Create) {
  954. KdPrintEx((
  955. DPFLTR_SETUP_ID,
  956. DPFLTR_INFO_LEVEL,
  957. "SETUP: Backup image is not specified; not creating a backup\n"
  958. ));
  959. } else {
  960. KdPrintEx((
  961. DPFLTR_SETUP_ID,
  962. DPFLTR_ERROR_LEVEL,
  963. "SETUP: Backup image is not specified; cannot perform a restore\n"
  964. ));
  965. }
  966. goto cleanup;
  967. }
  968. SpFormatStringW(CompressionTypeString, RTL_NUMBER_OF(CompressionTypeString), L"%d", (int)CompressionType);
  969. wcscpy(backupImage, p);
  970. #if TRY_ALL_COMPRESSION_ALGORITHMS
  971. SpAppendToBaseName(backupImage, CompressionTypeString);
  972. #endif
  973. //
  974. // The backup spec is a DOS path. Convert it into an NT object path.
  975. //
  976. if (!SpNtNameFromDosPath (
  977. backupImage,
  978. backupFileOb,
  979. ACTUAL_MAX_PATH * sizeof (WCHAR),
  980. PartitionOrdinalCurrent
  981. )) {
  982. KdPrintEx((
  983. DPFLTR_SETUP_ID,
  984. DPFLTR_ERROR_LEVEL,
  985. "SETUP: Cannot convert path %ws to an NT path\n",
  986. backupImage
  987. ));
  988. goto cleanup;
  989. }
  990. //
  991. // Check if backup.$$$ exists
  992. //
  993. wcscpy (backupJournalOb, backupFileOb);
  994. p = wcsrchr (backupJournalOb, L'\\');
  995. if (p) {
  996. p = wcsrchr (p, L'.');
  997. }
  998. if (!p) {
  999. KdPrintEx ((
  1000. DPFLTR_SETUP_ID,
  1001. DPFLTR_ERROR_LEVEL,
  1002. "SETUP: "__FUNCTION__": Invalid backup path spec: %ws\n",
  1003. backupFileOb
  1004. ));
  1005. goto cleanup;
  1006. }
  1007. wcscpy (p + 1, L"$$$");
  1008. if (JournalFile) {
  1009. wcscpy (JournalFile, backupJournalOb);
  1010. }
  1011. INIT_OBJA (&obja, &unicodeString, backupJournalOb);
  1012. status = ZwCreateFile (
  1013. &journalHandle,
  1014. SYNCHRONIZE | FILE_GENERIC_READ,
  1015. &obja,
  1016. &ioStatusBlock,
  1017. NULL,
  1018. FILE_ATTRIBUTE_NORMAL,
  1019. 0,
  1020. FILE_OPEN,
  1021. FILE_SYNCHRONOUS_IO_NONALERT,
  1022. NULL,
  1023. 0
  1024. );
  1025. if (NT_SUCCESS (status)) {
  1026. if (!SppReadFromFile (
  1027. journalHandle,
  1028. &journalStatus,
  1029. sizeof (journalStatus),
  1030. &dontCare,
  1031. NULL
  1032. )) {
  1033. journalStatus = BACKUP_DOESNT_EXIST;
  1034. KdPrintEx((
  1035. DPFLTR_SETUP_ID,
  1036. DPFLTR_ERROR_LEVEL,
  1037. "SETUP: Journal exist but can't be read\n"
  1038. ));
  1039. }
  1040. ZwClose (journalHandle);
  1041. } else {
  1042. journalStatus = BACKUP_DOESNT_EXIST;
  1043. KdPrintEx((
  1044. DPFLTR_SETUP_ID,
  1045. DPFLTR_ERROR_LEVEL,
  1046. "BUGBUG: Journal doesn't exist\n"
  1047. ));
  1048. }
  1049. if (((journalStatus == BACKUP_COMPLETE) && Create) ||
  1050. (journalStatus == BACKUP_SKIPPED_BY_USER)
  1051. ) {
  1052. KdPrintEx((
  1053. DPFLTR_SETUP_ID,
  1054. DPFLTR_ERROR_LEVEL,
  1055. "SETUP: Backup is done or is disabled\n"
  1056. ));
  1057. if (InvalidHandleMeansFail) {
  1058. *InvalidHandleMeansFail = FALSE;
  1059. }
  1060. goto cleanup;
  1061. }
  1062. //
  1063. // form seperate strings for the directory and leaf
  1064. //
  1065. wcscpy (backupDir, backupFileOb);
  1066. p = wcsrchr (backupDir, L'\\');
  1067. if (p != NULL && p > wcschr (backupDir, L'\\')) {
  1068. *p = 0;
  1069. backupLeafFile = p + 1;
  1070. backupDirIsRoot = FALSE;
  1071. } else if (backupDir[0] == '\\') {
  1072. ASSERTMSG("This is very strange, we got a path in the NT root.", FALSE);
  1073. backupDir[1] = 0;
  1074. backupLeafFile = &backupDir[2];
  1075. backupDirIsRoot = TRUE;
  1076. }
  1077. //
  1078. // Open the source file
  1079. //
  1080. if (Create) {
  1081. //
  1082. // If not the root directory, create the directory now
  1083. //
  1084. if (!backupDirIsRoot) {
  1085. region = SppRegionFromFullNtName (backupDir, PartitionOrdinalCurrent, &subDir);
  1086. if (!region) {
  1087. KdPrintEx ((
  1088. DPFLTR_SETUP_ID,
  1089. DPFLTR_ERROR_LEVEL,
  1090. "SETUP: "__FUNCTION__" - Can't get region for backup image\n"
  1091. ));
  1092. } else {
  1093. SpNtNameFromRegion (region, ntRoot, ACTUAL_MAX_PATH * sizeof (WCHAR), PartitionOrdinalCurrent);
  1094. SpCreateDirectory (ntRoot, NULL, subDir, 0, 0);
  1095. }
  1096. }
  1097. //
  1098. // If journal pre-existed, then delete the incomplete backup image and
  1099. // journal
  1100. //
  1101. if (journalStatus == BACKUP_IN_PROGRESS) {
  1102. // error ignored for now -- will be caught below
  1103. SpDeleteFile (backupFileOb, NULL, NULL);
  1104. SpDeleteFile (backupJournalOb, NULL, NULL);
  1105. KdPrintEx((
  1106. DPFLTR_SETUP_ID,
  1107. DPFLTR_ERROR_LEVEL,
  1108. "SETUP: Restarting backup process\n"
  1109. ));
  1110. } else {
  1111. KdPrintEx((
  1112. DPFLTR_SETUP_ID,
  1113. DPFLTR_ERROR_LEVEL,
  1114. "BUGBUG: Backup doesn't exist\n"
  1115. ));
  1116. ASSERT (journalStatus == BACKUP_DOESNT_EXIST);
  1117. }
  1118. //
  1119. // Create a new journal file
  1120. //
  1121. INIT_OBJA (&obja, &unicodeString, backupJournalOb);
  1122. status = ZwCreateFile (
  1123. &journalHandle,
  1124. SYNCHRONIZE | FILE_GENERIC_WRITE,
  1125. &obja,
  1126. &ioStatusBlock,
  1127. NULL,
  1128. FILE_ATTRIBUTE_NORMAL,
  1129. 0,
  1130. FILE_CREATE,
  1131. FILE_SYNCHRONOUS_IO_NONALERT|FILE_WRITE_THROUGH,
  1132. NULL,
  1133. 0
  1134. );
  1135. if (NT_SUCCESS(status)) {
  1136. journalStatus = BACKUP_IN_PROGRESS;
  1137. SppWriteToFile (journalHandle, &journalStatus, sizeof (journalStatus), NULL);
  1138. ZwClose (journalHandle);
  1139. } else {
  1140. KdPrintEx ((
  1141. DPFLTR_SETUP_ID,
  1142. DPFLTR_ERROR_LEVEL,
  1143. "SETUP: Unable to create %ws\n",
  1144. backupJournalOb
  1145. ));
  1146. goto cleanup;
  1147. }
  1148. } else {
  1149. //
  1150. // If open attempt and journal exists, then fail
  1151. //
  1152. if (journalStatus != BACKUP_COMPLETE) {
  1153. KdPrintEx ((
  1154. DPFLTR_SETUP_ID,
  1155. DPFLTR_ERROR_LEVEL,
  1156. "SETUP: Can't restore incomplete backup image %ws\n",
  1157. backupFileOb
  1158. ));
  1159. goto cleanup;
  1160. }
  1161. }
  1162. //
  1163. // Create/Open backup image
  1164. //
  1165. imageHandle = (BACKUP_IMAGE_HANDLE)SpMemAlloc(sizeof(*imageHandle));
  1166. if (imageHandle == NULL) {
  1167. goto cleanup;
  1168. }
  1169. RtlZeroMemory(imageHandle, sizeof(*imageHandle));
  1170. if (Create) {
  1171. CabHandle = SpCabCreateCabinetW(backupDir, backupLeafFile, NULL, 0);
  1172. imageHandle->CloseCabinet = SpCabFlushAndCloseCabinet;
  1173. } else {
  1174. CabHandle = SpCabOpenCabinetW(backupFileOb);
  1175. imageHandle->CloseCabinet = SpCabCloseCabinet;
  1176. }
  1177. if (CabHandle == NULL || CabHandle == INVALID_HANDLE_VALUE) {
  1178. goto cleanup;
  1179. }
  1180. imageHandle->CabHandle = CabHandle;
  1181. Success = TRUE;
  1182. cleanup:
  1183. if (!Success) {
  1184. SppCloseBackupImage (imageHandle, NULL, NULL);
  1185. imageHandle = INVALID_HANDLE_VALUE;
  1186. }
  1187. SpMemFree (ntRoot);
  1188. SpMemFree (backupDir);
  1189. SpMemFree (backupFileOb);
  1190. SpMemFree (backupJournalOb);
  1191. return imageHandle;
  1192. }
  1193. #define BLOCKSIZE (65536*4)
  1194. BOOLEAN
  1195. SppPutFileInBackupImage (
  1196. IN BACKUP_IMAGE_HANDLE ImageHandle,
  1197. IN OUT PLARGE_INTEGER ImagePos,
  1198. IN OUT PBACKUP_IMAGE_HEADER ImageHeader,
  1199. IN PWSTR DosPath
  1200. )
  1201. {
  1202. PWSTR ntPath;
  1203. BACKUP_FILE_HEADER fileHeader;
  1204. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1205. NTSTATUS status = STATUS_UNSUCCESSFUL;
  1206. IO_STATUS_BLOCK ioStatusBlock;
  1207. OBJECT_ATTRIBUTES obja;
  1208. UNICODE_STRING unicodeString;
  1209. FILE_STANDARD_INFORMATION stdInfo;
  1210. BOOLEAN fail = TRUE;
  1211. BOOLEAN truncate = FALSE;
  1212. BOOLEAN returnValue = FALSE;
  1213. PBYTE block = NULL;
  1214. INT bytesRead;
  1215. FILE_END_OF_FILE_INFORMATION eofInfo;
  1216. PWSTR fileName;
  1217. ntPath = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  1218. if (!ntPath) {
  1219. KdPrintEx((
  1220. DPFLTR_SETUP_ID,
  1221. DPFLTR_ERROR_LEVEL,
  1222. "SETUP: Can't allocate buffer\n"
  1223. ));
  1224. goto cleanup;
  1225. }
  1226. eofInfo.EndOfFile.QuadPart = ImagePos->QuadPart;
  1227. KdPrintEx((
  1228. DPFLTR_SETUP_ID,
  1229. DPFLTR_TRACE_LEVEL,
  1230. "SETUP: Backing up %ws\n",
  1231. DosPath
  1232. ));
  1233. fileName = wcsrchr (DosPath, L'\\');
  1234. if (!fileName) {
  1235. fileName = DosPath;
  1236. } else {
  1237. fileName++;
  1238. }
  1239. SpDisplayStatusText (SP_STAT_BACKING_UP_WIN9X_FILE, DEFAULT_STATUS_ATTRIBUTE, fileName);
  1240. //
  1241. // Convert the backup file's DOS path into an NT path
  1242. //
  1243. if (!SpNtNameFromDosPath (
  1244. DosPath,
  1245. ntPath,
  1246. ACTUAL_MAX_PATH * sizeof (WCHAR),
  1247. PartitionOrdinalCurrent
  1248. )) {
  1249. KdPrintEx((
  1250. DPFLTR_SETUP_ID,
  1251. DPFLTR_ERROR_LEVEL,
  1252. "SETUP: Cannot convert path %ws to an NT path\n",
  1253. DosPath
  1254. ));
  1255. goto cleanup;
  1256. }
  1257. status = SpCabAddFileToCabinetW (ImageHandle->CabHandle, ntPath, DosPath);//ntPath
  1258. if (!NT_SUCCESS (status)) {
  1259. goto cleanup;
  1260. }
  1261. returnValue = TRUE;
  1262. cleanup:
  1263. if (!returnValue) {
  1264. //
  1265. // File could not be added to the image. Allow user to continue.
  1266. //
  1267. if (status != STATUS_OBJECT_NAME_NOT_FOUND &&
  1268. status != STATUS_OBJECT_NAME_INVALID &&
  1269. status != STATUS_OBJECT_PATH_INVALID &&
  1270. status != STATUS_OBJECT_PATH_NOT_FOUND &&
  1271. status != STATUS_FILE_IS_A_DIRECTORY
  1272. ) {
  1273. KdPrintEx((
  1274. DPFLTR_SETUP_ID,
  1275. DPFLTR_ERROR_LEVEL,
  1276. "SETUP: Can't add %ws to backup CAB (%08Xh)\n",
  1277. DosPath,
  1278. status
  1279. ));
  1280. if (SpNonCriticalErrorWithContinue (SP_SCRN_BACKUP_SAVE_FAILED, DosPath, (PWSTR) status)) {
  1281. returnValue = TRUE;
  1282. }
  1283. CLEAR_CLIENT_SCREEN();
  1284. } else {
  1285. //
  1286. // Ignore errors that can be caused by users altering the system
  1287. // while setup is running, or by bad migration dll info
  1288. //
  1289. returnValue = TRUE;
  1290. }
  1291. }
  1292. SpMemFree (ntPath);
  1293. return returnValue;
  1294. }
  1295. #if DBG
  1296. VOID
  1297. SpDbgPrintElapsedTime(
  1298. PCSTR Prefix,
  1299. CONST LARGE_INTEGER* ElapsedTime
  1300. )
  1301. {
  1302. TIME_FIELDS TimeFields;
  1303. RtlTimeToElapsedTimeFields((PLARGE_INTEGER)ElapsedTime, &TimeFields);
  1304. KdPrint(("%s: %d:%d.%d\n", Prefix, (int)TimeFields.Minute, (int)TimeFields.Second, (int)TimeFields.Milliseconds));
  1305. }
  1306. #endif
  1307. BOOLEAN
  1308. SpAddRollbackBootOption (
  1309. BOOLEAN DefaultBootOption
  1310. )
  1311. {
  1312. PWSTR data;
  1313. BOOLEAN result;
  1314. data = SpGetSectionKeyIndex (SifHandle, SIF_SETUPDATA, L"LoadIdentifierCancel", 0);
  1315. if (!data) {
  1316. SpFatalSifError (SifHandle, SIF_SETUPDATA, L"LoadIdentifierCancel",0,0);
  1317. }
  1318. result = SppCreateTextModeBootEntry (data, L"/rollback", DefaultBootOption);
  1319. return result;
  1320. }
  1321. BOOLEAN
  1322. SpBackUpWin9xFiles (
  1323. IN PVOID WinntSif,
  1324. IN TCOMP CompressionType
  1325. )
  1326. /*++
  1327. Routine Description:
  1328. SpBackUpWin9xFiles takes full DOS paths in the BACKUP.TXT file
  1329. and puts them in a temporary location specified in the WINNT.SIF file.
  1330. the format of this file is
  1331. backupfile1.ext
  1332. backupfile2.ext
  1333. ...
  1334. Arguments:
  1335. WinntSif: Handle to Winnt.Sif
  1336. Return Value:
  1337. TRUE if a backup image was made, FALSE otherwise.
  1338. --*/
  1339. {
  1340. WIN9XDATFILEENUM e;
  1341. BACKUP_IMAGE_HANDLE backupImage;
  1342. BACKUP_IMAGE_HEADER header;
  1343. LARGE_INTEGER imagePos;
  1344. PWSTR p;
  1345. BOOLEAN result = FALSE;
  1346. PWSTR journalFile = NULL;
  1347. PWSTR data;
  1348. IO_STATUS_BLOCK ioStatusBlock;
  1349. OBJECT_ATTRIBUTES obja;
  1350. UNICODE_STRING unicodeString;
  1351. HANDLE journalHandle;
  1352. JOURNALSTATUS journalStatus;
  1353. NTSTATUS status;
  1354. UINT currentFile;
  1355. UINT percentDone;
  1356. UINT fileCount;
  1357. PWSTR srcBootIni;
  1358. PWSTR backupBootIni;
  1359. BOOLEAN askForRetry = FALSE;
  1360. //
  1361. // Get the backup image path
  1362. //
  1363. p = SpGetSectionKeyIndex (
  1364. WinntSifHandle,
  1365. WINNT_DATA_W,
  1366. WINNT_D_BACKUP_LIST_W,
  1367. 0
  1368. );
  1369. if (!p) {
  1370. KdPrintEx((
  1371. DPFLTR_SETUP_ID,
  1372. DPFLTR_ERROR_LEVEL,
  1373. "SETUP: Backup file list is not specified; cannot perform a backup\n"
  1374. ));
  1375. goto cleanup;
  1376. }
  1377. journalFile = SpMemAlloc (MAX_PATH * sizeof (WCHAR));
  1378. if (!journalFile) {
  1379. KdPrintEx((
  1380. DPFLTR_SETUP_ID,
  1381. DPFLTR_ERROR_LEVEL,
  1382. "SETUP: Can't allocate journal buffer\n"
  1383. ));
  1384. goto cleanup;
  1385. }
  1386. //
  1387. // Open the backup image
  1388. //
  1389. backupImage = SppOpenBackupImage (
  1390. TRUE,
  1391. &header,
  1392. &imagePos,
  1393. journalFile,
  1394. CompressionType,
  1395. &askForRetry
  1396. );
  1397. if (backupImage == INVALID_HANDLE_VALUE) {
  1398. goto cleanup;
  1399. }
  1400. askForRetry = TRUE;
  1401. backupImage->CabHandle->CompressionType = CompressionType;
  1402. //
  1403. // Process all files listed in backup.txt
  1404. //
  1405. result = TRUE;
  1406. fileCount = 0;
  1407. if (SpEnumFirstWin9xFile (&e, WinntSif, p)) {
  1408. do {
  1409. fileCount++;
  1410. } while (SpEnumNextWin9xFile (&e));
  1411. }
  1412. SendSetupProgressEvent (BackupEvent, BackupStartEvent, &fileCount);
  1413. currentFile = 0;
  1414. if (SpEnumFirstWin9xFile (&e, WinntSif, p)) {
  1415. do {
  1416. if (!SppPutFileInBackupImage (backupImage, &imagePos, &header, e.CurLine)) {
  1417. result = FALSE;
  1418. break;
  1419. }
  1420. currentFile++;
  1421. percentDone = currentFile * 100 / fileCount;
  1422. SendSetupProgressEvent (
  1423. BackupEvent,
  1424. OneFileBackedUpEvent,
  1425. &percentDone
  1426. );
  1427. } while (SpEnumNextWin9xFile (&e));
  1428. } else {
  1429. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error in SpBackUpWin9xFiles No files to enumerate.\n"));
  1430. }
  1431. SendSetupProgressEvent (BackupEvent, BackupEndEvent, NULL);
  1432. //
  1433. // Close the backup image. We leave the journal file in all cases.
  1434. //
  1435. if (!SppCloseBackupImage (backupImage, &header, result ? journalFile : NULL)) {
  1436. result = FALSE;
  1437. }
  1438. if (result) {
  1439. //
  1440. // Backup was successful
  1441. //
  1442. askForRetry = FALSE;
  1443. //
  1444. // Remove the MS-DOS boot.ini entry, and add a cancel entry.
  1445. //
  1446. result = SpAddRollbackBootOption (FALSE);
  1447. if (result) {
  1448. data = SpGetSectionKeyIndex (SifHandle, SIF_SETUPDATA, L"LoadIdentifier", 0);
  1449. if (!data) {
  1450. SpFatalSifError (SifHandle, SIF_SETUPDATA, L"LoadIdentifier",0,0);
  1451. }
  1452. result = SppCreateTextModeBootEntry (data, NULL, TRUE);
  1453. }
  1454. if (result) {
  1455. result = SpFlushBootVars();
  1456. }
  1457. //
  1458. // Make a backup of setup in-progress boot.ini in ~BT directory, for
  1459. // use by PSS
  1460. //
  1461. if (!NtBootDevicePath) {
  1462. ASSERT(NtBootDevicePath);
  1463. result = FALSE;
  1464. }
  1465. if (result) {
  1466. wcscpy (TemporaryBuffer, NtBootDevicePath);
  1467. SpConcatenatePaths (TemporaryBuffer, L"boot.ini");
  1468. srcBootIni = SpDupStringW (TemporaryBuffer);
  1469. if (!srcBootIni) {
  1470. result = FALSE;
  1471. }
  1472. }
  1473. if (result) {
  1474. wcscpy (TemporaryBuffer, NtBootDevicePath);
  1475. SpConcatenatePaths (TemporaryBuffer, L"$WIN_NT$.~BT\\bootini.bak");
  1476. backupBootIni = SpDupStringW (TemporaryBuffer);
  1477. if (!backupBootIni) {
  1478. SpMemFree (srcBootIni);
  1479. result = FALSE;
  1480. }
  1481. }
  1482. if (result) {
  1483. //
  1484. // If this fails, keep going.
  1485. //
  1486. SpCopyFileUsingNames (srcBootIni, backupBootIni, 0, COPY_NODECOMP|COPY_NOVERSIONCHECK);
  1487. SpMemFree (srcBootIni);
  1488. SpMemFree (backupBootIni);
  1489. }
  1490. }
  1491. cleanup:
  1492. if (askForRetry) {
  1493. //
  1494. // The backup image is bad. Notify the user but allow them continue.
  1495. // Delete the journal file so that any future restarts of textmode
  1496. // cause the backup process to be skipped.
  1497. //
  1498. //
  1499. SpNonCriticalErrorNoRetry (SP_SCRN_BACKUP_IMAGE_FAILED, NULL, NULL);
  1500. CLEAR_CLIENT_SCREEN();
  1501. //
  1502. // Create a new journal file, indicating that backup is disabled
  1503. //
  1504. INIT_OBJA (&obja, &unicodeString, journalFile);
  1505. SpDeleteFile (journalFile, NULL, NULL);
  1506. status = ZwCreateFile (
  1507. &journalHandle,
  1508. SYNCHRONIZE | FILE_GENERIC_WRITE,
  1509. &obja,
  1510. &ioStatusBlock,
  1511. NULL,
  1512. FILE_ATTRIBUTE_NORMAL,
  1513. 0,
  1514. FILE_CREATE,
  1515. FILE_SYNCHRONOUS_IO_NONALERT|FILE_WRITE_THROUGH,
  1516. NULL,
  1517. 0
  1518. );
  1519. if (NT_SUCCESS(status)) {
  1520. journalStatus = BACKUP_SKIPPED_BY_USER;
  1521. SppWriteToFile (journalHandle, &journalStatus, sizeof (journalStatus), NULL);
  1522. ZwClose (journalHandle);
  1523. }
  1524. }
  1525. SpMemFree (journalFile);
  1526. return result;
  1527. }
  1528. typedef struct tagHASHITEM {
  1529. struct tagHASHITEM *Next;
  1530. PCWSTR String;
  1531. } HASHITEM, *PHASHITEM;
  1532. typedef struct {
  1533. PHASHITEM HashItem;
  1534. INT BucketNumber;
  1535. BOOLEAN First;
  1536. } HASHITEM_ENUM, *PHASHITEM_ENUM;
  1537. HASHITEM g_UninstallHashTable[MAX_PATH];
  1538. UINT
  1539. SppComputeHash (
  1540. IN PCWSTR String
  1541. )
  1542. {
  1543. return MAX_PATH - 1 - (wcslen (String) % MAX_PATH);
  1544. }
  1545. PHASHITEM
  1546. SppFindInHashTable (
  1547. IN PCWSTR DosFilePath,
  1548. OUT PUINT OutHashValue, OPTIONAL
  1549. OUT PHASHITEM *LastItem OPTIONAL
  1550. )
  1551. {
  1552. UINT hashValue;
  1553. PHASHITEM item;
  1554. hashValue = SppComputeHash (DosFilePath);
  1555. if (OutHashValue) {
  1556. *OutHashValue = hashValue;
  1557. }
  1558. item = &g_UninstallHashTable[hashValue];
  1559. if (LastItem) {
  1560. *LastItem = NULL;
  1561. }
  1562. if (item->String) {
  1563. do {
  1564. if (_wcsicmp (item->String, DosFilePath) == 0) {
  1565. break;
  1566. }
  1567. if (LastItem) {
  1568. *LastItem = item;
  1569. }
  1570. item = item->Next;
  1571. } while (item);
  1572. } else {
  1573. item = NULL;
  1574. }
  1575. return item;
  1576. }
  1577. BOOLEAN
  1578. SppPutInHashTable (
  1579. IN PCWSTR DosFilePath
  1580. )
  1581. {
  1582. PHASHITEM newItem;
  1583. PHASHITEM parentItem;
  1584. UINT hashValue;
  1585. if (SppFindInHashTable (DosFilePath, &hashValue, &parentItem)) {
  1586. return TRUE;
  1587. }
  1588. if (!parentItem) {
  1589. g_UninstallHashTable[hashValue].String = SpDupStringW (DosFilePath);
  1590. return g_UninstallHashTable[hashValue].String != NULL;
  1591. }
  1592. newItem = SpMemAlloc (sizeof (HASHITEM));
  1593. if (!newItem) {
  1594. return FALSE;
  1595. }
  1596. newItem->Next = NULL;
  1597. newItem->String = SpDupStringW (DosFilePath);
  1598. parentItem->Next = newItem;
  1599. return TRUE;
  1600. }
  1601. BOOLEAN
  1602. SppPutParentsInHashTable (
  1603. IN PCWSTR DosFilePath
  1604. )
  1605. {
  1606. PCWSTR s;
  1607. PWSTR subPath;
  1608. PWSTR p;
  1609. BOOLEAN result = FALSE;
  1610. s = SpDupStringW (DosFilePath);
  1611. if (s) {
  1612. subPath = wcschr (s, L'\\');
  1613. if (subPath) {
  1614. subPath++;
  1615. for (;;) {
  1616. p = wcsrchr (subPath, L'\\');
  1617. if (p) {
  1618. *p = 0;
  1619. result = SppPutInHashTable (s);
  1620. } else {
  1621. break;
  1622. }
  1623. break; // for now, do not go all the way up the tree
  1624. }
  1625. }
  1626. SpMemFree ((PVOID) s);
  1627. }
  1628. return result;
  1629. }
  1630. PHASHITEM
  1631. SppEnumNextHashItem (
  1632. IN OUT PHASHITEM_ENUM EnumPtr
  1633. )
  1634. {
  1635. do {
  1636. if (!EnumPtr->HashItem) {
  1637. EnumPtr->BucketNumber += 1;
  1638. if (EnumPtr->BucketNumber >= MAX_PATH) {
  1639. break;
  1640. }
  1641. EnumPtr->HashItem = &g_UninstallHashTable[EnumPtr->BucketNumber];
  1642. if (EnumPtr->HashItem->String) {
  1643. EnumPtr->First = TRUE;
  1644. } else {
  1645. EnumPtr->HashItem = NULL;
  1646. }
  1647. } else {
  1648. EnumPtr->HashItem = EnumPtr->HashItem->Next;
  1649. EnumPtr->First = FALSE;
  1650. }
  1651. } while (!EnumPtr->HashItem);
  1652. return EnumPtr->HashItem;
  1653. }
  1654. PHASHITEM
  1655. SppEnumFirstHashItem (
  1656. OUT PHASHITEM_ENUM EnumPtr
  1657. )
  1658. {
  1659. EnumPtr->BucketNumber = -1;
  1660. EnumPtr->HashItem = NULL;
  1661. return SppEnumNextHashItem (EnumPtr);
  1662. }
  1663. VOID
  1664. SppEmptyHashTable (
  1665. VOID
  1666. )
  1667. {
  1668. HASHITEM_ENUM e;
  1669. PVOID freeMe = NULL;
  1670. if (SppEnumFirstHashItem (&e)) {
  1671. do {
  1672. ASSERT (e.HashItem->String);
  1673. SpMemFree ((PVOID) e.HashItem->String);
  1674. if (freeMe) {
  1675. SpMemFree (freeMe);
  1676. freeMe = NULL;
  1677. }
  1678. if (!e.First) {
  1679. freeMe = (PVOID) e.HashItem;
  1680. }
  1681. } while (SppEnumNextHashItem (&e));
  1682. }
  1683. if (freeMe) {
  1684. SpMemFree (freeMe);
  1685. }
  1686. RtlZeroMemory (g_UninstallHashTable, sizeof (g_UninstallHashTable));
  1687. }
  1688. /*BOOLEAN
  1689. SppIsDotOrDotDot(
  1690. PCWSTR s
  1691. )
  1692. {
  1693. return (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)));
  1694. }*/
  1695. BOOLEAN
  1696. SppEmptyDirProc (
  1697. IN PCWSTR Path,
  1698. IN PFILE_BOTH_DIR_INFORMATION DirInfo,
  1699. OUT PULONG ReturnData,
  1700. IN OUT PVOID DontCare
  1701. )
  1702. {
  1703. if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1704. return TRUE;
  1705. }
  1706. return FALSE;
  1707. }
  1708. BOOLEAN
  1709. SppIsDirEmpty (
  1710. IN PCWSTR NtPath
  1711. )
  1712. {
  1713. ENUMFILESRESULT result;
  1714. ULONG dontCare;
  1715. result = SpEnumFilesRecursive ((PWSTR) NtPath, SppEmptyDirProc, &dontCare, NULL);
  1716. return result == NormalReturn;
  1717. }
  1718. BOOLEAN
  1719. SppDelEmptyDirProc (
  1720. IN PCWSTR Path,
  1721. IN PFILE_BOTH_DIR_INFORMATION DirInfo,
  1722. OUT PULONG ReturnData,
  1723. IN OUT PVOID DontCare
  1724. )
  1725. {
  1726. PCWSTR subPath;
  1727. PWSTR p;
  1728. PWSTR end;
  1729. UINT bytesToCopy;
  1730. //
  1731. // If we find a file, fail. This must be checked before any deletion
  1732. // occurs.
  1733. //
  1734. if (!(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1735. return TRUE;
  1736. }
  1737. //
  1738. // Join Path with the enumerated directory. Contain the path to MAX_PATH.
  1739. //
  1740. wcscpy (TemporaryBuffer, Path);
  1741. if (DirInfo->FileNameLength) {
  1742. p = wcschr (TemporaryBuffer, 0);
  1743. bytesToCopy = DirInfo->FileNameLength;
  1744. end = (TemporaryBuffer + (ACTUAL_MAX_PATH) - 2) - (bytesToCopy / sizeof (WCHAR));
  1745. if (!p || p > end) {
  1746. KdPrintEx((
  1747. DPFLTR_SETUP_ID,
  1748. DPFLTR_ERROR_LEVEL,
  1749. "SETUP: Enumeration of %ws became too long\n",
  1750. Path
  1751. ));
  1752. return FALSE;
  1753. }
  1754. *p++ = L'\\';
  1755. RtlCopyMemory (p, DirInfo->FileName, bytesToCopy);
  1756. p[bytesToCopy / sizeof(WCHAR)] = 0;
  1757. /*
  1758. //
  1759. // Skip . and .. subdirs
  1760. //
  1761. if (SppIsDotOrDotDot (p)) {
  1762. return TRUE;
  1763. }
  1764. */
  1765. }
  1766. //
  1767. // Duplicate temp buffer and call ourselves recursively to delete
  1768. // any contained subdirs
  1769. //
  1770. subPath = SpDupStringW (TemporaryBuffer);
  1771. if (!subPath) {
  1772. KdPrintEx((
  1773. DPFLTR_SETUP_ID,
  1774. DPFLTR_ERROR_LEVEL,
  1775. "SETUP: SpDupStringW failed to allocate memory for %d bytes",
  1776. wcslen(TemporaryBuffer) * sizeof(WCHAR)));
  1777. return FALSE;
  1778. }
  1779. SppDelEmptyDir (subPath);
  1780. SpMemFree ((PVOID) subPath);
  1781. return TRUE;
  1782. }
  1783. BOOLEAN
  1784. SppDelEmptyDir (
  1785. IN PCWSTR NtPath
  1786. )
  1787. {
  1788. ENUMFILESRESULT result;
  1789. ULONG dontCare;
  1790. NTSTATUS status;
  1791. //
  1792. // Remove any empty subdirectories in NtPath
  1793. //
  1794. result = SpEnumFiles ((PWSTR) NtPath, SppDelEmptyDirProc, &dontCare, NULL);
  1795. if (result != NormalReturn) {
  1796. KdPrintEx((
  1797. DPFLTR_SETUP_ID,
  1798. DPFLTR_ERROR_LEVEL,
  1799. "SETUP: Failed to enumerate contents of %ws - status 0x%08X\n",
  1800. NtPath,
  1801. result
  1802. ));
  1803. }
  1804. //
  1805. // Now remove this subdirectory
  1806. //
  1807. status = SpSetAttributes ((PWSTR) NtPath, FILE_ATTRIBUTE_NORMAL);
  1808. if (!NT_SUCCESS (status)) {
  1809. KdPrintEx((
  1810. DPFLTR_SETUP_ID,
  1811. DPFLTR_ERROR_LEVEL,
  1812. "SETUP: Can't alter attributes of %ws - status 0x%08X\n",
  1813. NtPath,
  1814. status
  1815. ));
  1816. }
  1817. status = SpDeleteFileEx (
  1818. NtPath,
  1819. NULL,
  1820. NULL,
  1821. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  1822. FILE_OPEN_FOR_BACKUP_INTENT
  1823. );
  1824. if (!NT_SUCCESS (status)){
  1825. KdPrintEx((
  1826. DPFLTR_SETUP_ID,
  1827. DPFLTR_ERROR_LEVEL,
  1828. "SETUP: Can't delete %ws - status 0x%08X\n",
  1829. NtPath,
  1830. status
  1831. ));
  1832. }
  1833. return NT_SUCCESS (status);
  1834. }
  1835. VOID
  1836. SppCleanEmptyDirs (
  1837. VOID
  1838. )
  1839. {
  1840. HASHITEM_ENUM e;
  1841. PWSTR ntPath;
  1842. PWSTR p;
  1843. NTSTATUS result;
  1844. //
  1845. // Enumerate the length-sorted hash table
  1846. //
  1847. ntPath = SpMemAlloc (ACTUAL_MAX_PATH * sizeof (WCHAR));
  1848. if (!ntPath) {
  1849. return;
  1850. }
  1851. if (SppEnumFirstHashItem (&e)) {
  1852. do {
  1853. ASSERT (e.HashItem->String);
  1854. //
  1855. // Convert String into NT path
  1856. //
  1857. if (!SpNtNameFromDosPath (
  1858. (PWSTR) e.HashItem->String,
  1859. ntPath,
  1860. ACTUAL_MAX_PATH * sizeof (WCHAR),
  1861. PartitionOrdinalCurrent
  1862. )) {
  1863. KdPrintEx((
  1864. DPFLTR_SETUP_ID,
  1865. DPFLTR_ERROR_LEVEL,
  1866. "SETUP: Cannot convert path %ws to an NT path\n",
  1867. e.HashItem->String
  1868. ));
  1869. continue;
  1870. }
  1871. //
  1872. // Does it exist? If not, skip it.
  1873. //
  1874. if (!SpFileExists (ntPath, TRUE)) {
  1875. continue;
  1876. }
  1877. //
  1878. // Find the root emtpy dir. Then blow it away, including any empty
  1879. // subdirs it might have.
  1880. //
  1881. if (SppIsDirEmpty (ntPath)) {
  1882. if (!SppDelEmptyDir (ntPath)) {
  1883. KdPrintEx((
  1884. DPFLTR_SETUP_ID,
  1885. DPFLTR_ERROR_LEVEL,
  1886. "SETUP: Unable to delete empty dir %ws\n",
  1887. ntPath
  1888. ));
  1889. }
  1890. else {
  1891. //
  1892. // find the first non-empty path
  1893. //
  1894. p = wcsrchr (ntPath, L'\\');
  1895. while (p) {
  1896. *p = 0;
  1897. if (!SppIsDirEmpty (ntPath)) {
  1898. *p = L'\\';
  1899. break;
  1900. }
  1901. else{
  1902. //
  1903. // remove this empty tree
  1904. //
  1905. if (!SppDelEmptyDir (ntPath)) {
  1906. KdPrintEx((
  1907. DPFLTR_SETUP_ID,
  1908. DPFLTR_ERROR_LEVEL,
  1909. "SETUP: Unable to delete empty parent dir %ws\n",
  1910. ntPath
  1911. ));
  1912. break;
  1913. }
  1914. }
  1915. p = wcsrchr (ntPath, L'\\');
  1916. }
  1917. }
  1918. }
  1919. } while (SppEnumNextHashItem (&e));
  1920. }
  1921. SpMemFree (ntPath);
  1922. }
  1923. PDISK_REGION
  1924. SppRegionFromFullNtName (
  1925. IN PWSTR NtName,
  1926. IN PartitionOrdinalType OrdinalType,
  1927. OUT PWSTR *Path OPTIONAL
  1928. )
  1929. {
  1930. WCHAR ntRoot[ACTUAL_MAX_PATH];
  1931. PWSTR p;
  1932. PWSTR end;
  1933. //
  1934. // NtName is in the format of
  1935. //
  1936. // \Device\harddisk<n>\partition<m>\subdir
  1937. //
  1938. // and we need to separate the two.
  1939. //
  1940. wcscpy (ntRoot, NtName);
  1941. // p points to \Device\harddisk<n>\partition<m>\subdir
  1942. p = wcschr (ntRoot + 1, L'\\');
  1943. if (p) {
  1944. // p points to \harddisk<n>\partition<m>\subdir
  1945. p = wcschr (p + 1, L'\\');
  1946. if (p) {
  1947. // p points to \partition<m>\subdir
  1948. end = p;
  1949. p = wcschr (p + 1, L'\\');
  1950. if (!p) {
  1951. p = wcschr (end, 0);
  1952. }
  1953. }
  1954. }
  1955. if (p) {
  1956. // p points to \subdir or '\0'
  1957. *p = 0;
  1958. if (Path) {
  1959. *Path = NtName + (p - ntRoot);
  1960. }
  1961. return SpRegionFromNtName (ntRoot, OrdinalType);
  1962. }
  1963. return NULL;
  1964. }
  1965. BOOLEAN
  1966. SppCreateTextModeBootEntry (
  1967. IN PWSTR LoadIdentifierString,
  1968. IN PWSTR OsLoadOptions, OPTIONAL
  1969. IN BOOLEAN Default
  1970. )
  1971. /*++
  1972. Routine Description:
  1973. SppCreateTextModeBootEntry makes another boot.ini entry for textmode.
  1974. This is used to create the boot.ini entry that triggers rollback in
  1975. an incomplete setup scenario.
  1976. Arguments:
  1977. LoadIdentifierString - Specifies the localized text to put in the boot menu.
  1978. OsLoadOptions - Specifies options to associate with the boot option,
  1979. such as /rollback.
  1980. Default - Specifies TRUE if the entry should be the default
  1981. boot option
  1982. Return Value:
  1983. TRUE if boot.ini was updated, FALSE otherwise.
  1984. --*/
  1985. {
  1986. PWSTR bootVars[MAXBOOTVARS];
  1987. PWSTR defaultBootEntry = L"C:\\$WIN_NT$.~BT\\bootsect.dat";
  1988. PWSTR defaultArc = L"C:\\$WIN_NT$.~BT\\";
  1989. PWSTR defaultFile = L"bootsect.dat";
  1990. PDISK_REGION CColonRegion;
  1991. UINT defaultSignature;
  1992. CColonRegion = SpPtValidSystemPartition();
  1993. if (!CColonRegion) {
  1994. KdPrintEx((
  1995. DPFLTR_SETUP_ID,
  1996. DPFLTR_ERROR_LEVEL,
  1997. "SETUP: Unable to find region of drive C."
  1998. ));
  1999. }
  2000. //
  2001. // Create a boot set
  2002. //
  2003. bootVars[OSLOADOPTIONS] = SpDupStringW (OsLoadOptions ? OsLoadOptions : L"");
  2004. bootVars[LOADIDENTIFIER] = SpMemAlloc((wcslen(LoadIdentifierString)+3)*sizeof(WCHAR));
  2005. bootVars[LOADIDENTIFIER][0] = L'\"';
  2006. wcscpy (bootVars[LOADIDENTIFIER] + 1, LoadIdentifierString);
  2007. wcscat (bootVars[LOADIDENTIFIER], L"\"");
  2008. bootVars[OSLOADER] = SpDupStringW (defaultBootEntry);
  2009. bootVars[OSLOADPARTITION] = SpDupStringW (defaultArc);
  2010. bootVars[SYSTEMPARTITION] = SpDupStringW (defaultArc);
  2011. bootVars[OSLOADFILENAME] = SpDupStringW (defaultFile);
  2012. if (CColonRegion->DiskNumber != 0xffffffff) {
  2013. defaultSignature = HardDisks[CColonRegion->DiskNumber].Signature;
  2014. } else {
  2015. defaultSignature = 0;
  2016. }
  2017. SpAddBootSet (bootVars, Default, defaultSignature);
  2018. return TRUE;
  2019. }
  2020. BOOL
  2021. SppRestoreBackedUpFileNotification (
  2022. PCWSTR FileName
  2023. )
  2024. {
  2025. //KdPrint((__FUNCTION__" %ls\n", FileName));
  2026. return TRUE;
  2027. }
  2028. VOID
  2029. SppRestoreBackedUpFiles (
  2030. IN PVOID WinntSif
  2031. )
  2032. {
  2033. BOOL Success = FALSE;
  2034. BACKUP_IMAGE_HANDLE backupImage = NULL;
  2035. BACKUP_IMAGE_HEADER header = { 0 };
  2036. LARGE_INTEGER imagePos = { 0 };
  2037. backupImage = SppOpenBackupImage (FALSE, &header, &imagePos, NULL, tcompTYPE_MSZIP, NULL);
  2038. if (backupImage == INVALID_HANDLE_VALUE) {
  2039. return;
  2040. }
  2041. Success = SpCabExtractAllFilesExW(backupImage->CabHandle, L"", SppRestoreBackedUpFileNotification);
  2042. SppCloseBackupImage (backupImage, NULL, NULL);
  2043. }
  2044. DWORD Spwtoi (
  2045. IN LPCWSTR String)
  2046. {
  2047. DWORD rVal = 0;
  2048. //
  2049. // While on a number, build up rVal.
  2050. //
  2051. while (String && *String && *String >= L'0' && *String <= L'9') {
  2052. rVal = rVal * 10 + (*String - L'0');
  2053. String++;
  2054. }
  2055. return rVal;
  2056. }
  2057. BOOL
  2058. pParseLineForDirNameAndAttributes(
  2059. IN PCWSTR LineForParse,
  2060. OUT PWSTR DirName,
  2061. OUT DWORD * DirAttributes
  2062. )
  2063. {
  2064. int i;
  2065. int iLen;
  2066. if(!LineForParse || !DirName){
  2067. ASSERT(FALSE);
  2068. return FALSE;
  2069. }
  2070. for(i = 0, iLen = wcslen(LineForParse); i < iLen; i++)
  2071. {
  2072. if(LineForParse[i] == ','){
  2073. break;
  2074. }
  2075. }
  2076. if(i == iLen){
  2077. wcscpy(DirName, LineForParse);
  2078. }
  2079. else{
  2080. wcsncpy(DirName, LineForParse, i);DirName[i] = '\0';
  2081. if(DirAttributes){
  2082. *DirAttributes = Spwtoi((PCWSTR)&LineForParse[i + 1]);
  2083. }
  2084. }
  2085. return TRUE;
  2086. }
  2087. VOID
  2088. SppMkEmptyDirs (
  2089. IN PVOID WinntSif,
  2090. IN PCWSTR DosDirListPath
  2091. )
  2092. {
  2093. WIN9XDATFILEENUM e;
  2094. PDISK_REGION region = NULL;
  2095. NTSTATUS Status = STATUS_SUCCESS;
  2096. WCHAR ntName[ACTUAL_MAX_PATH];
  2097. WCHAR ntRoot[ACTUAL_MAX_PATH];
  2098. PWSTR subDir = NULL;
  2099. UINT dirAttributes;
  2100. WCHAR dirName[ACTUAL_MAX_PATH];
  2101. //
  2102. // Blow away files or empty directories
  2103. //
  2104. if (SpEnumFirstWin9xFile (&e, WinntSif, DosDirListPath)) {
  2105. do {
  2106. //
  2107. // Convert e.CurLine from a DOS path to an NT path
  2108. //
  2109. dirAttributes = 0;
  2110. if(!pParseLineForDirNameAndAttributes(e.CurLine, dirName, &dirAttributes)){
  2111. ASSERT(FALSE);
  2112. continue;
  2113. }
  2114. if (!SpNtNameFromDosPath (
  2115. dirName,
  2116. ntName,
  2117. sizeof (ntName),
  2118. PartitionOrdinalCurrent
  2119. )) {
  2120. KdPrintEx((
  2121. DPFLTR_SETUP_ID,
  2122. DPFLTR_ERROR_LEVEL,
  2123. "SETUP: " __FUNCTION__ ": Cannot convert path %ws to an NT path\n",
  2124. dirName
  2125. ));
  2126. } else {
  2127. region = SppRegionFromFullNtName (ntName, PartitionOrdinalCurrent, &subDir);
  2128. if (!region) {
  2129. KdPrintEx ((
  2130. DPFLTR_SETUP_ID,
  2131. DPFLTR_ERROR_LEVEL,
  2132. "SETUP: "__FUNCTION__" - Can't get region for empty dirs\n"
  2133. ));
  2134. } else{
  2135. SpNtNameFromRegion (region, ntRoot, sizeof(ntRoot), PartitionOrdinalCurrent);
  2136. SpCreateDirectory (ntRoot, NULL, subDir, dirAttributes, 0);
  2137. SpSetFileAttributesW(ntName, dirAttributes);
  2138. }
  2139. }
  2140. } while (SpEnumNextWin9xFile(&e));
  2141. } else {
  2142. KdPrintEx((
  2143. DPFLTR_SETUP_ID,
  2144. DPFLTR_ERROR_LEVEL,
  2145. "SETUP: " __FUNCTION__ ": No files to enumerate.\n"
  2146. ));
  2147. }
  2148. }
  2149. VOID
  2150. SpRemoveExtraBootIniEntry (
  2151. VOID
  2152. )
  2153. {
  2154. PWSTR bootVars[MAXBOOTVARS];
  2155. PWSTR defaultBootEntry = L"C:\\$WIN_NT$.~BT\\bootsect.dat";
  2156. PWSTR defaultArc = L"C:\\$WIN_NT$.~BT\\";
  2157. PWSTR defaultFile = L"bootsect.dat";
  2158. PDISK_REGION CColonRegion;
  2159. //
  2160. // Remove the boot set for text mode
  2161. //
  2162. RtlZeroMemory (bootVars, sizeof(bootVars));
  2163. bootVars[OSLOADOPTIONS] = L"";
  2164. bootVars[OSLOADER] = defaultBootEntry;
  2165. bootVars[OSLOADPARTITION] = defaultArc;
  2166. bootVars[SYSTEMPARTITION] = defaultArc;
  2167. bootVars[OSLOADFILENAME] = defaultFile;
  2168. SpDeleteBootSet (bootVars, NULL);
  2169. }
  2170. VOID
  2171. SppMakeLegacyBootIni (
  2172. IN PDISK_REGION TargetRegion
  2173. )
  2174. {
  2175. PWSTR data;
  2176. PWSTR bootVars[MAXBOOTVARS];
  2177. WCHAR sysPart[MAX_PATH];
  2178. UINT signature;
  2179. //
  2180. // Reset the entire boot.ini file
  2181. //
  2182. RtlZeroMemory (bootVars, sizeof(bootVars));
  2183. SpDeleteBootSet (bootVars, NULL);
  2184. //
  2185. // Build new boot.ini entry
  2186. //
  2187. // LOADIDENTIFIER - friendly name
  2188. data = SpGetSectionKeyIndex (SifHandle, SIF_SETUPDATA, L"LoadIdentifierWin9x", 0);
  2189. if (!data) {
  2190. SpFatalSifError (SifHandle, SIF_SETUPDATA, L"LoadIdentifierWin9x",0,0);
  2191. }
  2192. bootVars[LOADIDENTIFIER] = SpMemAlloc((wcslen(data)+3)*sizeof(WCHAR));
  2193. bootVars[LOADIDENTIFIER][0] = L'\"';
  2194. wcscpy (bootVars[LOADIDENTIFIER] + 1, data);
  2195. wcscat (bootVars[LOADIDENTIFIER], L"\"");
  2196. // OSLOADER - c:\ntldr (in ARC format)
  2197. SpArcNameFromRegion (
  2198. TargetRegion,
  2199. sysPart,
  2200. sizeof(sysPart),
  2201. PartitionOrdinalCurrent,
  2202. PrimaryArcPath
  2203. );
  2204. data = TemporaryBuffer;
  2205. wcscpy (data, sysPart);
  2206. SpConcatenatePaths (data, L"ntldr");
  2207. bootVars[OSLOADER] = SpDupStringW (data);
  2208. // OSLOADPARTITION - "c:\"
  2209. data[0] = TargetRegion->DriveLetter;
  2210. data[1] = L':';
  2211. data[2] = L'\\';
  2212. data[3] = 0;
  2213. if (data[0] != L'C' && data[0] != L'D' && data[0] != L'c' && data[0] != L'd') {
  2214. data[0] = L'C';
  2215. }
  2216. bootVars[OSLOADPARTITION] = SpDupStringW (data);
  2217. // SYSTEMPARTITION - same as OSLOADPARTITION
  2218. bootVars[SYSTEMPARTITION] = SpDupStringW (data);
  2219. // OSLOADFILENAME - empty
  2220. bootVars[OSLOADFILENAME] = SpDupStringW (L"");
  2221. // OSLOADOPTIONS - empty
  2222. bootVars[OSLOADOPTIONS] = SpDupStringW (L"");
  2223. // signature
  2224. if (TargetRegion->DiskNumber != 0xffffffff) {
  2225. signature = HardDisks[TargetRegion->DiskNumber].Signature;
  2226. } else {
  2227. signature = 0;
  2228. }
  2229. // add to boot.ini (takes ownership of allocations above)
  2230. SpAddBootSet (bootVars, TRUE, signature);
  2231. // flush boot.ini
  2232. SpCompleteBootListConfig (TargetRegion->DriveLetter);
  2233. }
  2234. BOOLEAN
  2235. SpExecuteWin9xRollback (
  2236. IN PVOID WinntSifHandle,
  2237. IN PWSTR BootDeviceNtPath
  2238. )
  2239. {
  2240. PWSTR data;
  2241. PDISK_REGION bootRegion;
  2242. ULONG i = 0;
  2243. PCWSTR Directory = NULL;
  2244. PWSTR NtNameFromDosPath = NULL;
  2245. //
  2246. // Perform rollback
  2247. //
  2248. // step 1: delete NT files
  2249. data = SpGetSectionKeyIndex (
  2250. WinntSifHandle,
  2251. SIF_DATA,
  2252. WINNT_D_ROLLBACK_DELETE_W,
  2253. 0
  2254. );
  2255. if (data) {
  2256. SppDeleteWin9xFilesWorker (WinntSifHandle, data, NULL, TRUE);
  2257. SppCleanEmptyDirs();
  2258. }
  2259. TESTHOOK(1003); // use 2003 in the answer file to hit this
  2260. // step 2: move Win9x files back to original locations
  2261. data = SpGetSectionKeyIndex (
  2262. WinntSifHandle,
  2263. SIF_DATA,
  2264. WINNT_D_ROLLBACK_MOVE_W,
  2265. 0
  2266. );
  2267. if (data) {
  2268. SppMoveWin9xFilesWorker (WinntSifHandle, data, TRUE);
  2269. }
  2270. TESTHOOK(1004); // use 2004 in the answer file to hit this
  2271. // step 3: blow away NT-specific subdirectories
  2272. data = SpGetSectionKeyIndex (
  2273. WinntSifHandle,
  2274. SIF_DATA,
  2275. WINNT_D_ROLLBACK_DELETE_DIR_W,
  2276. 0
  2277. );
  2278. if (data) {
  2279. SppDeleteWin9xFilesWorker (WinntSifHandle, NULL, data, TRUE);
  2280. }
  2281. TESTHOOK(1005); // use 2005 in the answer file to hit this
  2282. // step 4: restore files that were backed up
  2283. SppRestoreBackedUpFiles (WinntSifHandle);
  2284. TESTHOOK(1006); // use 2006 in the answer file to hit this
  2285. // step 5: wipe out dirs made empty
  2286. SppCleanEmptyDirs();
  2287. TESTHOOK(1007); // use 2007 in the answer file to hit this
  2288. // step 6: generate original empty dirs
  2289. data = SpGetSectionKeyIndex (
  2290. WinntSifHandle,
  2291. SIF_DATA,
  2292. L"RollbackMkDirs",
  2293. 0
  2294. );
  2295. if (data) {
  2296. SppMkEmptyDirs (WinntSifHandle, data);
  2297. }
  2298. TESTHOOK(1008); // use 2008 in the answer file to hit this
  2299. //
  2300. // step 7: clean up boot loader
  2301. //
  2302. bootRegion = SpRegionFromNtName (BootDeviceNtPath, PartitionOrdinalCurrent);
  2303. if (bootRegion) {
  2304. SppMakeLegacyBootIni (bootRegion);
  2305. } else {
  2306. SpBugCheck(SETUP_BUGCHECK_BOOTPATH,0,0,0);
  2307. }
  2308. SppEmptyHashTable();
  2309. return TRUE;
  2310. }
  2311. VOID
  2312. SppMoveWin9xFilesWorker (
  2313. IN PVOID WinntSif,
  2314. IN PCWSTR MoveSection,
  2315. IN BOOLEAN Rollback
  2316. )
  2317. /*++
  2318. Routine Description:
  2319. SpMoveWin9xFiles takes full DOS paths in the WIN9XMOV.TXT file
  2320. and puts them in a temporary location also specified in this file.
  2321. the format of this file is
  2322. oldpath
  2323. temppath
  2324. ...
  2325. oldpath can be either a directory or file, temppath can only be a directory (which may
  2326. not exist yet).
  2327. Arguments:
  2328. WinntSif: Handle to Winnt.Sif
  2329. Return Value:
  2330. None. Errors ignored.
  2331. --*/
  2332. {
  2333. WCHAR SourceFileOrDir[ACTUAL_MAX_PATH];
  2334. PWSTR DestFileOrDir;
  2335. WIN9XDATFILEENUM e;
  2336. if (SpEnumFirstWin9xFile(&e,WinntSif, MoveSection)) {
  2337. do {
  2338. wcscpy (SourceFileOrDir, e.CurLine);
  2339. if (!SpEnumNextWin9xFile(&e)) {
  2340. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error moving win9x files. Improper Win9x dat file."));
  2341. return;
  2342. }
  2343. DestFileOrDir = e.CurLine;
  2344. if (Rollback) {
  2345. SppPutParentsInHashTable (SourceFileOrDir);
  2346. }
  2347. // There's little chance for failure, because in Win95 we've already
  2348. // verified the source exists and the destination does not exist.
  2349. // The only way this can fail is if the hard disk craps out.
  2350. SpMigMoveFileOrDir (SourceFileOrDir, DestFileOrDir);
  2351. } while (SpEnumNextWin9xFile(&e));
  2352. }
  2353. else {
  2354. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error in SpWin9xMovFiles No files to enum in.\n"));
  2355. }
  2356. }
  2357. VOID
  2358. SpMoveWin9xFiles (
  2359. IN PVOID WinntSif
  2360. )
  2361. {
  2362. SppMoveWin9xFilesWorker (WinntSif, WINNT32_D_WIN9XMOV_FILE_W, FALSE);
  2363. }
  2364. VOID
  2365. SppDeleteWin9xFilesWorker (
  2366. IN PVOID WinntSif,
  2367. IN PCWSTR FileSection, OPTIONAL
  2368. IN PCWSTR DirSection, OPTIONAL
  2369. IN BOOLEAN Rollback
  2370. )
  2371. /*++
  2372. Routine Description:
  2373. SpDeleteWin9xFiles deletes files/empty directories specified by full DOS
  2374. paths in WIN9XDEL.TXT (install) or DELFILES.TXT (uninstall).
  2375. Each line in this file contains one path and is delimeted by a \r\n.
  2376. Arguments:
  2377. WinntSif: Handle to Winnt.Sif
  2378. Return Value:
  2379. None. Errors ignored.
  2380. --*/
  2381. {
  2382. WIN9XDATFILEENUM e;
  2383. PDISK_REGION region;
  2384. NTSTATUS Status = STATUS_SUCCESS;
  2385. //
  2386. // Blow away files or empty directories
  2387. //
  2388. if (FileSection && SpEnumFirstWin9xFile(&e,WinntSif,FileSection)) {
  2389. do {
  2390. if (Rollback) {
  2391. SppPutParentsInHashTable (e.CurLine);
  2392. }
  2393. SpMigDeleteFile (e.CurLine);
  2394. } while (SpEnumNextWin9xFile(&e));
  2395. } else {
  2396. KdPrintEx((
  2397. DPFLTR_SETUP_ID,
  2398. DPFLTR_ERROR_LEVEL,
  2399. "SETUP: " __FUNCTION__ ": No files to enumerate.\n"
  2400. ));
  2401. }
  2402. //
  2403. // Remove entire subdirectory trees.
  2404. //
  2405. if (DirSection && SpEnumFirstWin9xFile (&e, WinntSif, DirSection)) {
  2406. do {
  2407. region = SpRegionFromDosName (e.CurLine);
  2408. if (region) {
  2409. SpDeleteExistingTargetDir (region, e.CurLine + 2, TRUE, SP_SCRN_CLEARING_OLD_WINNT);
  2410. if (Rollback) {
  2411. SppPutParentsInHashTable (e.CurLine);
  2412. }
  2413. }
  2414. } while (SpEnumNextWin9xFile (&e));
  2415. } else {
  2416. KdPrintEx((
  2417. DPFLTR_SETUP_ID,
  2418. DPFLTR_ERROR_LEVEL,
  2419. "SETUP: No Directories to delete for win9xupg.\n"
  2420. ));
  2421. }
  2422. }
  2423. VOID
  2424. SpDeleteWin9xFiles (
  2425. IN PVOID WinntSif
  2426. )
  2427. {
  2428. SppDeleteWin9xFilesWorker (WinntSif, WINNT32_D_WIN9XDEL_FILE_W, WINNT32_D_W9XDDIR_FILE_W, FALSE);
  2429. }
  2430. //
  2431. // Win9x Drive Letter mapping specific structs, typedefs, and defines
  2432. //
  2433. typedef struct _WIN9XDRIVELETTERINFO WIN9XDRIVELETTERINFO,*PWIN9XDRIVELETTERINFO;
  2434. struct _WIN9XDRIVELETTERINFO {
  2435. BOOL StatusFlag; // Internal routine use.
  2436. DWORD Drive; // 0 - 25, 0 = 'A', etc.
  2437. DWORD Type; // Media type. Gathered by GetDriveType on Win9x.
  2438. LPCWSTR Identifier; // Media type dependent string identifier.
  2439. PWIN9XDRIVELETTERINFO Next; // Next drive letter.
  2440. };
  2441. #define NUMDRIVELETTERS 26
  2442. #define DEBUGSTATUS(string,status) \
  2443. if (!NT_SUCCESS(status)) KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP (DEBUGSTATUS) %ws %u (%x)\n",(string),(status),(status)))
  2444. //
  2445. // FALSE indicates a drive letter is available,
  2446. // TRUE indicates that a drive letter is already
  2447. // assigned to a system resource.
  2448. //
  2449. BOOL g_DriveLetters[NUMDRIVELETTERS];
  2450. PDISK_REGION
  2451. SpFirstPartitionedRegion (
  2452. IN PDISK_REGION Region,
  2453. IN BOOLEAN Primary
  2454. )
  2455. {
  2456. while (Region) {
  2457. if (Primary) {
  2458. if (SPPT_IS_REGION_PRIMARY_PARTITION(Region)) {
  2459. break;
  2460. }
  2461. } else {
  2462. if (SPPT_IS_REGION_LOGICAL_DRIVE(Region)) {
  2463. break;
  2464. }
  2465. }
  2466. Region = Region -> Next;
  2467. }
  2468. return Region;
  2469. }
  2470. PDISK_REGION
  2471. SpNextPartitionedRegion (
  2472. IN PDISK_REGION Region,
  2473. IN BOOLEAN Primary
  2474. )
  2475. {
  2476. if (Region) {
  2477. return SpFirstPartitionedRegion (Region->Next, Primary);
  2478. }
  2479. return NULL;
  2480. }
  2481. #if 0
  2482. VOID
  2483. SpOutputDriveLettersToRegionsMap(
  2484. VOID
  2485. )
  2486. {
  2487. //
  2488. // This is a debug function. Will be removed.
  2489. //
  2490. DWORD disk;
  2491. PDISK_REGION pRegion;
  2492. WCHAR tempBuffer[MAX_PATH];
  2493. for(disk=0; disk<HardDiskCount; disk++) {
  2494. pRegion = SpFirstPartitionedRegion(PartitionedDisks[disk].PrimaryDiskRegions, TRUE);
  2495. while(pRegion) {
  2496. SpNtNameFromRegion(pRegion,tempBuffer,MAX_PATH,PartitionOrdinalCurrent);
  2497. if (pRegion -> DriveLetter == 0) {
  2498. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: No drive letter for %ws.\n",tempBuffer));
  2499. }
  2500. else {
  2501. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws maps to drive letter %wc\n",tempBuffer,pRegion -> DriveLetter));
  2502. }
  2503. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws Info: Disk Num: %u Start: %u\n",tempBuffer,pRegion -> DiskNumber,pRegion -> StartSector));
  2504. pRegion = SpNextPartitionedRegion(pRegion, TRUE);
  2505. }
  2506. pRegion = SpFirstPartitionedRegion(PartitionedDisks[disk].PrimaryDiskRegions, FALSE);
  2507. while(pRegion) {
  2508. SpNtNameFromRegion(pRegion,tempBuffer,MAX_PATH,PartitionOrdinalCurrent);
  2509. if (pRegion -> DriveLetter == 0) {
  2510. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: No drive letter for %ws.\n",tempBuffer));
  2511. }
  2512. else {
  2513. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws maps to drive letter %wc\n",tempBuffer,pRegion -> DriveLetter));
  2514. }
  2515. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws Info: Disk Num: %u Start: %u\n",tempBuffer,pRegion -> DiskNumber,pRegion -> StartSector));
  2516. pRegion = SpNextPartitionedRegion(pRegion, FALSE);
  2517. }
  2518. }
  2519. }
  2520. #endif
  2521. WCHAR
  2522. SpGetNextDriveLetter (
  2523. IN WCHAR LastLetter
  2524. )
  2525. {
  2526. WCHAR rChar = 0;
  2527. DWORD index = LastLetter - L'A';
  2528. //
  2529. // Find the next unused drive letter.
  2530. //
  2531. while (index < NUMDRIVELETTERS && g_DriveLetters[index]) {
  2532. index++;
  2533. }
  2534. if (index < NUMDRIVELETTERS) {
  2535. //
  2536. // A valid letter was found.
  2537. // Set it as the return drive letter and mark its place in the table as used
  2538. //
  2539. rChar = (WCHAR) index + L'A';
  2540. g_DriveLetters[index] = TRUE;
  2541. }
  2542. return rChar;
  2543. }
  2544. VOID
  2545. SpAssignDriveLettersToRemainingPartitions (
  2546. VOID
  2547. )
  2548. /*++
  2549. Routine Description:
  2550. Assigns drive letters to partitions which have not yet received
  2551. the drive letter
  2552. NOTE : This is a modified version of SpGuessDriveLetters().
  2553. Arguments:
  2554. None
  2555. Return Value:
  2556. None
  2557. --*/
  2558. {
  2559. ULONG Disk;
  2560. BOOLEAN DriveLettersPresent = TRUE;
  2561. PDISK_REGION *PrimaryPartitions;
  2562. WCHAR DriveLetter;
  2563. PDISK_REGION pRegion;
  2564. ULONG Index;
  2565. PPARTITIONED_DISK PartDisk;
  2566. //
  2567. // Allocate adequate memory for region pointers to primary partitions
  2568. // on all disks
  2569. //
  2570. PrimaryPartitions = SpMemAlloc(PTABLE_DIMENSION * HardDiskCount * sizeof(PDISK_REGION));
  2571. if(!PrimaryPartitions) {
  2572. KdPrintEx((DPFLTR_SETUP_ID,
  2573. DPFLTR_ERROR_LEVEL,
  2574. "SETUP: Can't allocate memory for drive letter assignment\n"));
  2575. return;
  2576. }
  2577. RtlZeroMemory(PrimaryPartitions,PTABLE_DIMENSION * HardDiskCount * sizeof(PDISK_REGION));
  2578. //
  2579. // Go through each disk and fill up the primary partition
  2580. // region(s) in the array
  2581. //
  2582. for(Disk=0; Disk < HardDiskCount; Disk++) {
  2583. ULONG ActiveIndex = (ULONG)-1;
  2584. PartDisk = PartitionedDisks + Disk;
  2585. //
  2586. // Skip removable media. If a Disk is off-line it's hard to imagine
  2587. // that we'll actually have any partitioned spaces on it so
  2588. // we don't do any special checks here for that condition.
  2589. //
  2590. if(!(PartDisk->HardDisk->Characteristics & FILE_REMOVABLE_MEDIA)) {
  2591. for(pRegion=SPPT_GET_PRIMARY_DISK_REGION(Disk); pRegion; pRegion=pRegion->Next) {
  2592. //
  2593. // We only care about partitioned spaces that have yet to receive
  2594. // a drive letter.
  2595. //
  2596. if (SPPT_IS_REGION_PRIMARY_PARTITION(pRegion) && !pRegion -> DriveLetter) {
  2597. //
  2598. // This guy gets a drive letter.
  2599. //
  2600. ASSERT(pRegion->TablePosition <= PTABLE_DIMENSION);
  2601. PrimaryPartitions[(Disk*PTABLE_DIMENSION) + pRegion->TablePosition - 1] = pRegion;
  2602. //
  2603. // Do not save active flag on NEC98
  2604. //
  2605. if (!IsNEC_98) { //NEC98
  2606. if (SPPT_IS_REGION_ACTIVE_PARTITION(pRegion) && (ActiveIndex != (ULONG)(-1))) {
  2607. ActiveIndex = pRegion->TablePosition - 1;
  2608. }
  2609. } //NEC98
  2610. }
  2611. }
  2612. //
  2613. // Do not check active flag on NEC98
  2614. //
  2615. if (!IsNEC_98) { //NEC98
  2616. //
  2617. // If we found an active partition, move it to the start of
  2618. // the list for this drive unless it's already at the start.
  2619. //
  2620. if((ActiveIndex != (ULONG)(-1)) && ActiveIndex) {
  2621. PDISK_REGION ActiveRegion;
  2622. ASSERT(ActiveIndex < PTABLE_DIMENSION);
  2623. ActiveRegion = PrimaryPartitions[(Disk*PTABLE_DIMENSION) + ActiveIndex];
  2624. RtlMoveMemory(
  2625. &PrimaryPartitions[(Disk*PTABLE_DIMENSION)+1],
  2626. &PrimaryPartitions[(Disk*PTABLE_DIMENSION)],
  2627. (ActiveIndex) * sizeof(PDISK_REGION)
  2628. );
  2629. PrimaryPartitions[Disk*PTABLE_DIMENSION] = ActiveRegion;
  2630. }
  2631. } //NEC98
  2632. }
  2633. }
  2634. if (IsNEC_98 && DriveAssignFromA) { //NEC98
  2635. DriveLetter = L'A'; // First valid hard dive letter for legacy NEC assign.
  2636. } else {
  2637. DriveLetter = L'C'; // First valid hard dive letter.
  2638. } //NEC98
  2639. //
  2640. // Assign drive letters to the first primary partitions
  2641. // for each non-removable on-line Disk.
  2642. //
  2643. for(Disk=0; Disk<HardDiskCount; Disk++) {
  2644. for(Index=0; Index<PTABLE_DIMENSION; Index++) {
  2645. PDISK_REGION Region = PrimaryPartitions[(Disk*PTABLE_DIMENSION) + Index];
  2646. if(Region) {
  2647. DriveLetter = SpGetNextDriveLetter(DriveLetter);
  2648. if (DriveLetter && !Region->DriveLetter) {
  2649. Region->DriveLetter = DriveLetter;
  2650. //
  2651. // Done with the region
  2652. //
  2653. PrimaryPartitions[(Disk*PTABLE_DIMENSION) + Index] = NULL;
  2654. break;
  2655. } else {
  2656. DriveLettersPresent = FALSE;
  2657. break;
  2658. }
  2659. }
  2660. }
  2661. }
  2662. //
  2663. // For each disk, assign drive letters to all the logical drives.
  2664. // For removable drives, we assume a single partition, and that
  2665. // partition gets a drive letter as if it were a logical drive.
  2666. //
  2667. for(Disk=0; DriveLettersPresent && (Disk < HardDiskCount); Disk++) {
  2668. PartDisk = &PartitionedDisks[Disk];
  2669. if(PartDisk->HardDisk->Characteristics & FILE_REMOVABLE_MEDIA) {
  2670. //
  2671. // Give the first primary partition the drive letter
  2672. // and ignore other partitions. Even if there are no
  2673. // partitions, reserve a drive letter.
  2674. //
  2675. for(pRegion=SPPT_GET_PRIMARY_DISK_REGION(Disk); pRegion; pRegion=pRegion->Next) {
  2676. if(SPPT_IS_REGION_PRIMARY_PARTITION(pRegion) && !pRegion->DriveLetter) {
  2677. DriveLetter = SpGetNextDriveLetter(DriveLetter);
  2678. if (DriveLetter) {
  2679. pRegion->DriveLetter = DriveLetter;
  2680. break;
  2681. }
  2682. else {
  2683. DriveLettersPresent = FALSE;
  2684. break;
  2685. }
  2686. }
  2687. }
  2688. } else {
  2689. for(pRegion=SPPT_GET_PRIMARY_DISK_REGION(Disk); pRegion; pRegion=pRegion->Next) {
  2690. if(SPPT_IS_REGION_LOGICAL_DRIVE(pRegion) && pRegion->DriveLetter == 0) {
  2691. //
  2692. // This guy gets a drive letter.
  2693. //
  2694. DriveLetter = SpGetNextDriveLetter(DriveLetter);
  2695. if (DriveLetter) {
  2696. pRegion->DriveLetter = DriveLetter;
  2697. } else {
  2698. DriveLettersPresent = FALSE;
  2699. break;
  2700. }
  2701. }
  2702. }
  2703. }
  2704. }
  2705. //
  2706. // For each non-removable on-line disk, assign drive letters
  2707. // to all remaining primary partitions.
  2708. //
  2709. for (Disk=0; DriveLettersPresent && (Disk < HardDiskCount); Disk++) {
  2710. for(Index=0; Index<PTABLE_DIMENSION; Index++) {
  2711. PDISK_REGION Region = PrimaryPartitions[(Disk*PTABLE_DIMENSION)+Index];
  2712. if (Region && !Region->DriveLetter) {
  2713. DriveLetter = SpGetNextDriveLetter(DriveLetter);
  2714. if (DriveLetter) {
  2715. Region->DriveLetter = DriveLetter;
  2716. } else {
  2717. DriveLettersPresent = FALSE;
  2718. break;
  2719. }
  2720. }
  2721. }
  2722. }
  2723. SpMemFree(PrimaryPartitions);
  2724. #if 0
  2725. SpOutputDriveLettersToRegionsMap();
  2726. #endif
  2727. }
  2728. BOOL
  2729. SpCheckRegionForMatchWithWin9xData(
  2730. IN PDISK_REGION Region,
  2731. IN DWORD DriveToMatch
  2732. )
  2733. {
  2734. NTSTATUS ntStatus;
  2735. HANDLE fileHandle;
  2736. OBJECT_ATTRIBUTES attributes;
  2737. IO_STATUS_BLOCK ioStatus;
  2738. UNICODE_STRING filePath;
  2739. WCHAR tempBuffer[MAX_PATH];
  2740. DWORD sigFileDrive;
  2741. ASSERT(DriveToMatch < NUMDRIVELETTERS);
  2742. //
  2743. // Initialize sigFileDrive to an invalid drive.
  2744. //
  2745. sigFileDrive = NUMDRIVELETTERS;
  2746. //
  2747. // Create Unicode string for the path to the region.
  2748. SpNtNameFromRegion(Region,tempBuffer,sizeof(tempBuffer),PartitionOrdinalCurrent);
  2749. //
  2750. // Get the file creation times of the $DRVLTR$.~_~ file.
  2751. //
  2752. wcscat(tempBuffer,L"\\");
  2753. wcscat(tempBuffer,WINNT_WIN95UPG_DRVLTR_W);
  2754. RtlInitUnicodeString(&filePath,tempBuffer);
  2755. InitializeObjectAttributes(
  2756. &attributes,
  2757. &filePath,
  2758. OBJ_CASE_INSENSITIVE,
  2759. NULL,
  2760. NULL
  2761. );
  2762. //
  2763. // Attempt to open the signature file.
  2764. //
  2765. ntStatus = ZwCreateFile (
  2766. &fileHandle,
  2767. GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  2768. &attributes,
  2769. &ioStatus,
  2770. 0,
  2771. 0,
  2772. 0,
  2773. FILE_OPEN,
  2774. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  2775. NULL,
  2776. 0
  2777. );
  2778. if (NT_SUCCESS(ntStatus)) {
  2779. //
  2780. // Read the drive letter from this signature file, then close it.
  2781. //
  2782. ntStatus = ZwReadFile (
  2783. fileHandle,
  2784. NULL,
  2785. NULL,
  2786. NULL,
  2787. &ioStatus,
  2788. &sigFileDrive,
  2789. sizeof(DWORD),
  2790. NULL,
  2791. NULL
  2792. );
  2793. ZwClose(fileHandle);
  2794. }
  2795. //
  2796. // Print error message if we have a bad status.
  2797. //
  2798. if (!NT_SUCCESS(ntStatus)) {
  2799. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "Could not open win9x signature file %ws [Nt Status: %u (%x)]\n",
  2800. tempBuffer,ntStatus,ntStatus));
  2801. }
  2802. return sigFileDrive == DriveToMatch;
  2803. }
  2804. VOID
  2805. SpAssignOtherDriveLettersToMatchWin9x(
  2806. IN PWIN9XDRIVELETTERINFO Win9xOtherDrives
  2807. )
  2808. {
  2809. PWIN9XDRIVELETTERINFO curDrive;
  2810. if (IsNEC_98) {
  2811. WCHAR openPath[MAX_PATH+1];
  2812. HANDLE fdHandle;
  2813. DWORD numberOfFloppys;
  2814. OBJECT_ATTRIBUTES objectAttributes;
  2815. UNICODE_STRING unicodeString;
  2816. IO_STATUS_BLOCK ioStatusBlock;
  2817. NTSTATUS openStatus;
  2818. NTSTATUS status;
  2819. DWORD index, i;
  2820. PWIN9XDRIVELETTERINFO pOtherDrives[NUMDRIVELETTERS];
  2821. //
  2822. // Encount number of floppy device.
  2823. //
  2824. numberOfFloppys = 0;
  2825. do {
  2826. swprintf(openPath,L"\\device\\floppy%u",numberOfFloppys);
  2827. INIT_OBJA(&objectAttributes,&unicodeString,openPath);
  2828. openStatus = ZwCreateFile(
  2829. &fdHandle,
  2830. SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  2831. &objectAttributes,
  2832. &ioStatusBlock,
  2833. NULL, // allocation size
  2834. FILE_ATTRIBUTE_NORMAL,
  2835. FILE_SHARE_VALID_FLAGS, // full sharing
  2836. FILE_OPEN,
  2837. FILE_SYNCHRONOUS_IO_NONALERT,
  2838. NULL, // no EAs
  2839. 0
  2840. );
  2841. if(NT_SUCCESS(openStatus)) {
  2842. //
  2843. // Increment count of CdRoms and close this handle.
  2844. //
  2845. numberOfFloppys++;
  2846. ZwClose(fdHandle);
  2847. } else {
  2848. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open handle to %ws. %u (%x)\n",openPath,openStatus,openStatus));
  2849. }
  2850. } while (numberOfFloppys < NUMDRIVELETTERS && NT_SUCCESS(openStatus));
  2851. //
  2852. // At first, initialize temporary array.
  2853. //
  2854. for (i = 0;i < NUMDRIVELETTERS; i++) {
  2855. pOtherDrives[i] = NULL;
  2856. }
  2857. for (curDrive = Win9xOtherDrives;curDrive;curDrive = curDrive -> Next) {
  2858. pOtherDrives[curDrive -> Drive] = curDrive;
  2859. }
  2860. //
  2861. // Map floppy letter from begining of OtherDrives.
  2862. // On Win9x, floppy letter should be assigned before the other removables.
  2863. //
  2864. index = 0;
  2865. for (i = 0;i < NUMDRIVELETTERS; i++) {
  2866. if (index < numberOfFloppys) {
  2867. if (pOtherDrives[i]) {
  2868. //
  2869. // Need to map the floppys.
  2870. // It will be migrated by ftdisk.sys.
  2871. //
  2872. swprintf(openPath,L"\\device\\floppy%u",index);
  2873. //
  2874. // We use SpDiskRegistryAssignCdRomLetter() for map Floppy Letter too.
  2875. //
  2876. status = SpDiskRegistryAssignCdRomLetter(
  2877. openPath,
  2878. (WCHAR) ((WCHAR) (pOtherDrives[i] -> Drive) + L'A')
  2879. );
  2880. index++;
  2881. }
  2882. } else {
  2883. break;
  2884. }
  2885. }
  2886. }
  2887. for (curDrive = Win9xOtherDrives;curDrive;curDrive = curDrive -> Next) {
  2888. //
  2889. // Simply reserve the drive letter.
  2890. //
  2891. g_DriveLetters[curDrive -> Drive] = TRUE;
  2892. }
  2893. }
  2894. VOID
  2895. SpAssignCdRomDriveLettersToMatchWin9x(
  2896. IN PWIN9XDRIVELETTERINFO Win9xCdRoms
  2897. )
  2898. {
  2899. PWIN9XDRIVELETTERINFO curDrive;
  2900. SCSI_ADDRESS win9xAddress;
  2901. SCSI_ADDRESS ntCdAddresses[NUMDRIVELETTERS];
  2902. BOOL cdMapped[NUMDRIVELETTERS];
  2903. PWSTR curIdPtr;
  2904. WCHAR openPath[MAX_PATH+1];
  2905. HANDLE cdHandle;
  2906. INT numberOfCdRoms;
  2907. OBJECT_ATTRIBUTES objectAttributes;
  2908. UNICODE_STRING unicodeString;
  2909. IO_STATUS_BLOCK ioStatusBlock;
  2910. NTSTATUS openStatus;
  2911. NTSTATUS readStatus;
  2912. NTSTATUS status;
  2913. INT index;
  2914. //
  2915. // Clear out the ntCdDescriptions structure.
  2916. //
  2917. RtlZeroMemory(ntCdAddresses,sizeof(ntCdAddresses));
  2918. RtlZeroMemory(cdMapped,sizeof(cdMapped));
  2919. //
  2920. // gather scsi cdrom data.
  2921. //
  2922. numberOfCdRoms = 0;
  2923. for (index=0, openStatus=STATUS_SUCCESS;
  2924. ((index < NUMDRIVELETTERS) && NT_SUCCESS(openStatus));
  2925. index++) {
  2926. swprintf(openPath,L"\\device\\cdrom%u",index);
  2927. INIT_OBJA(&objectAttributes,&unicodeString,openPath);
  2928. openStatus = ZwCreateFile(
  2929. &cdHandle,
  2930. SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  2931. &objectAttributes,
  2932. &ioStatusBlock,
  2933. NULL, // allocation size
  2934. FILE_ATTRIBUTE_NORMAL,
  2935. FILE_SHARE_VALID_FLAGS, // full sharing
  2936. FILE_OPEN,
  2937. FILE_SYNCHRONOUS_IO_NONALERT,
  2938. NULL, // no EAs
  2939. 0
  2940. );
  2941. if(NT_SUCCESS(openStatus)) {
  2942. //
  2943. // Successfully opened a handle to the device, now, get the address information.
  2944. //
  2945. readStatus = ZwDeviceIoControlFile(
  2946. cdHandle,
  2947. NULL,
  2948. NULL,
  2949. NULL,
  2950. &ioStatusBlock,
  2951. IOCTL_SCSI_GET_ADDRESS,
  2952. NULL,
  2953. 0,
  2954. &(ntCdAddresses[numberOfCdRoms]),
  2955. sizeof(SCSI_ADDRESS)
  2956. );
  2957. if(!NT_SUCCESS(readStatus)) {
  2958. KdPrintEx((DPFLTR_SETUP_ID,
  2959. DPFLTR_ERROR_LEVEL,
  2960. "SETUP: Unable to get scsi address info for cd-rom %u (%x)\n",
  2961. index,
  2962. readStatus));
  2963. }
  2964. //
  2965. // Increment count of CdRoms
  2966. //
  2967. numberOfCdRoms++;
  2968. ZwClose(cdHandle);
  2969. } else {
  2970. KdPrintEx((DPFLTR_SETUP_ID,
  2971. DPFLTR_ERROR_LEVEL,
  2972. "SETUP: Unable to open handle to %ws. (%x)\n",
  2973. openPath,
  2974. openStatus));
  2975. }
  2976. }
  2977. //
  2978. // if we didn't find any CD-ROMs we have nothing to do
  2979. //
  2980. if (!numberOfCdRoms) {
  2981. return;
  2982. }
  2983. //
  2984. // Now, fill in a similar array of win9x drives..
  2985. //
  2986. for (curDrive = Win9xCdRoms;curDrive;curDrive = curDrive -> Next) {
  2987. //
  2988. // assume the drive is not mapped
  2989. //
  2990. curDrive -> StatusFlag = TRUE;
  2991. //
  2992. // Check to see if this is a SCSI device.
  2993. //
  2994. if (curDrive -> Identifier) {
  2995. curIdPtr = (PWSTR) curDrive -> Identifier;
  2996. //
  2997. // Collect the Win9x Address data.
  2998. //
  2999. win9xAddress.PortNumber = (UCHAR) Spwtoi(curIdPtr);
  3000. curIdPtr = wcschr(curIdPtr,L'^');
  3001. curIdPtr++;
  3002. win9xAddress.TargetId = (UCHAR) Spwtoi(curIdPtr);
  3003. curIdPtr = wcschr(curIdPtr,L'^');
  3004. curIdPtr++;
  3005. win9xAddress.Lun = (UCHAR) Spwtoi(curIdPtr);
  3006. //
  3007. // Now, loop through SCSI CD-Roms until a matching one is found.
  3008. //
  3009. for (index = 0; index < numberOfCdRoms; index++) {
  3010. if(!ntCdAddresses[index].Length){
  3011. continue;
  3012. }
  3013. if (win9xAddress.PortNumber == ntCdAddresses[index].PortNumber &&
  3014. win9xAddress.TargetId == ntCdAddresses[index].TargetId &&
  3015. win9xAddress.Lun == ntCdAddresses[index].Lun) {
  3016. if (cdMapped[index]) {
  3017. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Error: \\device\\cdrom%u already mapped..ignored.\n",index));
  3018. }
  3019. //
  3020. // Map the cdrom.
  3021. //
  3022. swprintf(openPath,L"\\device\\cdrom%u",index);
  3023. status = SpDiskRegistryAssignCdRomLetter(
  3024. openPath,
  3025. (WCHAR) ((WCHAR) (curDrive -> Drive) + L'A')
  3026. );
  3027. g_DriveLetters[curDrive -> Drive] = TRUE;
  3028. cdMapped[index] = TRUE;
  3029. curDrive -> StatusFlag = FALSE;
  3030. break;
  3031. }
  3032. }
  3033. } else {
  3034. curDrive -> StatusFlag = TRUE;
  3035. }
  3036. }
  3037. index = numberOfCdRoms - 1;
  3038. for (curDrive = Win9xCdRoms;curDrive;curDrive = curDrive -> Next) {
  3039. //
  3040. // If we haven't found a direct map yet, we'll any remaining drives.. This fixes the
  3041. // single IDE cdrom case. It could result in some reordering in multiple IDE CDRom
  3042. // systems. Still, this is the best we can do here.
  3043. //
  3044. if (curDrive -> StatusFlag) {
  3045. while (index >= 0 && cdMapped[index] == TRUE) {
  3046. index--;
  3047. }
  3048. if (index < 0){
  3049. break;
  3050. }
  3051. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Forcing Win9x CDRom Mapping for \\device\\cdrom%u, even though a direct match was not found.\n",index));
  3052. swprintf(openPath,L"\\device\\cdrom%u",index);
  3053. status = SpDiskRegistryAssignCdRomLetter(
  3054. openPath,
  3055. (WCHAR) ((WCHAR) (curDrive -> Drive) + L'A')
  3056. );
  3057. g_DriveLetters[curDrive -> Drive] = TRUE;
  3058. cdMapped[index] = TRUE;
  3059. index--;
  3060. }
  3061. }
  3062. }
  3063. VOID
  3064. SpAssignHardDriveLettersToMatchWin9x (
  3065. IN PWIN9XDRIVELETTERINFO Win9xHardDrives
  3066. )
  3067. {
  3068. PWIN9XDRIVELETTERINFO win9xDrive;
  3069. DWORD diskIndex;
  3070. PDISK_REGION region;
  3071. PPARTITIONED_DISK disk;
  3072. DWORD numMatchingRegions;
  3073. PDISK_REGION matchingRegion;
  3074. //
  3075. // Clear all partition drive letter informations.
  3076. // Note: This was copypasted from sppartit.c:SpGuessDriveLetters()
  3077. //
  3078. for(diskIndex=0; diskIndex<HardDiskCount; diskIndex++) {
  3079. for(region=PartitionedDisks[diskIndex].PrimaryDiskRegions; region; region=region->Next) {
  3080. region->DriveLetter = 0;
  3081. }
  3082. for(region=PartitionedDisks[diskIndex].ExtendedDiskRegions; region; region=region->Next) {
  3083. region->DriveLetter = 0;
  3084. }
  3085. }
  3086. //
  3087. // Iterate through the drives found in the winnt.sif file.
  3088. //
  3089. for (win9xDrive = Win9xHardDrives; win9xDrive; win9xDrive = win9xDrive -> Next) {
  3090. //
  3091. // find the partition that matches that drive.
  3092. //
  3093. numMatchingRegions = 0;
  3094. matchingRegion = NULL;
  3095. for(diskIndex=0; diskIndex<HardDiskCount; diskIndex++) {
  3096. disk = &PartitionedDisks[diskIndex];
  3097. //
  3098. // First, search through primary disk regions.
  3099. //
  3100. region = SpFirstPartitionedRegion(PartitionedDisks[diskIndex].PrimaryDiskRegions, TRUE);
  3101. while(region) {
  3102. if (SpCheckRegionForMatchWithWin9xData(region,win9xDrive -> Drive)) {
  3103. if (!matchingRegion) {
  3104. matchingRegion = region;
  3105. }
  3106. numMatchingRegions++;
  3107. }
  3108. region = SpNextPartitionedRegion(region, TRUE);
  3109. }
  3110. //
  3111. // Then, search through secondary disk regions.
  3112. //
  3113. region = SpFirstPartitionedRegion(PartitionedDisks[diskIndex].PrimaryDiskRegions, FALSE);
  3114. while(region) {
  3115. if (SpCheckRegionForMatchWithWin9xData(region,win9xDrive -> Drive)) {
  3116. if (!matchingRegion) {
  3117. matchingRegion = region;
  3118. }
  3119. numMatchingRegions++;
  3120. }
  3121. region = SpNextPartitionedRegion(region, FALSE);
  3122. }
  3123. }
  3124. if (numMatchingRegions == 1) {
  3125. //
  3126. // Found what we were looking for. Assign the win9x Drive letter
  3127. // to this region.
  3128. //
  3129. matchingRegion -> DriveLetter = L'A' + (WCHAR) win9xDrive -> Drive;
  3130. g_DriveLetters[win9xDrive -> Drive] = TRUE;
  3131. }
  3132. else if (numMatchingRegions > 1) {
  3133. //
  3134. // We are in trouble. print an error.
  3135. //
  3136. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: More than one drive matches Win9x drive.\n"));
  3137. } else {
  3138. //
  3139. // Big trouble. No regions matched the data collected on Windows 95.
  3140. //
  3141. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not find a drive matching Win9x.\n"));
  3142. }
  3143. }
  3144. }
  3145. VOID
  3146. SpRegisterHardDriveLetters (
  3147. VOID
  3148. )
  3149. {
  3150. BOOL rf;
  3151. PDISK_REGION curRegion;
  3152. BOOL wasExtended;
  3153. DWORD diskIndex;
  3154. PPARTITIONED_DISK disk;
  3155. LARGE_INTEGER startingOffset;
  3156. LARGE_INTEGER length;
  3157. UCHAR driveLetter;
  3158. for(diskIndex=0; diskIndex<HardDiskCount; diskIndex++) {
  3159. disk = &PartitionedDisks[diskIndex];
  3160. //
  3161. // Skip removable media. If a disk is off-line it's hard to imagine
  3162. // that we'll actually have any partitioned spaces on it so
  3163. // we don't do any special checks here for that condition.
  3164. //
  3165. if(!(disk->HardDisk->Characteristics & FILE_REMOVABLE_MEDIA)) {
  3166. //
  3167. // First, do all of the primary disk regions for this disk.
  3168. //
  3169. curRegion = SpFirstPartitionedRegion(PartitionedDisks[diskIndex].PrimaryDiskRegions, TRUE);
  3170. while(curRegion) {
  3171. //
  3172. // We only care about partitioned spaces that have drive letters.
  3173. //
  3174. if(curRegion->PartitionedSpace && curRegion -> DriveLetter) {
  3175. //
  3176. // Collect information needed for call to DiskRegistryAssignDriveLetter
  3177. //
  3178. SpGetPartitionStartingOffsetAndLength(
  3179. diskIndex,
  3180. curRegion,
  3181. FALSE,
  3182. &startingOffset,
  3183. &length
  3184. );
  3185. driveLetter = (UCHAR) ('A' + (curRegion -> DriveLetter - L'A'));
  3186. rf = SpDiskRegistryAssignDriveLetter(
  3187. disk -> HardDisk -> Signature,
  3188. startingOffset,
  3189. length,
  3190. driveLetter
  3191. );
  3192. if (!rf) {
  3193. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: DiskRegistryAssignDriveLetter call failed.\n"));
  3194. }
  3195. else {
  3196. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Added sticky letter %c to Disk Registry.\n",driveLetter));
  3197. }
  3198. }
  3199. curRegion = SpNextPartitionedRegion(curRegion, TRUE);
  3200. }
  3201. //
  3202. // Now, do all of the extended disk regions.
  3203. //
  3204. curRegion = SpFirstPartitionedRegion(PartitionedDisks[diskIndex].PrimaryDiskRegions, FALSE);
  3205. while(curRegion) {
  3206. //
  3207. // We only care about partitioned spaces that have drive letters.
  3208. //
  3209. if(curRegion->PartitionedSpace && curRegion -> DriveLetter) {
  3210. //
  3211. // Collect information needed for call to DiskRegistryAssignDriveLetter
  3212. //
  3213. SpGetPartitionStartingOffsetAndLength(
  3214. diskIndex,
  3215. curRegion,
  3216. TRUE,
  3217. &startingOffset,
  3218. &length
  3219. );
  3220. driveLetter = (UCHAR) ('A' + (curRegion -> DriveLetter - L'A'));
  3221. rf = SpDiskRegistryAssignDriveLetter(
  3222. disk -> HardDisk -> Signature,
  3223. startingOffset,
  3224. length,
  3225. driveLetter
  3226. );
  3227. if (!rf) {
  3228. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: DiskRegistryAssignDriveLetter call failed.\n"));
  3229. }
  3230. else {
  3231. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Added sticky letter %c to Disk Registry.\n",driveLetter));
  3232. }
  3233. }
  3234. curRegion = SpNextPartitionedRegion(curRegion, FALSE);
  3235. }
  3236. }
  3237. }
  3238. }
  3239. #define WIN9XHARDDRIVES 0
  3240. #define WIN9XCDROMS 1
  3241. #define WIN9XOTHERS 2
  3242. #define WIN9XNUMDRIVETYPES 3
  3243. VOID
  3244. SpAssignDriveLettersToMatchWin9x (
  3245. IN PVOID WinntSif
  3246. )
  3247. {
  3248. PWIN9XDRIVELETTERINFO win9xDrive = NULL;
  3249. PWIN9XDRIVELETTERINFO win9xDrives[WIN9XNUMDRIVETYPES];
  3250. DWORD index;
  3251. DWORD lineCount;
  3252. PWSTR driveString;
  3253. PWSTR dataString;
  3254. PWSTR curString;
  3255. DWORD drive;
  3256. DWORD type;
  3257. DWORD driveType;
  3258. //
  3259. // Read in the data on hard disks that was collected during the Detection
  3260. // phase of Win95 setup. This data is stored in the winnt.sif file
  3261. // in the [Win9x.DriveLetterInfo] section.
  3262. lineCount = SpCountLinesInSection(WinntSif,WINNT_D_WIN9XDRIVES_W);
  3263. if (!lineCount) {
  3264. //
  3265. // No information in the winnt.sif file, so nothing to do. Get out of here early.
  3266. //
  3267. return;
  3268. }
  3269. //
  3270. // build Disk Registry information. This will be used to store
  3271. // sticky drive letters.
  3272. //
  3273. SpBuildDiskRegistry();
  3274. //
  3275. // Build a list of usable drive letters. All drive letters should be
  3276. // initially usable exceptr for 'A' and 'B'
  3277. // For NEC98, hard drive letter usually assigned from 'A'.
  3278. // So we don't set TRUE in that case.
  3279. //
  3280. RtlZeroMemory(g_DriveLetters,sizeof(g_DriveLetters));
  3281. if( !IsNEC_98 || !DriveAssignFromA) {
  3282. g_DriveLetters[0] = g_DriveLetters[1] = TRUE;
  3283. }
  3284. RtlZeroMemory(win9xDrives,sizeof(win9xDrives));
  3285. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Win9x Drive Letters to map: %u\n", lineCount));
  3286. for (index = 0;index < lineCount; index++) {
  3287. //
  3288. // The Drive Number is in the key.
  3289. //
  3290. driveString = SpGetKeyName (
  3291. WinntSif,
  3292. WINNT_D_WIN9XDRIVES_W,
  3293. index
  3294. );
  3295. //
  3296. // This conditional _should_ always be true. but, just in case..
  3297. //
  3298. if (driveString) {
  3299. drive = Spwtoi(driveString);
  3300. //
  3301. // Now, get the type of this drive.
  3302. //
  3303. dataString = SpGetSectionKeyIndex (
  3304. WinntSif,
  3305. WINNT_D_WIN9XDRIVES_W,
  3306. driveString,
  3307. 0
  3308. );
  3309. if (dataString) {
  3310. curString = dataString;
  3311. if (*curString != L',') {
  3312. type = Spwtoi(curString);
  3313. }
  3314. //
  3315. // Advance dataString to the start of the identifier string.
  3316. //
  3317. curString = wcschr(curString,L',');
  3318. if (curString) {
  3319. //
  3320. // Pass the ','
  3321. //
  3322. *curString++;
  3323. }
  3324. }
  3325. else {
  3326. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not retrieve type for Win9x drive %ws\n",driveString));
  3327. type = DOSDEVICE_DRIVE_UNKNOWN;
  3328. }
  3329. //
  3330. // Now, add this drive to the list of win9x drives that we are
  3331. // dealing with.
  3332. //
  3333. win9xDrive = SpMemAlloc(sizeof(WIN9XDRIVELETTERINFO));
  3334. if (win9xDrive) {
  3335. //
  3336. // assign all of the gathered data.
  3337. //
  3338. win9xDrive -> Drive = drive;
  3339. win9xDrive -> Type = type;
  3340. win9xDrive -> Identifier = curString;
  3341. //
  3342. // place this drive into the list of drives of its type.
  3343. //
  3344. switch (type) {
  3345. case DOSDEVICE_DRIVE_FIXED:
  3346. driveType = WIN9XHARDDRIVES;
  3347. break;
  3348. case DOSDEVICE_DRIVE_CDROM:
  3349. driveType = WIN9XCDROMS;
  3350. break;
  3351. default:
  3352. driveType = WIN9XOTHERS;
  3353. break;
  3354. }
  3355. win9xDrive -> Next = win9xDrives[driveType];
  3356. win9xDrives[driveType] = win9xDrive;
  3357. }
  3358. else {
  3359. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not allocate memory for Win9x drive letter information.\n"));
  3360. //
  3361. // No use sticking around.
  3362. //
  3363. goto c0;
  3364. }
  3365. }
  3366. else {
  3367. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not find drive string in winnt.sif line.\n"));
  3368. goto c0;
  3369. }
  3370. }
  3371. //
  3372. // First, and most importantly, assign the drive letters for hard disks.
  3373. // If this is done incorrectly, setup may fail.
  3374. //
  3375. if (win9xDrives[WIN9XHARDDRIVES]) {
  3376. SpAssignHardDriveLettersToMatchWin9x (win9xDrives[WIN9XHARDDRIVES]);
  3377. }
  3378. //
  3379. // Secondly, assign drive letters for any CD-Roms.
  3380. //
  3381. if (win9xDrives[WIN9XCDROMS]) {
  3382. SpAssignCdRomDriveLettersToMatchWin9x(win9xDrives[WIN9XCDROMS]);
  3383. }
  3384. //
  3385. // Third, if possible, assign drive letters for other devices.
  3386. //
  3387. if (win9xDrives[WIN9XOTHERS]) {
  3388. SpAssignOtherDriveLettersToMatchWin9x(win9xDrives[WIN9XOTHERS]);
  3389. }
  3390. //
  3391. // Assign drive letters for any HDD partitions that have not been
  3392. // previously mapped. (These are drives unknown to Win9x.)
  3393. //
  3394. SpAssignDriveLettersToRemainingPartitions();
  3395. //
  3396. // Now, write all hard drive information into the disk registry.
  3397. //
  3398. SpRegisterHardDriveLetters();
  3399. c0:
  3400. ;
  3401. }
  3402. VOID
  3403. SpWin9xOverrideGuiModeCodePage (
  3404. HKEY NlsRegKey
  3405. )
  3406. {
  3407. PWSTR data;
  3408. NTSTATUS status;
  3409. WCHAR fileName[MAX_PATH];
  3410. data = SpGetSectionKeyIndex (
  3411. WinntSifHandle,
  3412. SIF_DATA,
  3413. WINNT_D_GUICODEPAGEOVERRIDE_W,
  3414. 0
  3415. );
  3416. if (!data) {
  3417. //
  3418. // Nothing to do.
  3419. //
  3420. return;
  3421. }
  3422. wcscpy (fileName, L"c_");
  3423. wcscat (fileName, data);
  3424. wcscat (fileName, L".nls");
  3425. status = SpOpenSetValueAndClose (NlsRegKey, L"CodePage", data, STRING_VALUE(fileName));
  3426. if(!NT_SUCCESS(status)) {
  3427. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "Setup: Unable to override the code page for GUI mode. Some strings may be incorrect.\n"));
  3428. return;
  3429. }
  3430. status = SpOpenSetValueAndClose (NlsRegKey, L"CodePage", L"Acp", STRING_VALUE(data));
  3431. if(!NT_SUCCESS(status)) {
  3432. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "Setup: Unable to override the ACP for GUI mode. Some strings may be incorrect.\n"));
  3433. return;
  3434. }
  3435. }
  3436. BOOLEAN
  3437. SpIsWindowsUpgrade(
  3438. IN PVOID SifFileHandle
  3439. )
  3440. /*++
  3441. Routine Description:
  3442. Determines whether we are upgrading Windows 3.x or Windows 9x.
  3443. Arguments:
  3444. SifFileHandle : Handle to WINNT.SIF file which has
  3445. the appropriate 3.x/9x upgrade flag value
  3446. Return Value:
  3447. TRUE : if upgrading Windows 3.x or 9X
  3448. FALSE : otherwise
  3449. --*/
  3450. {
  3451. BOOLEAN Result = FALSE;
  3452. PWSTR Value = 0;
  3453. Value = SpGetSectionKeyIndex(SifFileHandle, SIF_DATA,
  3454. WINNT_D_WIN95UPGRADE_W, 0);
  3455. if (!Value) {
  3456. Value = SpGetSectionKeyIndex(SifFileHandle, SIF_DATA,
  3457. WINNT_D_WIN31UPGRADE_W, 0);
  3458. }
  3459. if (Value)
  3460. Result = (_wcsicmp(Value, WINNT_A_YES_W) == 0);
  3461. return Result;
  3462. }