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.

1428 lines
44 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. termutil.c
  5. Abstract:
  6. Terminal server support functions and inifile syncing/merging code
  7. Author:
  8. Revision History:
  9. --*/
  10. #include "basedll.h"
  11. #include "regapi.h"
  12. #define TERMSRV_INIFILE_TIMES L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\IniFile Times"
  13. BOOL IsTerminalServerCompatible(VOID)
  14. {
  15. PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress );
  16. if ((NtHeader) && (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)) {
  17. return TRUE;
  18. } else {
  19. return FALSE;
  20. }
  21. }
  22. BOOL IsTSAppCompatEnabled(VOID)
  23. {
  24. NTSTATUS NtStatus;
  25. OBJECT_ATTRIBUTES ObjectAttributes;
  26. UNICODE_STRING UniString;
  27. HKEY hKey = 0;
  28. ULONG ul, ulcbuf;
  29. PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL;
  30. BOOL retval = TRUE;
  31. RtlInitUnicodeString(&UniString,REG_NTAPI_CONTROL_TSERVER);
  32. // Determine the value info buffer size
  33. ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) +
  34. sizeof(ULONG);
  35. pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
  36. 0,
  37. ulcbuf);
  38. // Did everything initialize OK?
  39. if (UniString.Buffer && pKeyValInfo) {
  40. InitializeObjectAttributes(&ObjectAttributes,
  41. &UniString,
  42. OBJ_CASE_INSENSITIVE,
  43. NULL,
  44. NULL
  45. );
  46. NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
  47. if (NT_SUCCESS(NtStatus)) {
  48. RtlInitUnicodeString(&UniString,
  49. L"TSAppCompat");
  50. NtStatus = NtQueryValueKey(hKey,
  51. &UniString,
  52. KeyValuePartialInformation,
  53. pKeyValInfo,
  54. ulcbuf,
  55. &ul);
  56. if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) {
  57. if ((*(PULONG)pKeyValInfo->Data) == 0) {
  58. retval = FALSE;
  59. }
  60. }
  61. NtClose(hKey);
  62. }
  63. }
  64. // Free up the buffers we allocated
  65. // Need to zero out the buffers, because some apps (MS Internet Assistant)
  66. // won't install if the heap is not zero filled.
  67. if (pKeyValInfo) {
  68. memset(pKeyValInfo, 0, ulcbuf);
  69. RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo );
  70. }
  71. return(retval);
  72. }
  73. BOOL IsSystemLUID(VOID)
  74. {
  75. HANDLE TokenHandle;
  76. UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ];
  77. ULONG ReturnLength;
  78. LUID CurrentLUID = { 0, 0 };
  79. LUID SystemLUID = SYSTEM_LUID;
  80. NTSTATUS Status;
  81. if ( CurrentLUID.LowPart == 0 && CurrentLUID.HighPart == 0 ) {
  82. Status = NtOpenProcessToken( NtCurrentProcess(),
  83. TOKEN_QUERY,
  84. &TokenHandle );
  85. if ( !NT_SUCCESS( Status ) )
  86. return(TRUE);
  87. NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation,
  88. sizeof(TokenInformation), &ReturnLength );
  89. NtClose( TokenHandle );
  90. RtlCopyLuid(&CurrentLUID,
  91. &(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId));
  92. }
  93. if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) {
  94. return(TRUE);
  95. } else {
  96. return(FALSE );
  97. }
  98. }
  99. void InitializeTermsrvFpns(void)
  100. {
  101. HANDLE dllHandle;
  102. if (IsTerminalServerCompatible() ||
  103. (IsSystemLUID()) ||
  104. (!IsTSAppCompatEnabled())) {
  105. return;
  106. }
  107. //
  108. // Load Terminal Server application compatibility dll
  109. //
  110. dllHandle = LoadLibraryW(L"tsappcmp.dll");
  111. if (dllHandle) {
  112. gpTermsrvFormatObjectName =
  113. (PTERMSRVFORMATOBJECTNAME)GetProcAddress(dllHandle,"TermsrvFormatObjectName");
  114. gpTermsrvGetComputerName =
  115. (PTERMSRVGETCOMPUTERNAME)GetProcAddress(dllHandle,"TermsrvGetComputerName");
  116. gpTermsrvAdjustPhyMemLimits =
  117. (PTERMSRVADJUSTPHYMEMLIMITS)GetProcAddress(dllHandle,"TermsrvAdjustPhyMemLimits");
  118. gpTermsrvGetWindowsDirectoryA =
  119. (PTERMSRVGETWINDOWSDIRECTORYA)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryA");
  120. gpTermsrvGetWindowsDirectoryW =
  121. (PTERMSRVGETWINDOWSDIRECTORYW)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryW");
  122. gpTermsrvConvertSysRootToUserDir =
  123. (PTERMSRVCONVERTSYSROOTTOUSERDIR)GetProcAddress(dllHandle,"TermsrvConvertSysRootToUserDir");
  124. gpTermsrvBuildIniFileName =
  125. (PTERMSRVBUILDINIFILENAME)GetProcAddress(dllHandle,"TermsrvBuildIniFileName");
  126. gpTermsrvCORIniFile =
  127. (PTERMSRVCORINIFILE)GetProcAddress(dllHandle,"TermsrvCORIniFile");
  128. gpGetTermsrCompatFlags =
  129. (PGETTERMSRCOMPATFLAGS)GetProcAddress(dllHandle,"GetTermsrCompatFlags");
  130. gpTermsrvBuildSysIniPath =
  131. (PTERMSRVBUILDSYSINIPATH)GetProcAddress(dllHandle,"TermsrvBuildSysIniPath");
  132. gpTermsrvCopyIniFile =
  133. (PTERMSRVCOPYINIFILE)GetProcAddress(dllHandle,"TermsrvCopyIniFile");
  134. gpTermsrvGetString =
  135. (PTERMSRVGETSTRING)GetProcAddress(dllHandle,"TermsrvGetString");
  136. gpTermsrvLogInstallIniFile =
  137. (PTERMSRVLOGINSTALLINIFILE)GetProcAddress(dllHandle,"TermsrvLogInstallIniFile");
  138. }
  139. }
  140. /*****************************************************************************
  141. *
  142. * TermsrvAppInstallMode
  143. *
  144. * Returns whether the system is in Install mode or not
  145. *
  146. * ENTRY:
  147. * Param1 (input/output)
  148. * Comments
  149. *
  150. * EXIT:
  151. * STATUS_SUCCESS - no error
  152. *
  153. ****************************************************************************/
  154. WINBASEAPI
  155. BOOL
  156. WINAPI
  157. TermsrvAppInstallMode( VOID )
  158. {
  159. if ( BaseStaticServerData->fTermsrvAppInstallMode )
  160. return( TRUE );
  161. return( FALSE );
  162. }
  163. /*****************************************************************************
  164. *
  165. * SetTermsrvAppInstallMode
  166. *
  167. * Turns App install mode on or off. Default is off
  168. *
  169. * ENTRY:
  170. * Param1 (input/output)
  171. * Comments
  172. *
  173. * EXIT:
  174. * STATUS_SUCCESS - no error
  175. *
  176. ****************************************************************************/
  177. WINBASEAPI
  178. BOOL
  179. WINAPI
  180. SetTermsrvAppInstallMode( BOOL bState )
  181. {
  182. typedef BOOL ( APIENTRY Func_CheckTokenMembership )( HANDLE , PSID , PBOOL);
  183. BOOL rc;
  184. NTSTATUS Status;
  185. PSID pSid = NULL ;
  186. SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
  187. HINSTANCE dllHandle;
  188. if ( !( IsTerminalServer() && IsTSAppCompatEnabled() ) )
  189. {
  190. return FALSE;
  191. }
  192. dllHandle = LoadLibraryW(L"advapi32.dll");
  193. if (dllHandle)
  194. {
  195. Func_CheckTokenMembership *fPtr;
  196. fPtr = (Func_CheckTokenMembership * )GetProcAddress(dllHandle,"CheckTokenMembership");
  197. if (fPtr)
  198. {
  199. Status = RtlAllocateAndInitializeSid(
  200. &SidAuthority,
  201. 2,
  202. SECURITY_BUILTIN_DOMAIN_RID,
  203. DOMAIN_ALIAS_RID_ADMINS,
  204. 0, 0, 0, 0, 0, 0,
  205. &pSid
  206. );
  207. if (NT_SUCCESS(Status))
  208. {
  209. BOOL FoundAdmin;
  210. if ( fPtr (NULL, pSid , &FoundAdmin))
  211. {
  212. if (FoundAdmin)
  213. {
  214. // caller is admin, go ahead
  215. #if defined(BUILD_WOW6432)
  216. Status = CsrBasepSetTermsrvAppInstallMode(bState);
  217. #else
  218. BASE_API_MSG m;
  219. PBASE_SET_TERMSRVAPPINSTALLMODE c= (PBASE_SET_TERMSRVAPPINSTALLMODE)&m.u.SetTermsrvAppInstallMode;
  220. c->bState = bState;
  221. Status = CsrClientCallServer((PCSR_API_MSG)&m, NULL,
  222. CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
  223. BasepSetTermsrvAppInstallMode),
  224. sizeof( *c ));
  225. #endif
  226. if ( NT_SUCCESS( Status ) )
  227. {
  228. //
  229. // Load tsappcmp.dll
  230. //
  231. if (gpTermsrvUpdateAllUserMenu == NULL)
  232. {
  233. HANDLE dllHandle;
  234. //
  235. // Load Terminal Server application compatibility dll
  236. //
  237. dllHandle = LoadLibraryW(L"tsappcmp.dll");
  238. if (dllHandle)
  239. {
  240. gpTermsrvUpdateAllUserMenu =
  241. (PTERMSRVUPDATEALLUSERMENU)GetProcAddress(dllHandle,"TermsrvUpdateAllUserMenu");
  242. }
  243. }
  244. if (gpTermsrvUpdateAllUserMenu)
  245. {
  246. gpTermsrvUpdateAllUserMenu( bState == TRUE ? 0 : 1 );
  247. }
  248. rc = TRUE;
  249. }
  250. else
  251. {
  252. SetLastError( RtlNtStatusToDosError( Status ) );
  253. rc = FALSE;
  254. }
  255. }
  256. else
  257. {
  258. // user is not admin
  259. SetLastError( ERROR_ACCESS_DENIED );
  260. rc = FALSE;
  261. }
  262. }
  263. else
  264. {
  265. // call to CheckTokenMembership() failed, it set the last error
  266. rc = FALSE;
  267. }
  268. }
  269. else
  270. {
  271. // attempt to allocate and init SID failed.
  272. SetLastError( RtlNtStatusToDosError( Status ) );
  273. rc = FALSE;
  274. }
  275. }
  276. else
  277. {
  278. // function not found, GetProc() set the last error.
  279. rc = FALSE;
  280. }
  281. FreeLibrary( dllHandle );
  282. }
  283. else
  284. {
  285. // library not found, LoadLib() set the last error
  286. rc = FALSE;
  287. }
  288. if (pSid)
  289. {
  290. RtlFreeSid( pSid );
  291. }
  292. return rc;
  293. }
  294. /////////////////////////////////////////////////////////////////////////////
  295. /////////////////////////////////////////////////////////////////////////////
  296. /*
  297. Ini File syncing/merging code
  298. */
  299. //////////////////////////////////////////////////////////////////////////////
  300. /* External Functions */
  301. NTSTATUS
  302. BaseDllOpenIniFileOnDisk(
  303. PINIFILE_PARAMETERS a
  304. );
  305. NTSTATUS
  306. BaseDllWriteKeywordValue(
  307. IN PINIFILE_PARAMETERS a,
  308. IN PUNICODE_STRING VariableName OPTIONAL
  309. );
  310. NTSTATUS
  311. BaseDllCloseIniFileOnDisk(
  312. IN PINIFILE_PARAMETERS a
  313. );
  314. NTSTATUS
  315. BaseDllFindSection(
  316. IN PINIFILE_PARAMETERS a
  317. );
  318. NTSTATUS
  319. BaseDllFindKeyword(
  320. IN PINIFILE_PARAMETERS a
  321. );
  322. NTSTATUS
  323. TermsrvIniSyncLoop( HANDLE SrcHandle,
  324. PINIFILE_PARAMETERS a,
  325. PBOOLEAN pfIniUpdated
  326. );
  327. NTSTATUS
  328. TermsrvGetSyncTime( PUNICODE_STRING pSysIniPath,
  329. PUNICODE_STRING pUserBasePath,
  330. PLARGE_INTEGER pLastSyncTime
  331. );
  332. NTSTATUS
  333. TermsrvPutSyncTime( PUNICODE_STRING pSysIniPath,
  334. PUNICODE_STRING pUserBasePath,
  335. PLARGE_INTEGER pLastSyncTime
  336. );
  337. /*****************************************************************************
  338. *
  339. * TermsrvGetSyncTime
  340. *
  341. * This routine will get the time of the system ini file that the user ini
  342. * file was last sync'd with.
  343. *
  344. * ENTRY:
  345. * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
  346. * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
  347. * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
  348. *
  349. * EXIT:
  350. * STATUS_SUCCESS - successfully retrieved the last sync time from infile.upd
  351. *
  352. ****************************************************************************/
  353. NTSTATUS
  354. TermsrvGetSyncTime(
  355. PUNICODE_STRING pSysIniPath,
  356. PUNICODE_STRING pUserBasePath,
  357. PLARGE_INTEGER pLastSyncTime)
  358. {
  359. NTSTATUS Status;
  360. HANDLE hUpdate = NULL;
  361. OBJECT_ATTRIBUTES ObjAUpd;
  362. IO_STATUS_BLOCK Iosb;
  363. FILE_STANDARD_INFORMATION StandardInfo;
  364. WCHAR wcUpdateFile[MAX_PATH+1];
  365. UNICODE_STRING UniUpdateName = {0,
  366. sizeof(wcUpdateFile),
  367. wcUpdateFile};
  368. PCHAR pBuff = NULL, pBuffEnd;
  369. PWCH pwch;
  370. SIZE_T ulBuffSize;
  371. LONG lresult;
  372. if (!pSysIniPath) {
  373. return STATUS_INVALID_PARAMETER_1;
  374. }
  375. if (!pUserBasePath) {
  376. return STATUS_INVALID_PARAMETER_2;
  377. }
  378. if (!pLastSyncTime) {
  379. return STATUS_INVALID_PARAMETER_3;
  380. }
  381. Status = RtlAppendUnicodeStringToString(&UniUpdateName,
  382. pUserBasePath);
  383. if (NT_SUCCESS(Status)) {
  384. Status = RtlAppendUnicodeToString(&UniUpdateName,
  385. L"\\inifile.upd");
  386. }
  387. if (! NT_SUCCESS(Status)) {
  388. return Status;
  389. }
  390. pLastSyncTime->LowPart = 0;
  391. pLastSyncTime->HighPart = 0;
  392. InitializeObjectAttributes( &ObjAUpd,
  393. &UniUpdateName,
  394. OBJ_CASE_INSENSITIVE,
  395. NULL,
  396. NULL
  397. );
  398. // Open the update log
  399. Iosb.Status = STATUS_SUCCESS;
  400. Status = NtOpenFile( &hUpdate,
  401. FILE_GENERIC_READ,
  402. &ObjAUpd,
  403. &Iosb,
  404. FILE_SHARE_READ|FILE_SHARE_WRITE,
  405. FILE_SYNCHRONOUS_IO_NONALERT // OpenOptions
  406. );
  407. // Get the size of the file
  408. if (NT_SUCCESS( Status )) {
  409. Status = NtQueryInformationFile( hUpdate,
  410. &Iosb,
  411. &StandardInfo,
  412. sizeof(StandardInfo),
  413. FileStandardInformation
  414. );
  415. if (Status == STATUS_BUFFER_OVERFLOW) {
  416. Status = STATUS_SUCCESS;
  417. }
  418. #if DBG
  419. else if (!NT_SUCCESS( Status )) {
  420. DbgPrint( "TermsrvGetSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status );
  421. }
  422. #endif
  423. }
  424. if (NT_SUCCESS( Status )) {
  425. ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR);
  426. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  427. &pBuff,
  428. 0,
  429. &ulBuffSize,
  430. MEM_RESERVE,
  431. PAGE_READWRITE
  432. );
  433. }
  434. if (NT_SUCCESS( Status )) {
  435. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  436. &pBuff,
  437. 0,
  438. &ulBuffSize,
  439. MEM_COMMIT,
  440. PAGE_READWRITE
  441. );
  442. }
  443. if (NT_SUCCESS( Status )) {
  444. Status = NtReadFile( hUpdate,
  445. NULL,
  446. NULL,
  447. NULL,
  448. &Iosb,
  449. pBuff,
  450. StandardInfo.EndOfFile.LowPart,
  451. NULL,
  452. NULL
  453. );
  454. if ( Status == STATUS_PENDING ) {
  455. Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
  456. }
  457. if ( NT_SUCCESS(Status) ) {
  458. // Get final I/O status
  459. Status = Iosb.Status;
  460. }
  461. }
  462. // Look for this ini file in the list
  463. if (NT_SUCCESS(Status)) {
  464. pwch = (PWCHAR)pBuff;
  465. pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart;
  466. // Look for the file in the sorted list
  467. while ((pwch < (PWCHAR)pBuffEnd) &&
  468. ((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) {
  469. pwch += wcslen(pwch) + sizeof(LARGE_INTEGER)/sizeof(WCHAR) + 1;
  470. }
  471. if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) {
  472. pwch += wcslen(pwch) + 1;
  473. pLastSyncTime->LowPart = ((PLARGE_INTEGER)pwch)->LowPart;
  474. pLastSyncTime->HighPart = ((PLARGE_INTEGER)pwch)->HighPart;
  475. }
  476. }
  477. if (NT_SUCCESS(Status) ) {
  478. // Get final I/O status
  479. Status = Iosb.Status;
  480. }
  481. if (pBuff) {
  482. NtFreeVirtualMemory( NtCurrentProcess(),
  483. &pBuff,
  484. &ulBuffSize,
  485. MEM_RELEASE
  486. );
  487. }
  488. if (hUpdate) {
  489. Status = NtClose( hUpdate );
  490. }
  491. return(Status);
  492. }
  493. /*****************************************************************************
  494. *
  495. * TermsrvPutSyncTime
  496. *
  497. * This routine will write the time of the system ini file that the user ini
  498. * file was last sync'd with.
  499. *
  500. * ENTRY:
  501. * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
  502. * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
  503. * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
  504. *
  505. * EXIT:
  506. * STATUS_SUCCESS - successfully stored the last sync time in infile.upd
  507. *
  508. ****************************************************************************/
  509. NTSTATUS
  510. TermsrvPutSyncTime(
  511. PUNICODE_STRING pSysIniPath,
  512. PUNICODE_STRING pUserBasePath,
  513. PLARGE_INTEGER pLastSyncTime)
  514. {
  515. NTSTATUS Status;
  516. HANDLE hUpdate = NULL;
  517. OBJECT_ATTRIBUTES ObjAUpd;
  518. IO_STATUS_BLOCK Iosb;
  519. FILE_STANDARD_INFORMATION StandardInfo;
  520. WCHAR wcUpdateFile[MAX_PATH+1];
  521. UNICODE_STRING UniUpdateName = {0,
  522. sizeof(wcUpdateFile),
  523. wcUpdateFile};
  524. PCHAR pBuff = NULL, pBuffEnd;
  525. PWCH pwch;
  526. SIZE_T ulBuffSize;
  527. ULONG ulLength;
  528. SIZE_T ulRegionSize;
  529. LONG lresult;
  530. LARGE_INTEGER FileLength;
  531. FILE_POSITION_INFORMATION CurrentPos;
  532. if (!pSysIniPath) {
  533. return STATUS_INVALID_PARAMETER_1;
  534. }
  535. if (!pUserBasePath) {
  536. return STATUS_INVALID_PARAMETER_2;
  537. }
  538. if (!pLastSyncTime) {
  539. return STATUS_INVALID_PARAMETER_3;
  540. }
  541. Status = RtlAppendUnicodeStringToString(&UniUpdateName,
  542. pUserBasePath);
  543. if (NT_SUCCESS(Status)) {
  544. Status = RtlAppendUnicodeToString(&UniUpdateName,
  545. L"\\inifile.upd");
  546. }
  547. if (! NT_SUCCESS(Status)) {
  548. return Status;
  549. }
  550. InitializeObjectAttributes( &ObjAUpd,
  551. &UniUpdateName,
  552. OBJ_CASE_INSENSITIVE,
  553. NULL,
  554. NULL
  555. );
  556. // Open the update log
  557. Iosb.Status = STATUS_SUCCESS;
  558. Status = NtCreateFile( &hUpdate,
  559. FILE_READ_DATA | FILE_WRITE_DATA |
  560. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  561. &ObjAUpd,
  562. &Iosb,
  563. NULL, // Allocation size
  564. FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
  565. FILE_SHARE_WRITE, // dwShareMode
  566. FILE_OPEN_IF, // CreateDisposition
  567. FILE_SYNCHRONOUS_IO_NONALERT |
  568. FILE_NON_DIRECTORY_FILE, // CreateFlags
  569. NULL, // EaBuffer
  570. 0 // EaLength
  571. );
  572. if (NT_SUCCESS( Status )) {
  573. Status = NtQueryInformationFile( hUpdate,
  574. &Iosb,
  575. &StandardInfo,
  576. sizeof(StandardInfo),
  577. FileStandardInformation
  578. );
  579. if (Status == STATUS_BUFFER_OVERFLOW) {
  580. Status = STATUS_SUCCESS;
  581. }
  582. #if DBG
  583. else if (!NT_SUCCESS( Status )) {
  584. DbgPrint( "TermsrvPutLastSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status );
  585. }
  586. #endif
  587. }
  588. if (NT_SUCCESS( Status )) {
  589. ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR);
  590. ulRegionSize = ulBuffSize + 0x1000; // Room for 4K of growth
  591. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  592. &pBuff,
  593. 0,
  594. &ulRegionSize,
  595. MEM_RESERVE,
  596. PAGE_READWRITE
  597. );
  598. }
  599. if (NT_SUCCESS( Status )) {
  600. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  601. &pBuff,
  602. 0,
  603. &ulBuffSize,
  604. MEM_COMMIT,
  605. PAGE_READWRITE
  606. );
  607. }
  608. if (NT_SUCCESS( Status ) && StandardInfo.EndOfFile.LowPart) {
  609. Status = NtReadFile( hUpdate,
  610. NULL,
  611. NULL,
  612. NULL,
  613. &Iosb,
  614. pBuff,
  615. StandardInfo.EndOfFile.LowPart,
  616. NULL,
  617. NULL
  618. );
  619. if ( Status == STATUS_PENDING ) {
  620. Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
  621. }
  622. if ( NT_SUCCESS(Status) ) {
  623. // Get final I/O status
  624. Status = Iosb.Status;
  625. }
  626. }
  627. // Look for this ini file in the list
  628. if (NT_SUCCESS(Status)) {
  629. pwch = (PWCHAR)pBuff;
  630. pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart;
  631. // Look for the file in the list
  632. while ((pwch < (PWCHAR)pBuffEnd) &&
  633. ((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) {
  634. pwch += wcslen(pwch) + (sizeof(LARGE_INTEGER)/sizeof(WCHAR)) + 1;
  635. }
  636. // If the ini file is already in the file, just update the time
  637. if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) {
  638. pwch += wcslen(pwch) + 1;
  639. ((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart;
  640. ((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart;
  641. } else { // Ini file not in list
  642. // Figure out the size to grow the file
  643. ulLength = (pSysIniPath->Length + 2) + sizeof(LARGE_INTEGER);
  644. ulBuffSize += ulLength;
  645. // Grow the memory region
  646. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  647. &pBuff,
  648. 0,
  649. &ulBuffSize,
  650. MEM_COMMIT,
  651. PAGE_READWRITE
  652. );
  653. if (NT_SUCCESS(Status)) {
  654. // figure out where the entry goes in the file
  655. if (pwch < (PWCHAR)pBuffEnd) {
  656. RtlMoveMemory( pwch+(ulLength/sizeof(WCHAR)),
  657. pwch,
  658. pBuffEnd - (PCHAR)pwch
  659. );
  660. }
  661. pBuffEnd += ulLength;
  662. wcscpy(pwch, pSysIniPath->Buffer);
  663. pwch += (pSysIniPath->Length + 2)/sizeof(WCHAR);
  664. ((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart;
  665. ((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart;
  666. }
  667. }
  668. }
  669. if (NT_SUCCESS(Status)) {
  670. CurrentPos.CurrentByteOffset.LowPart = 0;
  671. CurrentPos.CurrentByteOffset.HighPart = 0;
  672. Status = NtSetInformationFile( hUpdate,
  673. &Iosb,
  674. &CurrentPos,
  675. sizeof(CurrentPos),
  676. FilePositionInformation
  677. );
  678. Status = NtWriteFile( hUpdate,
  679. NULL,
  680. NULL,
  681. NULL,
  682. &Iosb,
  683. pBuff,
  684. (ULONG)(pBuffEnd - pBuff + 1),
  685. NULL,
  686. NULL
  687. );
  688. if( Status == STATUS_PENDING ) {
  689. Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
  690. }
  691. if( NT_SUCCESS(Status) ) {
  692. // Get final I/O status
  693. Status = Iosb.Status;
  694. }
  695. }
  696. if (NT_SUCCESS( Status )) {
  697. FileLength.LowPart = (ULONG)(pBuffEnd - pBuff);
  698. FileLength.HighPart = 0;
  699. Status = NtSetInformationFile( hUpdate,
  700. &Iosb,
  701. &FileLength,
  702. sizeof( FileLength ),
  703. FileEndOfFileInformation
  704. );
  705. }
  706. if (pBuff) {
  707. NtFreeVirtualMemory( NtCurrentProcess(),
  708. &pBuff,
  709. &ulRegionSize,
  710. MEM_RELEASE
  711. );
  712. }
  713. if (hUpdate) {
  714. Status = NtClose( hUpdate );
  715. }
  716. return(Status);
  717. }
  718. /*****************************************************************************
  719. *
  720. * TermsrvCheckIniSync
  721. *
  722. * This routine will get the time of the system ini file that the user ini
  723. * file was last sync'd with.
  724. *
  725. * ENTRY:
  726. * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
  727. * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
  728. * BOOLEAN fGet (In) - TRUE means to get last sync time, FALSE means to write it
  729. * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
  730. *
  731. * EXIT:
  732. * TRUE - User ini file should be sync'd
  733. * FALSE - User ini file should be sync'd
  734. *
  735. ****************************************************************************/
  736. BOOLEAN
  737. TermsrvCheckIniSync(
  738. PUNICODE_STRING pSysIniPath,
  739. PUNICODE_STRING pUserBasePath)
  740. {
  741. LARGE_INTEGER LastSyncTime;
  742. OBJECT_ATTRIBUTES objaIni;
  743. FILE_NETWORK_OPEN_INFORMATION BasicInfo;
  744. NTSTATUS Status;
  745. // Get the last sync time of the ini file from the inifile.upd file
  746. TermsrvGetSyncTime(pSysIniPath, pUserBasePath, &LastSyncTime);
  747. // Get the last write time of the system ini file
  748. InitializeObjectAttributes(
  749. &objaIni,
  750. pSysIniPath,
  751. OBJ_CASE_INSENSITIVE,
  752. NULL,
  753. NULL
  754. );
  755. // Now query it
  756. Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo );
  757. // If we couldn't get the time or the system ini file has been updated
  758. // since we last sync'd, return TRUE
  759. if (!NT_SUCCESS(Status) ||
  760. ((BasicInfo.LastWriteTime.HighPart > LastSyncTime.HighPart) ||
  761. ((BasicInfo.LastWriteTime.HighPart == LastSyncTime.HighPart) &&
  762. (BasicInfo.LastWriteTime.LowPart > LastSyncTime.LowPart)))) {
  763. return(TRUE);
  764. }
  765. return(FALSE);
  766. }
  767. /*****************************************************************************
  768. *
  769. * TermsrvDoesFileExist
  770. *
  771. * Returns whether the file exists or not.
  772. *
  773. * Must use NT, not WIN32 pathnames.
  774. *
  775. * ENTRY:
  776. * Param1 (input/output)
  777. * Comments
  778. *
  779. * EXIT:
  780. * STATUS_SUCCESS - no error
  781. *
  782. ****************************************************************************/
  783. BOOL
  784. TermsrvDoesFileExist(
  785. PUNICODE_STRING pFileName
  786. )
  787. {
  788. NTSTATUS Status;
  789. FILE_BASIC_INFORMATION BasicInfo;
  790. OBJECT_ATTRIBUTES Obja;
  791. InitializeObjectAttributes(
  792. &Obja,
  793. pFileName,
  794. OBJ_CASE_INSENSITIVE,
  795. NULL,
  796. NULL
  797. );
  798. /*
  799. * Now query it
  800. */
  801. Status = NtQueryAttributesFile( &Obja, &BasicInfo );
  802. if( NT_SUCCESS( Status ) ) {
  803. return( TRUE );
  804. }
  805. return( FALSE );
  806. }
  807. /*****************************************************************************
  808. *
  809. * TermsrvSyncUserIniFile
  810. *
  811. * This routine will check that the user's ini file is "sync'd" with the
  812. * system version of the ini file. This means that it walks through the
  813. * system ini file and checks that there is a corresponding entry in the
  814. * user's ini file.
  815. *
  816. * ENTRY:
  817. * IN PINIFILE_PARAMETERS a - ptr to inifile structure
  818. *
  819. * EXIT:
  820. * True - Ini file updated
  821. * False - User Ini file was unchanged
  822. *
  823. ****************************************************************************/
  824. BOOL TermsrvSyncUserIniFile(PINIFILE_PARAMETERS a)
  825. {
  826. WCHAR wcIniPath[MAX_PATH+1];
  827. UNICODE_STRING IniFilePath = {MAX_PATH,
  828. MAX_PATH+1,
  829. wcIniPath};
  830. PWCH pwch, pwcIniName;
  831. UNICODE_STRING UniSysPath;
  832. UNICODE_STRING UserBasePath;
  833. NTSTATUS Status;
  834. HANDLE SrcHandle;
  835. ULONG ulCompatFlags;
  836. OBJECT_ATTRIBUTES SrcObja;
  837. IO_STATUS_BLOCK SrcIosb;
  838. INIFILE_OPERATION OrigOperation;
  839. BOOLEAN OrigWrite, OrigMultiValue, OrigUnicode,
  840. OrigWriteOperation, fIniUpdated = FALSE;
  841. ANSI_STRING OrigAppName, OrigVarName;
  842. ULONG OrigResultChars, OrigResultMaxChars;
  843. LPSTR OrigResultBuffer;
  844. OBJECT_ATTRIBUTES objaIni;
  845. FILE_NETWORK_OPEN_INFORMATION BasicInfo;
  846. // If INI file mapping is not on, return
  847. if (IsSystemLUID() || TermsrvAppInstallMode()) {
  848. return(FALSE);
  849. }
  850. // Build full system path to the Ini file, and get BasePath to user dir
  851. if ((gpTermsrvBuildSysIniPath == NULL) || !(gpTermsrvBuildSysIniPath(&a->NtFileName, &UniSysPath, &UserBasePath))) {
  852. #if DBG
  853. //DbgPrint("TermsrvSyncUserIniFile: Error building Sys Ini Path!\n");
  854. #endif
  855. return(FALSE);
  856. }
  857. // Get the ini file name
  858. pwch = wcsrchr(a->NtFileName.Buffer, L'\\') ;
  859. if (pwch == NULL) {
  860. return FALSE;
  861. } else{
  862. pwch++;
  863. }
  864. pwcIniName = RtlAllocateHeap( RtlProcessHeap(),
  865. 0,
  866. (wcslen(pwch) + 1)*sizeof(WCHAR));
  867. if (pwcIniName == NULL) {
  868. return FALSE;
  869. }
  870. wcscpy(pwcIniName, pwch);
  871. pwch = wcsrchr(pwcIniName, L'.');
  872. if (pwch) {
  873. *pwch = L'\0';
  874. }
  875. if (gpGetTermsrCompatFlags) {
  876. gpGetTermsrCompatFlags(pwcIniName, &ulCompatFlags, CompatibilityIniFile);
  877. } else {
  878. return FALSE;
  879. }
  880. // If the INISYNC compatibility flag is set in the registry and the
  881. // system version of the ini file exists, sync up the user version
  882. if (((ulCompatFlags & (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) ==
  883. (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) &&
  884. TermsrvDoesFileExist(&UniSysPath) &&
  885. TermsrvCheckIniSync(&UniSysPath, &UserBasePath)) {
  886. // Create a backup copy of the original file (inifile.ctx)
  887. wcscpy(wcIniPath, UserBasePath.Buffer);
  888. if (UserBasePath.Buffer[UserBasePath.Length/sizeof(WCHAR) - 1] != L'\\')
  889. wcscat(wcIniPath, L"\\");
  890. wcscat(wcIniPath, pwcIniName);
  891. wcscat(wcIniPath, L".ctx");
  892. IniFilePath.Length = wcslen(wcIniPath)*sizeof(WCHAR);
  893. if (gpTermsrvCopyIniFile) {
  894. Status = gpTermsrvCopyIniFile(&a->NtFileName, NULL, &IniFilePath);
  895. #if DBG
  896. if (!NT_SUCCESS(Status)) {
  897. DbgPrint("TermsrvSyncUserIniFile: Error 0x%x creating backup ini file %ws\n",
  898. Status,
  899. wcIniPath);
  900. }
  901. #endif
  902. } else {
  903. return FALSE;
  904. }
  905. // Check that each entry in the system version is in the user's version
  906. InitializeObjectAttributes(&SrcObja,
  907. &UniSysPath,
  908. OBJ_CASE_INSENSITIVE,
  909. NULL,
  910. NULL);
  911. // Open the src
  912. SrcIosb.Status = STATUS_SUCCESS;
  913. Status = NtOpenFile(&SrcHandle,
  914. FILE_GENERIC_READ,
  915. &SrcObja,
  916. &SrcIosb,
  917. FILE_SHARE_READ|FILE_SHARE_WRITE,
  918. FILE_SYNCHRONOUS_IO_NONALERT);
  919. if( NT_SUCCESS(Status) ) {
  920. // Get final I/O status
  921. Status = SrcIosb.Status;
  922. }
  923. if( !NT_SUCCESS(Status) ) {
  924. #if DBG
  925. DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening SrcFile %ws\n",
  926. Status,
  927. &UniSysPath.Buffer);
  928. #endif
  929. goto Cleanup;
  930. }
  931. // Save the original values
  932. OrigOperation = a->Operation;
  933. OrigMultiValue = a->MultiValueStrings;
  934. OrigAppName = a->ApplicationName;
  935. OrigVarName = a->VariableName;
  936. OrigResultChars = a->ResultChars;
  937. OrigResultMaxChars = a->ResultMaxChars;
  938. OrigResultBuffer = a->ResultBuffer;
  939. OrigUnicode = a->Unicode;
  940. OrigWriteOperation = a->WriteOperation;
  941. // Set up the open for writes
  942. a->WriteOperation = TRUE;
  943. a->Operation = WriteKeyValue;
  944. a->MultiValueStrings = FALSE;
  945. a->Unicode = FALSE;
  946. Status = BaseDllOpenIniFileOnDisk( a );
  947. if( !NT_SUCCESS(Status) ) {
  948. #if DBG
  949. DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening DestFile %ws\n",
  950. Status,
  951. &a->NtFileName.Buffer);
  952. #endif
  953. NtClose( SrcHandle );
  954. goto Cleanup;
  955. }
  956. // set the data up for writing
  957. a->TextEnd = (PCHAR)a->IniFile->BaseAddress +
  958. a->IniFile->EndOfFile;
  959. a->TextCurrent = a->IniFile->BaseAddress;
  960. // Make sure entries in system ini file are in user ini file
  961. Status = TermsrvIniSyncLoop( SrcHandle, a, &fIniUpdated );
  962. #if DBG
  963. if( !NT_SUCCESS(Status) ) {
  964. DbgPrint("TermsrvSyncUserIniFile: Error 0x%x Doing sync loop\n",Status);
  965. }
  966. #endif
  967. // Close the file handles
  968. NtClose( SrcHandle );
  969. BaseDllCloseIniFileOnDisk( a );
  970. // Restore the variables in the ini file structure
  971. a->Operation = OrigOperation;
  972. a->MultiValueStrings = OrigMultiValue;
  973. a->ApplicationName = OrigAppName;
  974. a->VariableName = OrigVarName;
  975. a->ResultChars = OrigResultChars;
  976. a->ResultMaxChars = OrigResultMaxChars;
  977. a->ResultBuffer = OrigResultBuffer;
  978. a->WriteOperation = FALSE;
  979. a->Unicode = OrigUnicode;
  980. a->WriteOperation = OrigWriteOperation;
  981. // Get the last write time of the system ini file
  982. InitializeObjectAttributes( &objaIni,
  983. &UniSysPath,
  984. OBJ_CASE_INSENSITIVE,
  985. NULL,
  986. NULL
  987. );
  988. // Now query it
  989. Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo );
  990. // Update the sync time in the inisync file
  991. if (NT_SUCCESS(Status)) {
  992. TermsrvPutSyncTime( &UniSysPath,
  993. &UserBasePath,
  994. &BasicInfo.LastWriteTime
  995. );
  996. }
  997. }
  998. Cleanup:
  999. // Free the unicode buffers
  1000. RtlFreeHeap( RtlProcessHeap(), 0, UniSysPath.Buffer );
  1001. RtlFreeHeap( RtlProcessHeap(), 0, UserBasePath.Buffer );
  1002. RtlFreeHeap( RtlProcessHeap(), 0, pwcIniName);
  1003. return(fIniUpdated);
  1004. }
  1005. /*****************************************************************************
  1006. *
  1007. * TermsrvIniSyncLoop
  1008. *
  1009. * This routine will verify that there's a corresponding entry in the user's
  1010. * ini file for each entry in the system ini file.
  1011. *
  1012. * ENTRY:
  1013. * HANDLE SrcHandle (INPUT) - Handle to system ini file
  1014. * PINIFILE_PARAMETERS a (INPUT) - pointer to current ini file structure
  1015. * PBOOLEAN pfIniUpdated (OUTPUT) - Returns TRUE if user ini file is modified
  1016. *
  1017. * EXIT:
  1018. * STATUS_SUCCESS - no error
  1019. *
  1020. ****************************************************************************/
  1021. NTSTATUS
  1022. TermsrvIniSyncLoop(HANDLE SrcHandle,
  1023. PINIFILE_PARAMETERS a,
  1024. PBOOLEAN pfIniUpdated)
  1025. {
  1026. PCHAR pStr;
  1027. NTSTATUS Status;
  1028. ULONG StringSize;
  1029. CHAR IOBuf[512];
  1030. ULONG IOBufSize = 512;
  1031. ULONG IOBufIndex = 0;
  1032. ULONG IOBufFillSize = 0;
  1033. ANSI_STRING AnsiSection;
  1034. PCH pch;
  1035. PVOID pSection, origbase;
  1036. AnsiSection.Buffer = NULL;
  1037. *pfIniUpdated = FALSE;
  1038. while( 1 ) {
  1039. pStr = NULL;
  1040. StringSize = 0;
  1041. if (gpTermsrvGetString == NULL) {
  1042. return STATUS_UNSUCCESSFUL;
  1043. }
  1044. // Get a string from the source ini file
  1045. Status = gpTermsrvGetString(SrcHandle,
  1046. &pStr,
  1047. &StringSize,
  1048. IOBuf,
  1049. IOBufSize,
  1050. &IOBufIndex,
  1051. &IOBufFillSize);
  1052. if( !NT_SUCCESS(Status) ) {
  1053. ASSERT( pStr == NULL );
  1054. if( Status == STATUS_END_OF_FILE ) {
  1055. Status = STATUS_SUCCESS;
  1056. }
  1057. if (AnsiSection.Buffer) {
  1058. RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer );
  1059. }
  1060. a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile;
  1061. return( Status );
  1062. }
  1063. // Make sure we got some actual data
  1064. ASSERT( pStr != NULL );
  1065. // Is this a section name?
  1066. if (*pStr == '[') {
  1067. if (AnsiSection.Buffer) {
  1068. RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer );
  1069. AnsiSection.Buffer = NULL;
  1070. }
  1071. pch = strrchr(pStr, ']');
  1072. if (pch) {
  1073. AnsiSection.MaximumLength = (USHORT)(pch - pStr);
  1074. *pch = '\0';
  1075. } else {
  1076. AnsiSection.Length = (USHORT)strlen(pStr);
  1077. }
  1078. AnsiSection.Length = AnsiSection.MaximumLength - 1;
  1079. AnsiSection.Buffer = RtlAllocateHeap(RtlProcessHeap(),
  1080. 0,
  1081. AnsiSection.MaximumLength);
  1082. if (!AnsiSection.Buffer) {
  1083. return STATUS_INSUFFICIENT_RESOURCES;
  1084. }
  1085. strcpy(AnsiSection.Buffer, pStr+1);
  1086. a->ApplicationName = AnsiSection;
  1087. a->TextCurrent = a->IniFile->BaseAddress; // reset file pointer
  1088. // See if the section already exists, if so save the start of it
  1089. Status = BaseDllFindSection( a );
  1090. if (NT_SUCCESS(Status)) {
  1091. pSection = a->TextCurrent;
  1092. } else {
  1093. pSection = NULL;
  1094. }
  1095. // If it's not a comment, see if the entry is in the user's ini file
  1096. } else if (*pStr != ';') {
  1097. pch = strchr(pStr, '=');
  1098. if (pch) {
  1099. a->VariableName.Length = a->VariableName.MaximumLength =
  1100. (USHORT)(pch - pStr);
  1101. a->VariableName.Buffer = pStr;
  1102. a->ValueBuffer = (++pch);
  1103. a->ValueLength = 0;
  1104. while (*pch && (*pch != 0xa) && (*pch != 0xd)) {
  1105. pch++;
  1106. a->ValueLength++;
  1107. }
  1108. // If the section exists, check for the keyword in user's ini
  1109. if (pSection) {
  1110. a->TextCurrent = pSection;
  1111. Status = BaseDllFindKeyword( a );
  1112. }
  1113. // If variable isn't found, write it out
  1114. if (!pSection || !NT_SUCCESS( Status )) {
  1115. origbase = a->TextCurrent = a->IniFile->BaseAddress;
  1116. Status = BaseDllWriteKeywordValue( a, NULL );
  1117. a->TextEnd = (PCHAR)a->IniFile->BaseAddress +
  1118. a->IniFile->EndOfFile;
  1119. if (!NT_SUCCESS(Status)) {
  1120. #if DBG
  1121. DbgPrint("TermsrvIniSyncLoop: Error 0x%x write Key Value\n",
  1122. Status);
  1123. #endif
  1124. a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile;
  1125. RtlFreeHeap( RtlProcessHeap(), 0, pStr );
  1126. if (AnsiSection.Buffer) {
  1127. RtlFreeHeap(RtlProcessHeap(),
  1128. 0,
  1129. AnsiSection.Buffer);
  1130. }
  1131. return(Status);
  1132. }
  1133. *pfIniUpdated = TRUE;
  1134. if (origbase != a->IniFile->BaseAddress) {
  1135. a->TextCurrent = a->IniFile->BaseAddress;
  1136. Status = BaseDllFindSection( a );
  1137. if (NT_SUCCESS(Status)) {
  1138. pSection = a->TextCurrent;
  1139. } else {
  1140. pSection = NULL;
  1141. }
  1142. }
  1143. }
  1144. }
  1145. }
  1146. } // end while(1)
  1147. }
  1148. /******************************************************************************
  1149. *
  1150. * GetPerUserWindowsDirectory
  1151. *
  1152. *
  1153. *
  1154. *****************************************************************************/
  1155. ULONG
  1156. GetPerUserWindowsDirectory(PWCHAR TermSrvWindowsPath, ULONG len)
  1157. {
  1158. WCHAR Buf[MAX_PATH+1];
  1159. UNICODE_STRING BaseHomePathVariableName, BaseHomeDriveVariableName;
  1160. NTSTATUS Status;
  1161. UNICODE_STRING Path;
  1162. if( !IsTSAppCompatEnabled() || IsSystemLUID() || (len < MAX_PATH) ) {
  1163. return 0;
  1164. }
  1165. /*
  1166. * Check for HOMEDRIVE and HOMEPATH
  1167. */
  1168. RtlInitUnicodeString(&BaseHomeDriveVariableName,L"HOMEDRIVE");
  1169. RtlInitUnicodeString(&BaseHomePathVariableName,L"HOMEPATH");
  1170. Path.Buffer = Buf;
  1171. Path.Length = 0;
  1172. Path.MaximumLength = (MAX_PATH * sizeof(WCHAR)) - (9 * sizeof(WCHAR)); //MAX_PATH - wcslen(L"\\WINDOWS") + 1
  1173. if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomeDriveVariableName,
  1174. &Path))) {
  1175. Path.Buffer[Path.Length] = UNICODE_NULL;
  1176. wcscpy(TermSrvWindowsPath,Path.Buffer);
  1177. Path.MaximumLength -= Path.Length;
  1178. if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomePathVariableName,
  1179. &Path))) {
  1180. Path.Buffer[Path.Length] = UNICODE_NULL;
  1181. wcscat(TermSrvWindowsPath,Path.Buffer);
  1182. }
  1183. }
  1184. if (!NT_SUCCESS(Status)) {
  1185. #if DBG
  1186. DbgPrint("GetPerUserWindowsDirectory Failed with Status %lx\n", Status);
  1187. #endif
  1188. return 0;
  1189. }
  1190. /*
  1191. * Add a trailing backslash if one's not already there
  1192. */
  1193. if (TermSrvWindowsPath[wcslen(TermSrvWindowsPath) -1 ] != L'\\') {
  1194. wcscat(TermSrvWindowsPath,L"\\");
  1195. }
  1196. wcscat(TermSrvWindowsPath,L"WINDOWS");
  1197. return( wcslen(TermSrvWindowsPath) );
  1198. }