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.

868 lines
20 KiB

  1. #include "spprecmp.h"
  2. #pragma hdrstop
  3. #define MAX_NT_DIR_LEN 50
  4. /*++
  5. Revision History:
  6. --*/
  7. VOID
  8. SpCheckDirectoryForNt(
  9. IN PDISK_REGION Region,
  10. IN PWSTR Directory,
  11. OUT PBOOLEAN ReselectDirectory,
  12. OUT PBOOLEAN NtInDirectory
  13. );
  14. VOID
  15. pSpDrawGetNtPathScreen(
  16. OUT PULONG EditFieldY
  17. );
  18. ValidationValue
  19. SpGetPathKeyCallback(
  20. IN ULONG Key
  21. );
  22. BOOLEAN
  23. SpIsValid8Dot3(
  24. IN PWSTR Path
  25. );
  26. BOOLEAN
  27. pSpConsecutiveBackslashes(
  28. IN PWSTR Path
  29. );
  30. VOID
  31. SpNtfsNameFilter(
  32. IN OUT PWSTR Path
  33. );
  34. BOOLEAN
  35. SpGetUnattendedPath(
  36. IN PDISK_REGION Region,
  37. IN PWSTR DefaultPath,
  38. OUT PWSTR TargetPath
  39. );
  40. BOOLEAN
  41. SpGenerateNTPathName(
  42. IN PDISK_REGION Region,
  43. IN PWSTR DefaultPath,
  44. OUT PWSTR TargetPath
  45. );
  46. //
  47. // From spcopy.c.
  48. //
  49. BOOLEAN
  50. SpDelEnumFileAndDirectory(
  51. IN PCWSTR DirName,
  52. IN PFILE_BOTH_DIR_INFORMATION FileInfo,
  53. OUT PULONG ret,
  54. IN PVOID Pointer
  55. );
  56. extern PVOID FileDeleteGauge;
  57. BOOLEAN
  58. SpGetTargetPath(
  59. IN PVOID SifHandle,
  60. IN PDISK_REGION Region,
  61. IN PWSTR DefaultPath,
  62. OUT PWSTR *TargetPath
  63. )
  64. //
  65. // Return value - True - indicates that the path has to be wiped out
  66. // False - the path doesn't already exist
  67. //
  68. {
  69. ULONG EditFieldY;
  70. WCHAR NtDir[MAX_NT_DIR_LEN+2];
  71. BOOLEAN BadDirectory = FALSE;
  72. BOOLEAN NtAlreadyPresent;
  73. BOOLEAN GotUnattendedPath = FALSE;
  74. BOOLEAN WipeDir = FALSE;
  75. NtDir[0] = 0;
  76. //
  77. // If this is an ASR recovery session, just get the target path from
  78. // the dr_state.sif file and return.
  79. //
  80. if( SpDrEnabled() && ! RepairWinnt ) {
  81. PWSTR TargetPathFromDrState;
  82. TargetPathFromDrState = SpDrGetNtDirectory();
  83. *TargetPath = SpDupStringW(TargetPathFromDrState);
  84. ASSERT(*TargetPath);
  85. NTUpgrade = DontUpgrade;
  86. return(FALSE);
  87. }
  88. //
  89. // If this is unattended operation, fetch the path from the
  90. // unattended script. The path we get there might have
  91. // indicate that we should generate a pathname. This allows
  92. // installation into a path that is guaranteed to be unique.
  93. // (in case the user already has nt on the machine, etc).
  94. //
  95. if(UnattendedOperation) {
  96. GotUnattendedPath = SpGetUnattendedPath(Region,DefaultPath,NtDir);
  97. } else {
  98. if (PreferredInstallDir) {
  99. GotUnattendedPath = TRUE;
  100. wcscpy( NtDir, PreferredInstallDir );
  101. } else {
  102. GotUnattendedPath = TRUE;
  103. wcscpy( NtDir, DefaultPath );
  104. }
  105. }
  106. if (!GotUnattendedPath) {
  107. BadDirectory = TRUE;
  108. }
  109. do {
  110. if (BadDirectory) {
  111. //
  112. // we do not have a good path so ask the user
  113. //
  114. ASSERT(wcslen(DefaultPath) < MAX_NT_DIR_LEN);
  115. ASSERT(*DefaultPath == L'\\');
  116. wcsncpy(NtDir,DefaultPath,MAX_NT_DIR_LEN);
  117. NtDir[MAX_NT_DIR_LEN] = 0;
  118. pSpDrawGetNtPathScreen(&EditFieldY);
  119. SpGetInput(
  120. SpGetPathKeyCallback,
  121. 6, // left edge of the edit field
  122. EditFieldY,
  123. MAX_NT_DIR_LEN,
  124. NtDir,
  125. FALSE // escape clears edit field
  126. );
  127. }
  128. //
  129. // If the user didn't start with a backslash, put one in there
  130. // for him.
  131. //
  132. if(NtDir[0] != L'\\') {
  133. RtlMoveMemory(NtDir+1,NtDir,MAX_NT_DIR_LEN+1);
  134. NtDir[0] = L'\\';
  135. }
  136. //
  137. // Assume the directory is OK and not already present.
  138. //
  139. BadDirectory = FALSE;
  140. NtAlreadyPresent = FALSE;
  141. //
  142. // Force 8.3 because otherwise WOW won't run.
  143. // This checks also nabs "" and "\" and disallows them.
  144. //
  145. if(!SpIsValid8Dot3(NtDir)) {
  146. BadDirectory = TRUE;
  147. } else {
  148. //
  149. // Perform a filtering operation that coalesces
  150. // consecutive dots, etc.
  151. //
  152. SpNtfsNameFilter(NtDir);
  153. //
  154. // If the name has consecutive backslashes, disallow it.
  155. //
  156. if(pSpConsecutiveBackslashes(NtDir)) {
  157. BadDirectory = TRUE;
  158. }
  159. }
  160. //
  161. // If we have a bad directory, tell the user.
  162. //
  163. if(BadDirectory) {
  164. SpDisplayScreen(SP_SCRN_INVALID_NTPATH,3,HEADER_HEIGHT+1);
  165. SpDisplayStatusOptions(
  166. DEFAULT_STATUS_ATTRIBUTE,
  167. SP_STAT_ENTER_EQUALS_CONTINUE,
  168. 0
  169. );
  170. SpInputDrain();
  171. while(SpInputGetKeypress() != ASCI_CR) ;
  172. } else {
  173. //
  174. // The directory is good. Check to see if Windows NT is
  175. // already in there. If it is, then the user will have
  176. // the option of reselecting a path or overwriting the
  177. // existing installation. This is brute force. Next
  178. // time look for an opportunity to be more elegant.
  179. //
  180. if(!SpDrEnabled()) {
  181. SpCheckDirectoryForNt(Region,NtDir,&BadDirectory,&NtAlreadyPresent);
  182. } else {
  183. BadDirectory = FALSE;
  184. }
  185. //
  186. // If the directory is OK and we didn't find Windows NT in it,
  187. // then see if the directory is the Windows directory and whether
  188. // the user wants to install into this. If we found Windows NT
  189. // in this directory, no user input is needed. We just need to
  190. // find out if this also contains a Windows installation.
  191. //
  192. if(!BadDirectory && NtAlreadyPresent)
  193. WipeDir = TRUE;
  194. }
  195. } while(BadDirectory);
  196. //
  197. // Remove trailing backslash. Only have to worry about one
  198. // because if there were two, pSpConsecutiveBackslashes() would
  199. // have caught this earlier and we'd never have gotten here.
  200. //
  201. if(NtDir[wcslen(NtDir)-1] == L'\\') {
  202. NtDir[wcslen(NtDir)-1] = 0;
  203. }
  204. //
  205. // Make a duplicate of the directory name.
  206. //
  207. *TargetPath = SpDupStringW(NtDir);
  208. ASSERT(*TargetPath);
  209. return( WipeDir );
  210. }
  211. BOOLEAN
  212. SpGenerateNTPathName(
  213. IN PDISK_REGION Region,
  214. IN PWSTR DefaultPath,
  215. OUT PWSTR TargetPath
  216. )
  217. /*++
  218. Routine Description:
  219. Using the default path as a starting point,
  220. this routine generates a unique path name
  221. to install nt into.
  222. Arguments:
  223. Region - supplies region to which nt is being installed.
  224. DefaultPath - supplies the default path for the installation.
  225. The path to install to will be based on this name.
  226. TargetPath - receives the path to install to if the return value is TRUE.
  227. This buffer must be large enough to hold MAX_NT_DIR_LEN+2 wchars.
  228. Return Value:
  229. TRUE if the path we return is valid and should be used as
  230. the target path. FALSE otherwise.
  231. --*/
  232. {
  233. PWCHAR p;
  234. unsigned i;
  235. WCHAR num[5];
  236. //
  237. // Init TargetPath and remember where he ends.
  238. //
  239. wcscpy( TargetPath, DefaultPath );
  240. num[0] = L'.';
  241. p = TargetPath + wcslen( TargetPath );
  242. //
  243. // Form the region's nt pathname.
  244. //
  245. SpNtNameFromRegion(
  246. Region,
  247. TemporaryBuffer,
  248. sizeof(TemporaryBuffer),
  249. PartitionOrdinalCurrent
  250. );
  251. //
  252. // Using extensions with numerical values 0-999, attempt to locate
  253. // a nonexistent directory name.
  254. //
  255. for(i=0; i<999; i++) {
  256. //
  257. // See whether a directory or file exists. If not, we found our path.
  258. //
  259. if( (!SpNFilesExist(TemporaryBuffer,&TargetPath,1,TRUE )) &&
  260. (!SpNFilesExist(TemporaryBuffer,&TargetPath,1,FALSE)) ) {
  261. return(TRUE);
  262. }
  263. swprintf(&num[1],L"%u",i);
  264. wcscpy(p,num);
  265. }
  266. //
  267. // Couldn't find a pathname that doesn't exist.
  268. //
  269. return FALSE;
  270. }
  271. BOOLEAN
  272. SpGetUnattendedPath(
  273. IN PDISK_REGION Region,
  274. IN PWSTR DefaultPath,
  275. OUT PWSTR TargetPath
  276. )
  277. /*++
  278. Routine Description:
  279. In an unattended installation, look in the unattended script
  280. to determine the target path. The target path can either be fully
  281. specified or can be * which will cause is to generate
  282. a unique pathname. This is useful to ensure that nt gets installed
  283. into a unique directory when other installations may be present
  284. on the same machine.
  285. Call this routine only if this is unattended mode setup.
  286. Arguments:
  287. Region - supplies region to which nt is being installed.
  288. DefaultPath - supplies the default path for the installation.
  289. The path to install to will be based on this name.
  290. TargetPath - receives the path to install to if the return value is TRUE.
  291. This buffer must be large enough to hold MAX_NT_DIR_LEN+2 wchars.
  292. Return Value:
  293. TRUE if the path we return is valid and should be used as
  294. the target path. FALSE otherwise.
  295. --*/
  296. {
  297. PWSTR PathSpec;
  298. ASSERT(UnattendedOperation);
  299. if(!UnattendedOperation) {
  300. return(FALSE);
  301. }
  302. PathSpec = SpGetSectionKeyIndex(UnattendedSifHandle,SIF_UNATTENDED,L"TargetPath",0);
  303. if(!PathSpec) {
  304. //
  305. // Default to *.
  306. //
  307. PathSpec = L"*";
  308. }
  309. //
  310. // if it's not "*" then it's an absolute path -- just return it.
  311. //
  312. if(wcscmp(PathSpec,L"*")) {
  313. wcsncpy(TargetPath,PathSpec,MAX_NT_DIR_LEN);
  314. TargetPath[MAX_NT_DIR_LEN] = 0;
  315. return(TRUE);
  316. }
  317. return SpGenerateNTPathName( Region, DefaultPath, TargetPath );
  318. }
  319. VOID
  320. SpCheckDirectoryForNt(
  321. IN PDISK_REGION Region,
  322. IN PWSTR Directory,
  323. OUT PBOOLEAN ReselectDirectory,
  324. OUT PBOOLEAN NtInDirectory
  325. )
  326. /*++
  327. Routine Description:
  328. Check a directory for the presence of Windows NT. If Windows NT
  329. is in there, then inform the user that if he continues, his existing
  330. configuration will be overwritten.
  331. Arguments:
  332. Region - supplies region descriptor for partition to check for nt.
  333. Directory - supplies name of directory on the partition ro check for nt.
  334. ReselectDirectory - receives boolean value indicating whether the caller
  335. should ask the user to select a different directory.
  336. NtInDirectory - receives a boolean value indicating whether we found
  337. windows nt in the given directory.
  338. Return Value:
  339. None.
  340. --*/
  341. {
  342. ULONG ValidKeys[3] = { KEY_F3,ASCI_ESC,0 };
  343. ULONG Mnemonics[2] = { MnemonicDeletePartition2, 0 };
  344. //
  345. // Assume the directory is ok as-is and so the user does not have to
  346. // select a different one.
  347. //
  348. *ReselectDirectory = FALSE;
  349. *NtInDirectory = FALSE;
  350. //
  351. // Check for Windows NT in the directory.
  352. // If it's in there, then ask the user whether he wants to
  353. // overwrite it.
  354. //
  355. SpNtNameFromRegion(
  356. Region,
  357. TemporaryBuffer,
  358. sizeof(TemporaryBuffer),
  359. PartitionOrdinalCurrent
  360. );
  361. if( (!SpNFilesExist(TemporaryBuffer,&Directory,1,TRUE )) &&
  362. (!SpNFilesExist(TemporaryBuffer,&Directory,1,FALSE)) ) {
  363. return;
  364. }
  365. else{
  366. *NtInDirectory = TRUE;
  367. while(1) {
  368. SpStartScreen( SP_SCRN_NTPATH_EXISTS,
  369. 3,
  370. HEADER_HEIGHT+1,
  371. FALSE,
  372. FALSE,
  373. DEFAULT_ATTRIBUTE,
  374. Directory );
  375. SpDisplayStatusOptions(
  376. DEFAULT_STATUS_ATTRIBUTE,
  377. SP_STAT_L_EQUALS_DELETE,
  378. SP_STAT_ESC_EQUALS_NEW_PATH,
  379. SP_STAT_F3_EQUALS_EXIT,
  380. 0
  381. );
  382. switch(SpWaitValidKey(ValidKeys,NULL,Mnemonics)) {
  383. case KEY_F3:
  384. SpConfirmExit();
  385. break;
  386. case ASCI_ESC:
  387. //
  388. // Reselect path.
  389. //
  390. *ReselectDirectory = TRUE;
  391. // fall through
  392. default:
  393. //
  394. // Path is ok, just return.
  395. //
  396. return;
  397. }
  398. }
  399. }
  400. }
  401. ValidationValue
  402. SpGetPathKeyCallback(
  403. IN ULONG Key
  404. )
  405. {
  406. ULONG u;
  407. switch(Key) {
  408. case KEY_F3:
  409. SpConfirmExit();
  410. pSpDrawGetNtPathScreen(&u);
  411. return(ValidateRepaint);
  412. default:
  413. //
  414. // Ignore special keys and illegal characters.
  415. // Use the set of illegal FAT characters.
  416. // Disallow 127 because there is no ANSI equivalent
  417. // and so the name can't be displayed by Windows.
  418. // Disallow space because DOS can't handle it.
  419. // Disallow oem characters because we have problems
  420. // booting if they are used.
  421. //
  422. if((Key & KEY_NON_CHARACTER)
  423. || wcschr(L" \"*+,/:;<=>?[]|!#$&@^'`{}()%~",(WCHAR)Key)
  424. || (Key >= 127) || (Key < 32))
  425. {
  426. return(ValidateReject);
  427. }
  428. break;
  429. }
  430. return(ValidateAccept);
  431. }
  432. VOID
  433. pSpDrawGetNtPathScreen(
  434. OUT PULONG EditFieldY
  435. )
  436. {
  437. SpDisplayScreen(SP_SCRN_GETPATH_1,3,HEADER_HEIGHT+1);
  438. *EditFieldY = NextMessageTopLine + 1;
  439. SpContinueScreen(SP_SCRN_GETPATH_2,3,4,FALSE,DEFAULT_ATTRIBUTE);
  440. SpDisplayStatusOptions(
  441. DEFAULT_STATUS_ATTRIBUTE,
  442. SP_STAT_ENTER_EQUALS_CONTINUE,
  443. SP_STAT_F3_EQUALS_EXIT,
  444. 0
  445. );
  446. }
  447. BOOLEAN
  448. SpIsValid8Dot3(
  449. IN PWSTR Path
  450. )
  451. /*++
  452. Routine Description:
  453. Check whether a path is valid 8.3. The path may or may not start with
  454. a backslash. Only backslashes are recognized as path separators.
  455. Individual characters are not checked for validity (ie, * would not
  456. invalidate the path). The path may or may not terminate with a backslash.
  457. A component may have a dot without characters in the extension
  458. (ie, a\b.\c is valid).
  459. \ and "" are explicitly disallowed even though they fit the rules.
  460. Arguments:
  461. Path - pointer to path to check.
  462. Return Value:
  463. TRUE if valid 8.3, FALSE otherwise.
  464. --*/
  465. {
  466. unsigned Count;
  467. BOOLEAN DotSeen,FirstChar;
  468. if((*Path == 0) || ((Path[0] == L'\\') && (Path[1] == 0))) {
  469. return(FALSE);
  470. }
  471. DotSeen = FALSE;
  472. FirstChar = TRUE;
  473. Count = 0;
  474. while(*Path) {
  475. //
  476. // Path points to start of current component (1 past the slash)
  477. //
  478. switch(*Path) {
  479. case L'.':
  480. if(FirstChar) {
  481. return(FALSE);
  482. }
  483. if(DotSeen) {
  484. return(FALSE);
  485. }
  486. Count = 0;
  487. DotSeen = TRUE;
  488. break;
  489. case L'\\':
  490. DotSeen = FALSE;
  491. FirstChar = TRUE;
  492. Count = 0;
  493. if(*(++Path) == '\\') {
  494. // 2 slashes in a row
  495. return(FALSE);
  496. }
  497. continue;
  498. default:
  499. Count++;
  500. FirstChar = FALSE;
  501. if((Count == 4) && DotSeen) {
  502. return(FALSE);
  503. }
  504. if(Count == 9) {
  505. return(FALSE);
  506. }
  507. }
  508. Path++;
  509. }
  510. return(TRUE);
  511. }
  512. BOOLEAN
  513. pSpConsecutiveBackslashes(
  514. IN PWSTR Path
  515. )
  516. {
  517. int x = wcslen(Path);
  518. int i;
  519. for(i=0; i<x-1; i++) {
  520. if((Path[i] == L'\\') && (Path[i+1] == L'\\')) {
  521. return(TRUE);
  522. }
  523. }
  524. return(FALSE);
  525. }
  526. VOID
  527. SpNtfsNameFilter(
  528. IN OUT PWSTR Path
  529. )
  530. /*++
  531. Routine Description:
  532. Strip trailing .' within a path component. This also strips tailing
  533. .'s from the entire path itself. Also condense other consecutive .'s
  534. into a single ..
  535. Example: \...\..a...b. ==> \\.a.b
  536. Arguments:
  537. Path - On input, supplies the path to be filtered. On output, contains
  538. the filtered pathname.
  539. Return Value:
  540. None.
  541. --*/
  542. {
  543. PWSTR TempPath = SpDupStringW(Path);
  544. PWSTR p,q;
  545. BOOLEAN Dot;
  546. if (TempPath) {
  547. //
  548. // Coalesce adjacent dots and strip trailing dots within a component.
  549. // xfers Path ==> TempPath
  550. //
  551. for(Dot=FALSE,p=Path,q=TempPath; *p; p++) {
  552. if(*p == L'.') {
  553. Dot = TRUE;
  554. } else {
  555. if(Dot && (*p != L'\\')) {
  556. *q++ = L'.';
  557. }
  558. Dot = FALSE;
  559. *q++ = *p;
  560. }
  561. }
  562. *q = 0;
  563. wcscpy(Path,TempPath);
  564. }
  565. }
  566. ULONG
  567. SpGetMaxNtDirLen( VOID )
  568. {
  569. return( MAX_NT_DIR_LEN );
  570. }
  571. VOID
  572. SpDeleteExistingTargetDir(
  573. IN PDISK_REGION Region,
  574. IN PWSTR NtDir,
  575. IN BOOLEAN GaugeNeeded,
  576. IN DWORD MsgId
  577. )
  578. /*
  579. Parameters :
  580. Region - Pointer to Region structure associated with the partition that contains the OS
  581. NtDir - The directory to recursively delete
  582. GaugeNeeded - Should we display a gauge while deleting the dir ?
  583. MsgId - Use this if you want to display a title message
  584. */
  585. {
  586. ENUMFILESRESULT Result;
  587. PWSTR FullNtPath;
  588. NTSTATUS Status, Stat;
  589. PULONG RecursiveOperation;
  590. if( MsgId )
  591. SpDisplayScreen(MsgId, 3, 4 );
  592. SpNtNameFromRegion(
  593. Region,
  594. TemporaryBuffer,
  595. sizeof(TemporaryBuffer),
  596. PartitionOrdinalCurrent
  597. );
  598. SpConcatenatePaths( TemporaryBuffer, NtDir );
  599. FullNtPath = SpDupStringW(TemporaryBuffer);
  600. // First try and delete the install directory
  601. // This is to see if itself is a reparse point. Also if it is just an empty dir
  602. // then we save time.
  603. Stat = SpDeleteFileEx( FullNtPath,
  604. NULL,
  605. NULL,
  606. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  607. FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT );
  608. if(NT_SUCCESS(Stat)){
  609. SpMemFree( FullNtPath);
  610. return; // We are done
  611. }
  612. RecursiveOperation = SpMemAlloc(sizeof(ULONG));
  613. ASSERT( RecursiveOperation );
  614. //
  615. // Do the counting phase for the clean-up
  616. //
  617. *RecursiveOperation = SP_COUNT_FILESTODELETE;
  618. SpDisplayStatusText(SP_STAT_SETUP_IS_EXAMINING_DIRS,DEFAULT_STATUS_ATTRIBUTE);
  619. Result = SpEnumFilesRecursiveDel(
  620. FullNtPath,
  621. SpDelEnumFileAndDirectory,
  622. &Status,
  623. RecursiveOperation);
  624. //
  625. // Now do the cleanup (actual deleting)
  626. //
  627. FileDeleteGauge = NULL;
  628. if( GaugeNeeded ){
  629. SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_SETUP_IS_DELETING);
  630. FileDeleteGauge = SpCreateAndDisplayGauge(*RecursiveOperation,0,15,TemporaryBuffer,NULL,GF_PERCENTAGE,0);
  631. ASSERT(FileDeleteGauge);
  632. }
  633. *RecursiveOperation = SP_DELETE_FILESTODELETE;
  634. Result = SpEnumFilesRecursiveDel(
  635. FullNtPath,
  636. SpDelEnumFileAndDirectory,
  637. &Status,
  638. RecursiveOperation);
  639. //Delete the main parent as the recursive call only cleans out everythin below it
  640. Stat = SpDeleteFileEx( FullNtPath,
  641. NULL,
  642. NULL,
  643. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  644. FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT );
  645. if(!NT_SUCCESS(Stat) && (Stat != STATUS_OBJECT_NAME_NOT_FOUND)) {
  646. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Dir Not Deleted - Status - %ws (%lx)\n", (PWSTR)FullNtPath, Stat));
  647. }
  648. if (GaugeNeeded) {
  649. SpDestroyGauge(FileDeleteGauge);
  650. FileDeleteGauge = NULL;
  651. }
  652. SpMemFree( FullNtPath );
  653. SpMemFree( RecursiveOperation );
  654. return;
  655. }