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.

547 lines
14 KiB

  1. #include "precomp.h"
  2. #pragma hdrstop
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. // Define registry section and value
  6. #define DSA_CONFIG_SECTION TEXT("System\\CurrentControlSet\\Services\\NTDS\\Parameters")
  7. #define SCHEMAVERSION TEXT("Schema Version")
  8. BOOL
  9. IsNT5DC()
  10. /*++
  11. Routine Descrtiption:
  12. Checks if a machine is a NT5 DC. Uses a call to RtlGetProductType that
  13. requires ntdll.dll. Since ntdll.dll is not loaded for x86 versions of
  14. setup (since they have to run on Windows95 too), loads ntdll.dll
  15. dynamically (for alphas, ntdll.dll.is already loaded and LoadLibrary
  16. returns a handle to it). So this function should be called only after
  17. checking that the current system is NT
  18. Currently, this function is called only for NT5 upgrades
  19. Arguments:
  20. None
  21. Return Value:
  22. TRUE if the machine is a NT5 DC, FALSE otherwise
  23. --*/
  24. {
  25. NT_PRODUCT_TYPE Type = NtProductWinNt;
  26. HMODULE h;
  27. TCHAR DllName[MAX_PATH];
  28. VOID (*GetPrType)(NT_PRODUCT_TYPE *Typ);
  29. if (OsVersion.dwMajorVersion != 5) {
  30. // not NT5
  31. return FALSE;
  32. }
  33. // Load ntdll.dll from the system directory
  34. GetSystemDirectory(DllName, MAX_PATH);
  35. ConcatenatePaths(DllName, TEXT("ntdll.dll"), MAX_PATH);
  36. if (h = LoadLibrary(DllName)) {
  37. if((FARPROC) GetPrType = GetProcAddress(h, "RtlGetNtProductType")) {
  38. GetPrType(&Type);
  39. }
  40. FreeLibrary(h);
  41. }
  42. if ( Type == NtProductLanManNt ) {
  43. return TRUE;
  44. }
  45. else {
  46. return FALSE;
  47. }
  48. }
  49. BOOL
  50. ISDC()
  51. /*++
  52. Routine Descrtiption:
  53. Checks if a machine is a DC. Uses a call to RtlGetProductType that
  54. requires ntdll.dll. Since ntdll.dll is not loaded for x86 versions of
  55. setup (since they have to run on Windows95 too), loads ntdll.dll
  56. dynamically (for alphas, ntdll.dll.is already loaded and LoadLibrary
  57. returns a handle to it).
  58. Arguments:
  59. None
  60. Return Value:
  61. TRUE if the machine is a DC, FALSE otherwise
  62. --*/
  63. {
  64. NT_PRODUCT_TYPE Type = NtProductWinNt;
  65. HMODULE h;
  66. TCHAR DllName[MAX_PATH];
  67. VOID (*GetPrType)(NT_PRODUCT_TYPE *Typ);
  68. if (!ISNT()) {
  69. // not NT
  70. return FALSE;
  71. }
  72. // Load ntdll.dll from the system directory
  73. GetSystemDirectory(DllName, MAX_PATH);
  74. ConcatenatePaths(DllName, TEXT("ntdll.dll"), MAX_PATH);
  75. if (h = LoadLibrary(DllName)) {
  76. if((FARPROC) GetPrType = GetProcAddress(h, "RtlGetNtProductType")) {
  77. GetPrType(&Type);
  78. }
  79. FreeLibrary(h);
  80. }
  81. if ( Type == NtProductLanManNt ) {
  82. return TRUE;
  83. }
  84. else {
  85. return FALSE;
  86. }
  87. }
  88. int
  89. GetObjVersionInIniFile(
  90. IN TCHAR *IniFileName,
  91. OUT DWORD *Version
  92. )
  93. /*++
  94. Routine Decsription:
  95. Reads the Object-Version key in the SCHEMA section of the
  96. given ini file and returns the value in *Version. If the
  97. key cannot be read, 0 is returned in *Version
  98. Arguments:
  99. IniFileName - Pointer to null-terminated inifile name
  100. Version - Pointer to DWORD to return version in
  101. Return Value:
  102. 0
  103. --*/
  104. {
  105. TCHAR Buffer[32];
  106. BOOL fFound = FALSE;
  107. LPCTSTR SCHEMASECTION = TEXT("SCHEMA");
  108. LPCTSTR OBJECTVER = TEXT("objectVersion");
  109. LPCTSTR DEFAULT = TEXT("NOT_FOUND");
  110. *Version = 0;
  111. GetPrivateProfileString(
  112. SCHEMASECTION,
  113. OBJECTVER,
  114. DEFAULT,
  115. Buffer,
  116. sizeof(Buffer)/sizeof(TCHAR),
  117. IniFileName
  118. );
  119. if ( lstrcmpi(Buffer, DEFAULT) ) {
  120. // Not the default string, so got a value
  121. *Version = _ttoi(Buffer);
  122. fFound = TRUE;
  123. }
  124. return 0;
  125. }
  126. BOOL
  127. NtdsCheckSchemaVersion(
  128. IN TCHAR *IniFileName,
  129. OUT DWORD *DCVersion,
  130. OUT DWORD *IniVersion
  131. )
  132. /*++
  133. Routine Description:
  134. Reads a particular registry key, a key value from a given inifile
  135. and compares them.
  136. Arguments:
  137. IniFileName - Pointer to null-terminated inifile name to read key from
  138. DCVersion - Pointer to DWORD to return the registry key value in DC
  139. IniVersion - Pointer to DWORD to return the key value read from inifile
  140. Return:
  141. TRUE if the two values match, FALSE otherwise
  142. --*/
  143. {
  144. DWORD regVersion = 0, objVersion = 0;
  145. DWORD herr, err, dwType, dwSize;
  146. HKEY hk;
  147. // Read the "Schema Version" value from NTDS config section in registry
  148. // Value is assumed to be 0 if not found
  149. dwSize = sizeof(regVersion);
  150. if ( (herr = RegOpenKey(HKEY_LOCAL_MACHINE, DSA_CONFIG_SECTION, &hk)) ||
  151. (err = RegQueryValueEx(hk, SCHEMAVERSION, NULL, &dwType, (LPBYTE) &regVersion, &dwSize)) ) {
  152. // Error getting the key. We assume it is not there
  153. regVersion = 0;
  154. }
  155. if (!herr) RegCloseKey(hk);
  156. // Get key value in inifile
  157. GetObjVersionInIniFile( IniFileName, &objVersion );
  158. // Return the two values, and compare and return appropriate boolean
  159. *DCVersion = regVersion;
  160. *IniVersion = objVersion;
  161. if (regVersion != objVersion) {
  162. return FALSE;
  163. }
  164. return TRUE;
  165. }
  166. int
  167. MyCopyFile(
  168. IN HWND ParentWnd,
  169. IN TCHAR *FileName
  170. )
  171. /*++
  172. Routine Description:
  173. Copies the file specified by filename from the first source
  174. (NativeSourcePaths[0]). Files are copied to the system directory, except
  175. schema.ini, which is copied into windows directory since we do not
  176. want to overwrite the current schema.ini in the system directory of
  177. the DC
  178. Arguments:
  179. ParentWnd - Handle to parent window to raise appropriate error popups
  180. FileName - Pointer to null-terminated string containg name of file to copy
  181. Return Value:
  182. DSCHECK_ERR_FILE_NOT_FOUND if file is not found on source
  183. DSCHECK_ERR_FILE_COPY if error copying file
  184. DSCHECK_ERR_SUCCESS otherwise
  185. Also raises appropriate error popups too inform users
  186. --*/
  187. {
  188. TCHAR SourceName[MAX_PATH], ActualSourceName[MAX_PATH];
  189. TCHAR TargetName[MAX_PATH];
  190. #if 0
  191. TCHAR SourceA[MAX_PATH], TargetA[MAX_PATH];
  192. #endif
  193. HANDLE FindHandle;
  194. WIN32_FIND_DATA FindData;
  195. DWORD d = 0;
  196. int err = 0;
  197. // Create the source file name
  198. lstrcpy(SourceName, NativeSourcePaths[0]);
  199. // First check if the uncompressed file is there
  200. ConcatenatePaths(SourceName, FileName, MAX_PATH);
  201. FindHandle = FindFirstFile(SourceName, &FindData);
  202. if(FindHandle && (FindHandle != INVALID_HANDLE_VALUE)) {
  203. //
  204. // Got the file, copy name in ActualSourceName
  205. //
  206. FindClose(FindHandle);
  207. lstrcpy( ActualSourceName, SourceName);
  208. } else {
  209. //
  210. // Don't have the file, try the compressed file name
  211. //
  212. GenerateCompressedName(SourceName,ActualSourceName);
  213. FindHandle = FindFirstFile(ActualSourceName, &FindData);
  214. if(FindHandle && (FindHandle != INVALID_HANDLE_VALUE)) {
  215. // Got the file. Name is already in ActualSourceName
  216. FindClose(FindHandle);
  217. }
  218. else {
  219. ActualSourceName[0] = 0;
  220. }
  221. }
  222. if ( !ActualSourceName[0] ) {
  223. // file is not found. Error
  224. MessageBoxFromMessage(
  225. ParentWnd,
  226. MSG_DSCHECK_REQD_FILE_MISSING,
  227. FALSE,
  228. AppTitleStringId,
  229. MB_OK | MB_ICONWARNING | MB_TASKMODAL,
  230. FileName,
  231. NativeSourcePaths[0]
  232. );
  233. return DSCHECK_ERR_FILE_NOT_FOUND;
  234. }
  235. // Ok, the file is there. Copy it to system32, except if it is
  236. // schema.ini, in which case copy it to windows directory
  237. if (lstrcmpi(FileName, TEXT("schema.ini")) == 0) {
  238. MyGetWindowsDirectory(TargetName, MAX_PATH) ;
  239. }
  240. else {
  241. GetSystemDirectory(TargetName, MAX_PATH);
  242. }
  243. ConcatenatePaths(TargetName, FileName, MAX_PATH);
  244. // Delete any existing file of the same name
  245. DeleteFile(TargetName);
  246. #if 0
  247. // Ok, the names are ready. Create the ANSI versions in SourceA
  248. // and TargetA, since we will be calling the setupapi routine
  249. // SetupDecompressOrCopyFileA which is only Ansi
  250. #ifdef UNICODE
  251. d = WideCharToMultiByte(
  252. CP_ACP,
  253. 0,
  254. ActualSourceName,
  255. -1,
  256. (LPSTR) SourceA,
  257. MAX_PATH,
  258. NULL,
  259. NULL
  260. );
  261. if (d) {
  262. d = WideCharToMultiByte(
  263. CP_ACP,
  264. 0,
  265. TargetName,
  266. -1,
  267. (LPSTR) TargetA,
  268. MAX_PATH,
  269. NULL,
  270. NULL
  271. );
  272. }
  273. if (!d) {
  274. err = 1;
  275. }
  276. #else
  277. lstrcpy(SourceA, ActualSourceName);
  278. lstrcpy(TargetA, TargetName);
  279. #endif
  280. if (!err) {
  281. err = SetupapiDecompressOrCopyFile((LPCSTR) SourceA, (LPCSTR) TargetA, NULL);
  282. }
  283. #endif
  284. if (!err) {
  285. err = SetupapiDecompressOrCopyFile (ActualSourceName, TargetName, 0);
  286. }
  287. if (err) {
  288. // some error copying file. Raise message box
  289. MessageBoxFromMessage(
  290. ParentWnd,
  291. MSG_DSCHECK_COPY_ERROR,
  292. FALSE,
  293. AppTitleStringId,
  294. MB_OK | MB_ICONWARNING | MB_TASKMODAL,
  295. FileName,
  296. NativeSourcePaths[0]
  297. );
  298. return DSCHECK_ERR_FILE_COPY;
  299. }
  300. // successfully copied file
  301. return DSCHECK_ERR_SUCCESS;
  302. }
  303. int
  304. CheckSchemaVersionForNT5DCs(
  305. IN HWND ParentWnd
  306. )
  307. /*++
  308. Routine Description:
  309. Main routine called from Options wizard page to initiate schema version
  310. check
  311. Arguments:
  312. ParentWnd - Handle to parent window to raise errors
  313. Return Value:
  314. DSCHECK_ERR_FILE_NOT_FOUND if a required file is not found
  315. DSCHECK_ERR_FILE_COPY if error copying a required file
  316. DSCHECK_ERR_SCHEMA_MISMATCH if schema versions do not match
  317. DSCHECK_ERR_SUCCESS otherwise
  318. Also pops up appropriate error windows on DSCHECK_ERR_SCHEMA_MISMATCH.
  319. Error Windows for the other errors are opened by downlevel routines
  320. --*/
  321. {
  322. TCHAR FileName[MAX_PATH];
  323. TCHAR IniFilePath[MAX_PATH];
  324. TCHAR IniVerStr[32], RegVerStr[32], TempStr[32];
  325. DWORD RegVer, IniVer, i;
  326. int err;
  327. int err1=0;
  328. if (!IsNT5DC()) {
  329. // Not an NT5 DC, nothing to do
  330. return DSCHECK_ERR_SUCCESS;
  331. }
  332. // copy the schema.ini to the local windows directory
  333. lstrcpy(FileName, TEXT("schema.ini"));
  334. err = MyCopyFile(ParentWnd, FileName);
  335. if (err) {
  336. // return DSCHECK error returned by MyCopyFile
  337. return err;
  338. }
  339. // The schema.ini is now copied to windows directory.
  340. // Do schema version check
  341. MyGetWindowsDirectory(IniFilePath, MAX_PATH);
  342. ConcatenatePaths(IniFilePath, TEXT("schema.ini"), MAX_PATH);
  343. if ( NtdsCheckSchemaVersion( IniFilePath, &RegVer, &IniVer) ) {
  344. // The schema versions match. Nothing to do
  345. return DSCHECK_ERR_SUCCESS;
  346. }
  347. // We are here means schema versions do not match.
  348. // Copy all necesary files for schema upgrades.
  349. _itot(IniVer, IniVerStr, 10);
  350. _itot(RegVer, RegVerStr, 10);
  351. if ( (RegVer < 10) && (IniVer >= 10) ) {
  352. // Trying to upgrade from before B3-RC1 to B3-RC1 or above.
  353. // B3-RC1 or above requires a clean install due to incompatible
  354. // DS checkins. No upgrades are possible. Pop up the clean install
  355. // message and leave
  356. MessageBoxFromMessage(
  357. ParentWnd,
  358. MSG_DSCHECK_SCHEMA_CLEAN_INSTALL_NEEDED,
  359. FALSE,
  360. AppTitleStringId,
  361. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  362. RegVerStr,
  363. IniVerStr
  364. );
  365. return DSCHECK_ERR_VERSION_MISMATCH;
  366. }
  367. if (RegVer == 16) {
  368. // trying to upgrade a machine in an enterprise with schema
  369. // version of 16 (Whistler-Beta1)
  370. // possibly, there are beta1 machines lying around
  371. // so we have to tell them to demote them before they continue
  372. i = MessageBoxFromMessage(
  373. ParentWnd,
  374. MSG_DSCHECK_SCHEMA_WHISTLER_BETA1_DETECTED,
  375. FALSE,
  376. AppTitleStringId,
  377. MB_OKCANCEL | MB_ICONWARNING | MB_TASKMODAL,
  378. NULL
  379. );
  380. if(i == IDCANCEL) {
  381. return DSCHECK_ERR_VERSION_MISMATCH;
  382. }
  383. }
  384. if ( RegVer > IniVer ) {
  385. // The schema version in the enterprise is already greater than
  386. // the schema version of the build you are trying to upgrade to.
  387. //
  388. // This is okay. Imagine upgrading a 5.0 DC to the next service
  389. // pack even though the 5.0 DC is in a domain of mixed 5.0 and
  390. // 5.1 DCs. The schema version for the 5.0 DC is the same as the
  391. // schema version for the 5.1 DCs because schema upgrades replicate
  392. // to all DCs in an enterprise. There is no reason to disallow
  393. // upgrading the 5.0 DC to the next service pack even though
  394. // the schema version of the service pack (IniVer) is less than
  395. // the schema version of the 5.0 DC (RegVer).
  396. return DSCHECK_ERR_SUCCESS;
  397. }
  398. // Else, upgrade is possible, so copy all necessary files
  399. // copy all files from version on DC to latest version
  400. for ( i=RegVer+1; i<=IniVer; i++ ) {
  401. _itot(i, TempStr, 10);
  402. lstrcpy(FileName, TEXT("sch"));
  403. lstrcat(FileName, TempStr);
  404. lstrcat(FileName, TEXT(".ldf"));
  405. err1 = MyCopyFile(ParentWnd, FileName);
  406. if (err1 != DSCHECK_ERR_SUCCESS) {
  407. // error copying file. Return if file not found,
  408. // else just break out of loop.
  409. if (err1 == DSCHECK_ERR_FILE_NOT_FOUND) {
  410. return err1;
  411. }
  412. else {
  413. break;
  414. }
  415. }
  416. }
  417. if (err1) {
  418. // error copying at least one file, and not a file not
  419. // found error. raise appropriate message and return
  420. MessageBoxFromMessage(
  421. ParentWnd,
  422. MSG_DSCHECK_SCHEMA_UPGRADE_COPY_ERROR,
  423. FALSE,
  424. AppTitleStringId,
  425. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  426. RegVerStr,
  427. IniVerStr
  428. );
  429. return DSCHECK_ERR_FILE_COPY;
  430. }
  431. // Files copied successfully
  432. MessageBoxFromMessage(
  433. ParentWnd,
  434. MSG_DSCHECK_SCHEMA_UPGRADE_NEEDED,
  435. FALSE,
  436. AppTitleStringId,
  437. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  438. RegVerStr,
  439. IniVerStr
  440. );
  441. return DSCHECK_ERR_VERSION_MISMATCH;
  442. }