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

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