windows-nt/Source/XPSP1/NT/base/win32/client/termutil.c
2020-09-26 16:20:57 +08:00

1429 lines
44 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
termutil.c
Abstract:
Terminal server support functions and inifile syncing/merging code
Author:
Revision History:
--*/
#include "basedll.h"
#include "regapi.h"
#define TERMSRV_INIFILE_TIMES L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\IniFile Times"
BOOL IsTerminalServerCompatible(VOID)
{
PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress );
if ((NtHeader) && (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)) {
return TRUE;
} else {
return FALSE;
}
}
BOOL IsTSAppCompatEnabled(VOID)
{
NTSTATUS NtStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UniString;
HKEY hKey = 0;
ULONG ul, ulcbuf;
PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL;
BOOL retval = TRUE;
RtlInitUnicodeString(&UniString,REG_NTAPI_CONTROL_TSERVER);
// Determine the value info buffer size
ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) +
sizeof(ULONG);
pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
ulcbuf);
// Did everything initialize OK?
if (UniString.Buffer && pKeyValInfo) {
InitializeObjectAttributes(&ObjectAttributes,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(NtStatus)) {
RtlInitUnicodeString(&UniString,
L"TSAppCompat");
NtStatus = NtQueryValueKey(hKey,
&UniString,
KeyValuePartialInformation,
pKeyValInfo,
ulcbuf,
&ul);
if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) {
if ((*(PULONG)pKeyValInfo->Data) == 0) {
retval = FALSE;
}
}
NtClose(hKey);
}
}
// Free up the buffers we allocated
// Need to zero out the buffers, because some apps (MS Internet Assistant)
// won't install if the heap is not zero filled.
if (pKeyValInfo) {
memset(pKeyValInfo, 0, ulcbuf);
RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo );
}
return(retval);
}
BOOL IsSystemLUID(VOID)
{
HANDLE TokenHandle;
UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ];
ULONG ReturnLength;
LUID CurrentLUID = { 0, 0 };
LUID SystemLUID = SYSTEM_LUID;
NTSTATUS Status;
if ( CurrentLUID.LowPart == 0 && CurrentLUID.HighPart == 0 ) {
Status = NtOpenProcessToken( NtCurrentProcess(),
TOKEN_QUERY,
&TokenHandle );
if ( !NT_SUCCESS( Status ) )
return(TRUE);
NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation,
sizeof(TokenInformation), &ReturnLength );
NtClose( TokenHandle );
RtlCopyLuid(&CurrentLUID,
&(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId));
}
if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) {
return(TRUE);
} else {
return(FALSE );
}
}
void InitializeTermsrvFpns(void)
{
HANDLE dllHandle;
if (IsTerminalServerCompatible() ||
(IsSystemLUID()) ||
(!IsTSAppCompatEnabled())) {
return;
}
//
// Load Terminal Server application compatibility dll
//
dllHandle = LoadLibraryW(L"tsappcmp.dll");
if (dllHandle) {
gpTermsrvFormatObjectName =
(PTERMSRVFORMATOBJECTNAME)GetProcAddress(dllHandle,"TermsrvFormatObjectName");
gpTermsrvGetComputerName =
(PTERMSRVGETCOMPUTERNAME)GetProcAddress(dllHandle,"TermsrvGetComputerName");
gpTermsrvAdjustPhyMemLimits =
(PTERMSRVADJUSTPHYMEMLIMITS)GetProcAddress(dllHandle,"TermsrvAdjustPhyMemLimits");
gpTermsrvGetWindowsDirectoryA =
(PTERMSRVGETWINDOWSDIRECTORYA)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryA");
gpTermsrvGetWindowsDirectoryW =
(PTERMSRVGETWINDOWSDIRECTORYW)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryW");
gpTermsrvConvertSysRootToUserDir =
(PTERMSRVCONVERTSYSROOTTOUSERDIR)GetProcAddress(dllHandle,"TermsrvConvertSysRootToUserDir");
gpTermsrvBuildIniFileName =
(PTERMSRVBUILDINIFILENAME)GetProcAddress(dllHandle,"TermsrvBuildIniFileName");
gpTermsrvCORIniFile =
(PTERMSRVCORINIFILE)GetProcAddress(dllHandle,"TermsrvCORIniFile");
gpGetTermsrCompatFlags =
(PGETTERMSRCOMPATFLAGS)GetProcAddress(dllHandle,"GetTermsrCompatFlags");
gpTermsrvBuildSysIniPath =
(PTERMSRVBUILDSYSINIPATH)GetProcAddress(dllHandle,"TermsrvBuildSysIniPath");
gpTermsrvCopyIniFile =
(PTERMSRVCOPYINIFILE)GetProcAddress(dllHandle,"TermsrvCopyIniFile");
gpTermsrvGetString =
(PTERMSRVGETSTRING)GetProcAddress(dllHandle,"TermsrvGetString");
gpTermsrvLogInstallIniFile =
(PTERMSRVLOGINSTALLINIFILE)GetProcAddress(dllHandle,"TermsrvLogInstallIniFile");
}
}
/*****************************************************************************
*
* TermsrvAppInstallMode
*
* Returns whether the system is in Install mode or not
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
WINBASEAPI
BOOL
WINAPI
TermsrvAppInstallMode( VOID )
{
if ( BaseStaticServerData->fTermsrvAppInstallMode )
return( TRUE );
return( FALSE );
}
/*****************************************************************************
*
* SetTermsrvAppInstallMode
*
* Turns App install mode on or off. Default is off
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
WINBASEAPI
BOOL
WINAPI
SetTermsrvAppInstallMode( BOOL bState )
{
typedef BOOL ( APIENTRY Func_CheckTokenMembership )( HANDLE , PSID , PBOOL);
BOOL rc;
NTSTATUS Status;
PSID pSid = NULL ;
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
HINSTANCE dllHandle;
if ( !( IsTerminalServer() && IsTSAppCompatEnabled() ) )
{
return FALSE;
}
dllHandle = LoadLibraryW(L"advapi32.dll");
if (dllHandle)
{
Func_CheckTokenMembership *fPtr;
fPtr = (Func_CheckTokenMembership * )GetProcAddress(dllHandle,"CheckTokenMembership");
if (fPtr)
{
Status = RtlAllocateAndInitializeSid(
&SidAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSid
);
if (NT_SUCCESS(Status))
{
BOOL FoundAdmin;
if ( fPtr (NULL, pSid , &FoundAdmin))
{
if (FoundAdmin)
{
// caller is admin, go ahead
#if defined(BUILD_WOW6432)
Status = CsrBasepSetTermsrvAppInstallMode(bState);
#else
BASE_API_MSG m;
PBASE_SET_TERMSRVAPPINSTALLMODE c= (PBASE_SET_TERMSRVAPPINSTALLMODE)&m.u.SetTermsrvAppInstallMode;
c->bState = bState;
Status = CsrClientCallServer((PCSR_API_MSG)&m, NULL,
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
BasepSetTermsrvAppInstallMode),
sizeof( *c ));
#endif
if ( NT_SUCCESS( Status ) )
{
//
// Load tsappcmp.dll
//
if (gpTermsrvUpdateAllUserMenu == NULL)
{
HANDLE dllHandle;
//
// Load Terminal Server application compatibility dll
//
dllHandle = LoadLibraryW(L"tsappcmp.dll");
if (dllHandle)
{
gpTermsrvUpdateAllUserMenu =
(PTERMSRVUPDATEALLUSERMENU)GetProcAddress(dllHandle,"TermsrvUpdateAllUserMenu");
}
}
if (gpTermsrvUpdateAllUserMenu)
{
gpTermsrvUpdateAllUserMenu( bState == TRUE ? 0 : 1 );
}
rc = TRUE;
}
else
{
SetLastError( RtlNtStatusToDosError( Status ) );
rc = FALSE;
}
}
else
{
// user is not admin
SetLastError( ERROR_ACCESS_DENIED );
rc = FALSE;
}
}
else
{
// call to CheckTokenMembership() failed, it set the last error
rc = FALSE;
}
}
else
{
// attempt to allocate and init SID failed.
SetLastError( RtlNtStatusToDosError( Status ) );
rc = FALSE;
}
}
else
{
// function not found, GetProc() set the last error.
rc = FALSE;
}
FreeLibrary( dllHandle );
}
else
{
// library not found, LoadLib() set the last error
rc = FALSE;
}
if (pSid)
{
RtlFreeSid( pSid );
}
return rc;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/*
Ini File syncing/merging code
*/
//////////////////////////////////////////////////////////////////////////////
/* External Functions */
NTSTATUS
BaseDllOpenIniFileOnDisk(
PINIFILE_PARAMETERS a
);
NTSTATUS
BaseDllWriteKeywordValue(
IN PINIFILE_PARAMETERS a,
IN PUNICODE_STRING VariableName OPTIONAL
);
NTSTATUS
BaseDllCloseIniFileOnDisk(
IN PINIFILE_PARAMETERS a
);
NTSTATUS
BaseDllFindSection(
IN PINIFILE_PARAMETERS a
);
NTSTATUS
BaseDllFindKeyword(
IN PINIFILE_PARAMETERS a
);
NTSTATUS
TermsrvIniSyncLoop( HANDLE SrcHandle,
PINIFILE_PARAMETERS a,
PBOOLEAN pfIniUpdated
);
NTSTATUS
TermsrvGetSyncTime( PUNICODE_STRING pSysIniPath,
PUNICODE_STRING pUserBasePath,
PLARGE_INTEGER pLastSyncTime
);
NTSTATUS
TermsrvPutSyncTime( PUNICODE_STRING pSysIniPath,
PUNICODE_STRING pUserBasePath,
PLARGE_INTEGER pLastSyncTime
);
/*****************************************************************************
*
* TermsrvGetSyncTime
*
* This routine will get the time of the system ini file that the user ini
* file was last sync'd with.
*
* ENTRY:
* PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
* PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
* PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
*
* EXIT:
* STATUS_SUCCESS - successfully retrieved the last sync time from infile.upd
*
****************************************************************************/
NTSTATUS
TermsrvGetSyncTime(
PUNICODE_STRING pSysIniPath,
PUNICODE_STRING pUserBasePath,
PLARGE_INTEGER pLastSyncTime)
{
NTSTATUS Status;
HANDLE hUpdate = NULL;
OBJECT_ATTRIBUTES ObjAUpd;
IO_STATUS_BLOCK Iosb;
FILE_STANDARD_INFORMATION StandardInfo;
WCHAR wcUpdateFile[MAX_PATH+1];
UNICODE_STRING UniUpdateName = {0,
sizeof(wcUpdateFile),
wcUpdateFile};
PCHAR pBuff = NULL, pBuffEnd;
PWCH pwch;
SIZE_T ulBuffSize;
LONG lresult;
if (!pSysIniPath) {
return STATUS_INVALID_PARAMETER_1;
}
if (!pUserBasePath) {
return STATUS_INVALID_PARAMETER_2;
}
if (!pLastSyncTime) {
return STATUS_INVALID_PARAMETER_3;
}
Status = RtlAppendUnicodeStringToString(&UniUpdateName,
pUserBasePath);
if (NT_SUCCESS(Status)) {
Status = RtlAppendUnicodeToString(&UniUpdateName,
L"\\inifile.upd");
}
if (! NT_SUCCESS(Status)) {
return Status;
}
pLastSyncTime->LowPart = 0;
pLastSyncTime->HighPart = 0;
InitializeObjectAttributes( &ObjAUpd,
&UniUpdateName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
// Open the update log
Iosb.Status = STATUS_SUCCESS;
Status = NtOpenFile( &hUpdate,
FILE_GENERIC_READ,
&ObjAUpd,
&Iosb,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT // OpenOptions
);
// Get the size of the file
if (NT_SUCCESS( Status )) {
Status = NtQueryInformationFile( hUpdate,
&Iosb,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation
);
if (Status == STATUS_BUFFER_OVERFLOW) {
Status = STATUS_SUCCESS;
}
#if DBG
else if (!NT_SUCCESS( Status )) {
DbgPrint( "TermsrvGetSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status );
}
#endif
}
if (NT_SUCCESS( Status )) {
ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR);
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&pBuff,
0,
&ulBuffSize,
MEM_RESERVE,
PAGE_READWRITE
);
}
if (NT_SUCCESS( Status )) {
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&pBuff,
0,
&ulBuffSize,
MEM_COMMIT,
PAGE_READWRITE
);
}
if (NT_SUCCESS( Status )) {
Status = NtReadFile( hUpdate,
NULL,
NULL,
NULL,
&Iosb,
pBuff,
StandardInfo.EndOfFile.LowPart,
NULL,
NULL
);
if ( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
}
if ( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
}
// Look for this ini file in the list
if (NT_SUCCESS(Status)) {
pwch = (PWCHAR)pBuff;
pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart;
// Look for the file in the sorted list
while ((pwch < (PWCHAR)pBuffEnd) &&
((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) {
pwch += wcslen(pwch) + sizeof(LARGE_INTEGER)/sizeof(WCHAR) + 1;
}
if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) {
pwch += wcslen(pwch) + 1;
pLastSyncTime->LowPart = ((PLARGE_INTEGER)pwch)->LowPart;
pLastSyncTime->HighPart = ((PLARGE_INTEGER)pwch)->HighPart;
}
}
if (NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
if (pBuff) {
NtFreeVirtualMemory( NtCurrentProcess(),
&pBuff,
&ulBuffSize,
MEM_RELEASE
);
}
if (hUpdate) {
Status = NtClose( hUpdate );
}
return(Status);
}
/*****************************************************************************
*
* TermsrvPutSyncTime
*
* This routine will write the time of the system ini file that the user ini
* file was last sync'd with.
*
* ENTRY:
* PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
* PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
* PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
*
* EXIT:
* STATUS_SUCCESS - successfully stored the last sync time in infile.upd
*
****************************************************************************/
NTSTATUS
TermsrvPutSyncTime(
PUNICODE_STRING pSysIniPath,
PUNICODE_STRING pUserBasePath,
PLARGE_INTEGER pLastSyncTime)
{
NTSTATUS Status;
HANDLE hUpdate = NULL;
OBJECT_ATTRIBUTES ObjAUpd;
IO_STATUS_BLOCK Iosb;
FILE_STANDARD_INFORMATION StandardInfo;
WCHAR wcUpdateFile[MAX_PATH+1];
UNICODE_STRING UniUpdateName = {0,
sizeof(wcUpdateFile),
wcUpdateFile};
PCHAR pBuff = NULL, pBuffEnd;
PWCH pwch;
SIZE_T ulBuffSize;
ULONG ulLength;
SIZE_T ulRegionSize;
LONG lresult;
LARGE_INTEGER FileLength;
FILE_POSITION_INFORMATION CurrentPos;
if (!pSysIniPath) {
return STATUS_INVALID_PARAMETER_1;
}
if (!pUserBasePath) {
return STATUS_INVALID_PARAMETER_2;
}
if (!pLastSyncTime) {
return STATUS_INVALID_PARAMETER_3;
}
Status = RtlAppendUnicodeStringToString(&UniUpdateName,
pUserBasePath);
if (NT_SUCCESS(Status)) {
Status = RtlAppendUnicodeToString(&UniUpdateName,
L"\\inifile.upd");
}
if (! NT_SUCCESS(Status)) {
return Status;
}
InitializeObjectAttributes( &ObjAUpd,
&UniUpdateName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
// Open the update log
Iosb.Status = STATUS_SUCCESS;
Status = NtCreateFile( &hUpdate,
FILE_READ_DATA | FILE_WRITE_DATA |
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjAUpd,
&Iosb,
NULL, // Allocation size
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
FILE_SHARE_WRITE, // dwShareMode
FILE_OPEN_IF, // CreateDisposition
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_NON_DIRECTORY_FILE, // CreateFlags
NULL, // EaBuffer
0 // EaLength
);
if (NT_SUCCESS( Status )) {
Status = NtQueryInformationFile( hUpdate,
&Iosb,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation
);
if (Status == STATUS_BUFFER_OVERFLOW) {
Status = STATUS_SUCCESS;
}
#if DBG
else if (!NT_SUCCESS( Status )) {
DbgPrint( "TermsrvPutLastSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status );
}
#endif
}
if (NT_SUCCESS( Status )) {
ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR);
ulRegionSize = ulBuffSize + 0x1000; // Room for 4K of growth
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&pBuff,
0,
&ulRegionSize,
MEM_RESERVE,
PAGE_READWRITE
);
}
if (NT_SUCCESS( Status )) {
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&pBuff,
0,
&ulBuffSize,
MEM_COMMIT,
PAGE_READWRITE
);
}
if (NT_SUCCESS( Status ) && StandardInfo.EndOfFile.LowPart) {
Status = NtReadFile( hUpdate,
NULL,
NULL,
NULL,
&Iosb,
pBuff,
StandardInfo.EndOfFile.LowPart,
NULL,
NULL
);
if ( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
}
if ( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
}
// Look for this ini file in the list
if (NT_SUCCESS(Status)) {
pwch = (PWCHAR)pBuff;
pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart;
// Look for the file in the list
while ((pwch < (PWCHAR)pBuffEnd) &&
((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) {
pwch += wcslen(pwch) + (sizeof(LARGE_INTEGER)/sizeof(WCHAR)) + 1;
}
// If the ini file is already in the file, just update the time
if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) {
pwch += wcslen(pwch) + 1;
((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart;
((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart;
} else { // Ini file not in list
// Figure out the size to grow the file
ulLength = (pSysIniPath->Length + 2) + sizeof(LARGE_INTEGER);
ulBuffSize += ulLength;
// Grow the memory region
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&pBuff,
0,
&ulBuffSize,
MEM_COMMIT,
PAGE_READWRITE
);
if (NT_SUCCESS(Status)) {
// figure out where the entry goes in the file
if (pwch < (PWCHAR)pBuffEnd) {
RtlMoveMemory( pwch+(ulLength/sizeof(WCHAR)),
pwch,
pBuffEnd - (PCHAR)pwch
);
}
pBuffEnd += ulLength;
wcscpy(pwch, pSysIniPath->Buffer);
pwch += (pSysIniPath->Length + 2)/sizeof(WCHAR);
((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart;
((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart;
}
}
}
if (NT_SUCCESS(Status)) {
CurrentPos.CurrentByteOffset.LowPart = 0;
CurrentPos.CurrentByteOffset.HighPart = 0;
Status = NtSetInformationFile( hUpdate,
&Iosb,
&CurrentPos,
sizeof(CurrentPos),
FilePositionInformation
);
Status = NtWriteFile( hUpdate,
NULL,
NULL,
NULL,
&Iosb,
pBuff,
(ULONG)(pBuffEnd - pBuff + 1),
NULL,
NULL
);
if( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( hUpdate, FALSE, NULL );
}
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
}
if (NT_SUCCESS( Status )) {
FileLength.LowPart = (ULONG)(pBuffEnd - pBuff);
FileLength.HighPart = 0;
Status = NtSetInformationFile( hUpdate,
&Iosb,
&FileLength,
sizeof( FileLength ),
FileEndOfFileInformation
);
}
if (pBuff) {
NtFreeVirtualMemory( NtCurrentProcess(),
&pBuff,
&ulRegionSize,
MEM_RELEASE
);
}
if (hUpdate) {
Status = NtClose( hUpdate );
}
return(Status);
}
/*****************************************************************************
*
* TermsrvCheckIniSync
*
* This routine will get the time of the system ini file that the user ini
* file was last sync'd with.
*
* ENTRY:
* PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path
* PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path
* BOOLEAN fGet (In) - TRUE means to get last sync time, FALSE means to write it
* PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time
*
* EXIT:
* TRUE - User ini file should be sync'd
* FALSE - User ini file should be sync'd
*
****************************************************************************/
BOOLEAN
TermsrvCheckIniSync(
PUNICODE_STRING pSysIniPath,
PUNICODE_STRING pUserBasePath)
{
LARGE_INTEGER LastSyncTime;
OBJECT_ATTRIBUTES objaIni;
FILE_NETWORK_OPEN_INFORMATION BasicInfo;
NTSTATUS Status;
// Get the last sync time of the ini file from the inifile.upd file
TermsrvGetSyncTime(pSysIniPath, pUserBasePath, &LastSyncTime);
// Get the last write time of the system ini file
InitializeObjectAttributes(
&objaIni,
pSysIniPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
// Now query it
Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo );
// If we couldn't get the time or the system ini file has been updated
// since we last sync'd, return TRUE
if (!NT_SUCCESS(Status) ||
((BasicInfo.LastWriteTime.HighPart > LastSyncTime.HighPart) ||
((BasicInfo.LastWriteTime.HighPart == LastSyncTime.HighPart) &&
(BasicInfo.LastWriteTime.LowPart > LastSyncTime.LowPart)))) {
return(TRUE);
}
return(FALSE);
}
/*****************************************************************************
*
* TermsrvDoesFileExist
*
* Returns whether the file exists or not.
*
* Must use NT, not WIN32 pathnames.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
BOOL
TermsrvDoesFileExist(
PUNICODE_STRING pFileName
)
{
NTSTATUS Status;
FILE_BASIC_INFORMATION BasicInfo;
OBJECT_ATTRIBUTES Obja;
InitializeObjectAttributes(
&Obja,
pFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
/*
* Now query it
*/
Status = NtQueryAttributesFile( &Obja, &BasicInfo );
if( NT_SUCCESS( Status ) ) {
return( TRUE );
}
return( FALSE );
}
/*****************************************************************************
*
* TermsrvSyncUserIniFile
*
* This routine will check that the user's ini file is "sync'd" with the
* system version of the ini file. This means that it walks through the
* system ini file and checks that there is a corresponding entry in the
* user's ini file.
*
* ENTRY:
* IN PINIFILE_PARAMETERS a - ptr to inifile structure
*
* EXIT:
* True - Ini file updated
* False - User Ini file was unchanged
*
****************************************************************************/
BOOL TermsrvSyncUserIniFile(PINIFILE_PARAMETERS a)
{
WCHAR wcIniPath[MAX_PATH+1];
UNICODE_STRING IniFilePath = {MAX_PATH,
MAX_PATH+1,
wcIniPath};
PWCH pwch, pwcIniName;
UNICODE_STRING UniSysPath;
UNICODE_STRING UserBasePath;
NTSTATUS Status;
HANDLE SrcHandle;
ULONG ulCompatFlags;
OBJECT_ATTRIBUTES SrcObja;
IO_STATUS_BLOCK SrcIosb;
INIFILE_OPERATION OrigOperation;
BOOLEAN OrigWrite, OrigMultiValue, OrigUnicode,
OrigWriteOperation, fIniUpdated = FALSE;
ANSI_STRING OrigAppName, OrigVarName;
ULONG OrigResultChars, OrigResultMaxChars;
LPSTR OrigResultBuffer;
OBJECT_ATTRIBUTES objaIni;
FILE_NETWORK_OPEN_INFORMATION BasicInfo;
// If INI file mapping is not on, return
if (IsSystemLUID() || TermsrvAppInstallMode()) {
return(FALSE);
}
// Build full system path to the Ini file, and get BasePath to user dir
if ((gpTermsrvBuildSysIniPath == NULL) || !(gpTermsrvBuildSysIniPath(&a->NtFileName, &UniSysPath, &UserBasePath))) {
#if DBG
//DbgPrint("TermsrvSyncUserIniFile: Error building Sys Ini Path!\n");
#endif
return(FALSE);
}
// Get the ini file name
pwch = wcsrchr(a->NtFileName.Buffer, L'\\') ;
if (pwch == NULL) {
return FALSE;
} else{
pwch++;
}
pwcIniName = RtlAllocateHeap( RtlProcessHeap(),
0,
(wcslen(pwch) + 1)*sizeof(WCHAR));
if (pwcIniName == NULL) {
return FALSE;
}
wcscpy(pwcIniName, pwch);
pwch = wcsrchr(pwcIniName, L'.');
if (pwch) {
*pwch = L'\0';
}
if (gpGetTermsrCompatFlags) {
gpGetTermsrCompatFlags(pwcIniName, &ulCompatFlags, CompatibilityIniFile);
} else {
return FALSE;
}
// If the INISYNC compatibility flag is set in the registry and the
// system version of the ini file exists, sync up the user version
if (((ulCompatFlags & (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) ==
(TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) &&
TermsrvDoesFileExist(&UniSysPath) &&
TermsrvCheckIniSync(&UniSysPath, &UserBasePath)) {
// Create a backup copy of the original file (inifile.ctx)
wcscpy(wcIniPath, UserBasePath.Buffer);
if (UserBasePath.Buffer[UserBasePath.Length/sizeof(WCHAR) - 1] != L'\\')
wcscat(wcIniPath, L"\\");
wcscat(wcIniPath, pwcIniName);
wcscat(wcIniPath, L".ctx");
IniFilePath.Length = wcslen(wcIniPath)*sizeof(WCHAR);
if (gpTermsrvCopyIniFile) {
Status = gpTermsrvCopyIniFile(&a->NtFileName, NULL, &IniFilePath);
#if DBG
if (!NT_SUCCESS(Status)) {
DbgPrint("TermsrvSyncUserIniFile: Error 0x%x creating backup ini file %ws\n",
Status,
wcIniPath);
}
#endif
} else {
return FALSE;
}
// Check that each entry in the system version is in the user's version
InitializeObjectAttributes(&SrcObja,
&UniSysPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// Open the src
SrcIosb.Status = STATUS_SUCCESS;
Status = NtOpenFile(&SrcHandle,
FILE_GENERIC_READ,
&SrcObja,
&SrcIosb,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = SrcIosb.Status;
}
if( !NT_SUCCESS(Status) ) {
#if DBG
DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening SrcFile %ws\n",
Status,
&UniSysPath.Buffer);
#endif
goto Cleanup;
}
// Save the original values
OrigOperation = a->Operation;
OrigMultiValue = a->MultiValueStrings;
OrigAppName = a->ApplicationName;
OrigVarName = a->VariableName;
OrigResultChars = a->ResultChars;
OrigResultMaxChars = a->ResultMaxChars;
OrigResultBuffer = a->ResultBuffer;
OrigUnicode = a->Unicode;
OrigWriteOperation = a->WriteOperation;
// Set up the open for writes
a->WriteOperation = TRUE;
a->Operation = WriteKeyValue;
a->MultiValueStrings = FALSE;
a->Unicode = FALSE;
Status = BaseDllOpenIniFileOnDisk( a );
if( !NT_SUCCESS(Status) ) {
#if DBG
DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening DestFile %ws\n",
Status,
&a->NtFileName.Buffer);
#endif
NtClose( SrcHandle );
goto Cleanup;
}
// set the data up for writing
a->TextEnd = (PCHAR)a->IniFile->BaseAddress +
a->IniFile->EndOfFile;
a->TextCurrent = a->IniFile->BaseAddress;
// Make sure entries in system ini file are in user ini file
Status = TermsrvIniSyncLoop( SrcHandle, a, &fIniUpdated );
#if DBG
if( !NT_SUCCESS(Status) ) {
DbgPrint("TermsrvSyncUserIniFile: Error 0x%x Doing sync loop\n",Status);
}
#endif
// Close the file handles
NtClose( SrcHandle );
BaseDllCloseIniFileOnDisk( a );
// Restore the variables in the ini file structure
a->Operation = OrigOperation;
a->MultiValueStrings = OrigMultiValue;
a->ApplicationName = OrigAppName;
a->VariableName = OrigVarName;
a->ResultChars = OrigResultChars;
a->ResultMaxChars = OrigResultMaxChars;
a->ResultBuffer = OrigResultBuffer;
a->WriteOperation = FALSE;
a->Unicode = OrigUnicode;
a->WriteOperation = OrigWriteOperation;
// Get the last write time of the system ini file
InitializeObjectAttributes( &objaIni,
&UniSysPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
// Now query it
Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo );
// Update the sync time in the inisync file
if (NT_SUCCESS(Status)) {
TermsrvPutSyncTime( &UniSysPath,
&UserBasePath,
&BasicInfo.LastWriteTime
);
}
}
Cleanup:
// Free the unicode buffers
RtlFreeHeap( RtlProcessHeap(), 0, UniSysPath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, UserBasePath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, pwcIniName);
return(fIniUpdated);
}
/*****************************************************************************
*
* TermsrvIniSyncLoop
*
* This routine will verify that there's a corresponding entry in the user's
* ini file for each entry in the system ini file.
*
* ENTRY:
* HANDLE SrcHandle (INPUT) - Handle to system ini file
* PINIFILE_PARAMETERS a (INPUT) - pointer to current ini file structure
* PBOOLEAN pfIniUpdated (OUTPUT) - Returns TRUE if user ini file is modified
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvIniSyncLoop(HANDLE SrcHandle,
PINIFILE_PARAMETERS a,
PBOOLEAN pfIniUpdated)
{
PCHAR pStr;
NTSTATUS Status;
ULONG StringSize;
CHAR IOBuf[512];
ULONG IOBufSize = 512;
ULONG IOBufIndex = 0;
ULONG IOBufFillSize = 0;
ANSI_STRING AnsiSection;
PCH pch;
PVOID pSection, origbase;
AnsiSection.Buffer = NULL;
*pfIniUpdated = FALSE;
while( 1 ) {
pStr = NULL;
StringSize = 0;
if (gpTermsrvGetString == NULL) {
return STATUS_UNSUCCESSFUL;
}
// Get a string from the source ini file
Status = gpTermsrvGetString(SrcHandle,
&pStr,
&StringSize,
IOBuf,
IOBufSize,
&IOBufIndex,
&IOBufFillSize);
if( !NT_SUCCESS(Status) ) {
ASSERT( pStr == NULL );
if( Status == STATUS_END_OF_FILE ) {
Status = STATUS_SUCCESS;
}
if (AnsiSection.Buffer) {
RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer );
}
a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile;
return( Status );
}
// Make sure we got some actual data
ASSERT( pStr != NULL );
// Is this a section name?
if (*pStr == '[') {
if (AnsiSection.Buffer) {
RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer );
AnsiSection.Buffer = NULL;
}
pch = strrchr(pStr, ']');
if (pch) {
AnsiSection.MaximumLength = (USHORT)(pch - pStr);
*pch = '\0';
} else {
AnsiSection.Length = (USHORT)strlen(pStr);
}
AnsiSection.Length = AnsiSection.MaximumLength - 1;
AnsiSection.Buffer = RtlAllocateHeap(RtlProcessHeap(),
0,
AnsiSection.MaximumLength);
if (!AnsiSection.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
strcpy(AnsiSection.Buffer, pStr+1);
a->ApplicationName = AnsiSection;
a->TextCurrent = a->IniFile->BaseAddress; // reset file pointer
// See if the section already exists, if so save the start of it
Status = BaseDllFindSection( a );
if (NT_SUCCESS(Status)) {
pSection = a->TextCurrent;
} else {
pSection = NULL;
}
// If it's not a comment, see if the entry is in the user's ini file
} else if (*pStr != ';') {
pch = strchr(pStr, '=');
if (pch) {
a->VariableName.Length = a->VariableName.MaximumLength =
(USHORT)(pch - pStr);
a->VariableName.Buffer = pStr;
a->ValueBuffer = (++pch);
a->ValueLength = 0;
while (*pch && (*pch != 0xa) && (*pch != 0xd)) {
pch++;
a->ValueLength++;
}
// If the section exists, check for the keyword in user's ini
if (pSection) {
a->TextCurrent = pSection;
Status = BaseDllFindKeyword( a );
}
// If variable isn't found, write it out
if (!pSection || !NT_SUCCESS( Status )) {
origbase = a->TextCurrent = a->IniFile->BaseAddress;
Status = BaseDllWriteKeywordValue( a, NULL );
a->TextEnd = (PCHAR)a->IniFile->BaseAddress +
a->IniFile->EndOfFile;
if (!NT_SUCCESS(Status)) {
#if DBG
DbgPrint("TermsrvIniSyncLoop: Error 0x%x write Key Value\n",
Status);
#endif
a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile;
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
if (AnsiSection.Buffer) {
RtlFreeHeap(RtlProcessHeap(),
0,
AnsiSection.Buffer);
}
return(Status);
}
*pfIniUpdated = TRUE;
if (origbase != a->IniFile->BaseAddress) {
a->TextCurrent = a->IniFile->BaseAddress;
Status = BaseDllFindSection( a );
if (NT_SUCCESS(Status)) {
pSection = a->TextCurrent;
} else {
pSection = NULL;
}
}
}
}
}
} // end while(1)
}
/******************************************************************************
*
* GetPerUserWindowsDirectory
*
*
*
*****************************************************************************/
ULONG
GetPerUserWindowsDirectory(PWCHAR TermSrvWindowsPath, ULONG len)
{
WCHAR Buf[MAX_PATH+1];
UNICODE_STRING BaseHomePathVariableName, BaseHomeDriveVariableName;
NTSTATUS Status;
UNICODE_STRING Path;
if( !IsTSAppCompatEnabled() || IsSystemLUID() || (len < MAX_PATH) ) {
return 0;
}
/*
* Check for HOMEDRIVE and HOMEPATH
*/
RtlInitUnicodeString(&BaseHomeDriveVariableName,L"HOMEDRIVE");
RtlInitUnicodeString(&BaseHomePathVariableName,L"HOMEPATH");
Path.Buffer = Buf;
Path.Length = 0;
Path.MaximumLength = (MAX_PATH * sizeof(WCHAR)) - (9 * sizeof(WCHAR)); //MAX_PATH - wcslen(L"\\WINDOWS") + 1
if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomeDriveVariableName,
&Path))) {
Path.Buffer[Path.Length] = UNICODE_NULL;
wcscpy(TermSrvWindowsPath,Path.Buffer);
Path.MaximumLength -= Path.Length;
if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomePathVariableName,
&Path))) {
Path.Buffer[Path.Length] = UNICODE_NULL;
wcscat(TermSrvWindowsPath,Path.Buffer);
}
}
if (!NT_SUCCESS(Status)) {
#if DBG
DbgPrint("GetPerUserWindowsDirectory Failed with Status %lx\n", Status);
#endif
return 0;
}
/*
* Add a trailing backslash if one's not already there
*/
if (TermSrvWindowsPath[wcslen(TermSrvWindowsPath) -1 ] != L'\\') {
wcscat(TermSrvWindowsPath,L"\\");
}
wcscat(TermSrvWindowsPath,L"WINDOWS");
return( wcslen(TermSrvWindowsPath) );
}