Windows2003-3790/termsrv/tsappcmp/inimap.c

2868 lines
87 KiB
C

/*************************************************************************
*
* inimap.c
*
* Handle Copy-On-Reference Ini File Mapping
*
* copyright notice: Copyright 1998 Micrsoft
*
*
*************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define LOCAL
#include "regmap.h"
//#include "basedll.h"
#if DBG
ULONG
DbgPrint(
PCH Format,
...
);
#define DBGPRINT(x) DbgPrint x
#if DBGTRACE
#define TRACE0(x) DbgPrint x
#define TRACE1(x) DbgPrint x
#else
#define TRACE0(x)
#define TRACE1(x)
#endif
#else
#define DBGPRINT(x)
#define TRACE0(x)
#define TRACE1(x)
#endif
#define IS_NEWLINE_CHAR( c ) ((c == 0x0D) || (c == 0x0A))
/*
* INI_BUF_SIZE defines the maximum number of characters that can
* be on a single INI file line. If a line contains more than this
* number of characters, the additional characters will be lost.
*/
#define INI_BUF_SIZE 1024
/* Internal Functions */
BOOL
TermsrvDoesFileExist(
PUNICODE_STRING pFileName
);
BOOL
TermsrvBuildSysIniPath(
PUNICODE_STRING pIniPath,
PUNICODE_STRING pSysPath,
PUNICODE_STRING pUserPath
);
BOOL
TermsrvCopyIniFile(
PUNICODE_STRING pSysPath,
PUNICODE_STRING pUserPath,
PUNICODE_STRING pFileName
);
BOOL
TermsrvGetUnicodeRemainder(
PUNICODE_STRING pFullPath,
PUNICODE_STRING pPrefix,
PUNICODE_STRING pRemainder
);
NTSTATUS
TermsrvIniCopyLoop(
HANDLE SrcHandle,
HANDLE DestHandle
);
NTSTATUS
TermsrvPutString(
HANDLE DestHandle,
PCHAR pStr,
ULONG StringSize
);
NTSTATUS
TermsrvProcessBuffer(
PCHAR *ppStr,
PULONG pStrSize,
PULONG pStrBufSize,
PBOOL pSawNL,
PCHAR pIOBuf,
PULONG pIOBufIndex,
PULONG pIOBufFillSize
);
NTSTATUS
TermsrvGetString(
HANDLE SrcHandle,
PCHAR *ppStringPtr,
PULONG pStringSize,
PCHAR pIOBuf,
ULONG IOBufSize,
PULONG pIOBufIndex,
PULONG pIOBufFillSize
);
NTSTATUS
TermsrvIniCopyAndChangeLoop(
HANDLE SrcHandle,
HANDLE DestHandle,
PUNICODE_STRING pUserFullPath,
PUNICODE_STRING pSysFullPath
);
BOOL
TermsrvReallocateBuf(
PCHAR *ppStr,
PULONG pStrBufSize,
ULONG NewSize
);
PCHAR
Ctxstristr( PCHAR pstring1,
PCHAR pstring2
);
NTSTATUS
TermsrvCheckKeys(HANDLE hKeySysRoot,
HANDLE hKeyUsrRoot,
PKEY_BASIC_INFORMATION pKeySysInfo,
PKEY_FULL_INFORMATION pKeyUsrInfo,
ULONG ulcsys,
ULONG ulcusr,
DWORD indentLevel);
NTSTATUS
TermsrvCloneKey(HANDLE hKeySys,
HANDLE hKeyUsr,
PKEY_FULL_INFORMATION pDefKeyInfo,
BOOL fCreateSubKeys);
VOID
InitUnicodeStringWithLen(
OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString,
IN USHORT StringLength
);
void TermsrvCheckNewRegEntries(IN LPCWSTR wszBaseKeyName);
BOOL
TermsrvGetUserSyncTime(PULONG pultime);
BOOL
TermsrvSetUserSyncTime(void);
NTSTATUS
TermsrvCheckNewIniFilesInternal(IN LPCWSTR wszBaseKeyName);
NTSTATUS
GetFullKeyPath(
IN HANDLE hKeyParent,
IN LPCWSTR wszKey,
OUT LPWSTR *pwszKeyPath);
PWINSTATIONQUERYINFORMATIONW pWinStationQueryInformationW;
DWORD g_debugIniMap=FALSE;
DWORD IsDebugIniMapEnabled()
{
HKEY hKey;
DWORD rc;
DWORD res=0;
DWORD size;
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install",
0, KEY_READ, &hKey );
size = sizeof(DWORD);
if (rc == ERROR_SUCCESS )
{
rc = RegQueryValueEx( hKey, L"debug", NULL , NULL , (LPBYTE ) &res, & size ) ;
if (rc != ERROR_SUCCESS )
{
res = FALSE;
}
RegCloseKey( hKey );
}
return res;
}
void Indent( ULONG indent)
{
ULONG i;
for ( i = 1; i <indent ; i++ )
{
DbgPrint("%ws", L"\t");
}
}
// last param is a unicode string
void Debug1( DWORD indent, DWORD line, WCHAR *where, UNICODE_STRING *pS )
{
WCHAR s[1024];
if (g_debugIniMap)
{
wcsncpy( s, pS->Buffer, pS->Length );
s[pS->Length + 1 ] = L'\0';
Indent( indent );
DbgPrint("L: %4d, %10ws: %ws \n", line, where, s );
}
}
// last param two params, one is the wchar str and the last one is the length. Boy I miss c++ and func overloading...
void Debug2( DWORD indent, DWORD line, WCHAR *where, WCHAR *pS , DWORD length)
{
WCHAR s[1024];
if (g_debugIniMap)
{
wcsncpy( s, pS, length );
s[length + 1 ] = L'\0';
Indent( indent );
DbgPrint("L: %4d, %10ws: %ws \n", line, where, s );
}
}
void DebugTime( DWORD indent, DWORD line, WCHAR *comment, LARGE_INTEGER li )
{
if (g_debugIniMap)
{
Indent( indent );
DbgPrint("L: %4d, %5ws : %I64x \n", line , comment , li.QuadPart );
}
}
/*****************************************************************************
*
* TermsrvGetUserSyncTime
*
* This routine will get the last time we sync'd up this user's .ini files
* and registry values with the system versions.
*
* ENTRY:
* PULONG pultime: pointer to receive last sync time (in seconds since 1970)
*
* EXIT:
* SUCCESS:
* returns TRUE
* FAILURE:
* returns FALSE
*
****************************************************************************/
BOOL TermsrvGetUserSyncTime(PULONG pultime)
{
ULONG ullen, ultmp;
NTSTATUS Status;
HANDLE hKey, hKeyRoot;
OBJECT_ATTRIBUTES ObjectAttr;
PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo;
UNICODE_STRING UniString, UserSID;
PWCHAR pwch;
// Allocate a buffer for the key value name and time info
ullen = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG);
pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
ullen);
// If we didn't get the buffer, return
if (!pKeyValInfo) {
return(FALSE);
}
Status = RtlOpenCurrentUser(KEY_READ,
&hKeyRoot);
if (NT_SUCCESS(Status)) {
// Now open up the Citrix key for this user
RtlInitUnicodeString(&UniString,
USER_SOFTWARE_TERMSRV);
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
hKeyRoot,
NULL);
Status = NtCreateKey(&hKey,
KEY_READ,
&ObjectAttr,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&ultmp);
NtClose(hKeyRoot);
}
// If we opened the key, and it was already there, get the value
if (NT_SUCCESS(Status) && (ultmp == REG_OPENED_EXISTING_KEY)) {
RtlInitUnicodeString(&UniString, TERMSRV_USER_SYNCTIME);
Status = NtQueryValueKey(hKey,
&UniString,
KeyValuePartialInformation,
pKeyValInfo,
ullen,
&ultmp);
NtClose(hKey);
if (NT_SUCCESS(Status)) {
*pultime = *(PULONG)pKeyValInfo->Data;
}
} else {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo );
return(NT_SUCCESS(Status));
}
/*****************************************************************************
*
* TermsrvSetUserSyncTime
*
* This routine will set the current time as this user's last .ini file
* sync time.
*
* ENTRY:
*
* EXIT:
* SUCCESS:
* returns TRUE
* FAILURE:
* returns FALSE
*
****************************************************************************/
BOOL TermsrvSetUserSyncTime(void)
{
ULONG ultmp;
NTSTATUS Status;
HANDLE hKey, hKeyRoot;
OBJECT_ATTRIBUTES ObjectAttr;
UNICODE_STRING UniString;
PWCHAR pwch;
FILETIME FileTime;
Status = RtlOpenCurrentUser(KEY_WRITE,
&hKeyRoot);
if (NT_SUCCESS(Status)) {
// Now open up the Citrix key for this user
RtlInitUnicodeString(&UniString,
USER_SOFTWARE_TERMSRV);
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
hKeyRoot,
NULL);
Status = NtCreateKey(&hKey,
KEY_WRITE,
&ObjectAttr,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&ultmp);
NtClose(hKeyRoot);
}
// If we opened the key, and set the sync time value
if (NT_SUCCESS(Status)) {
// Get the system time, convert to local time, and convert to seconds
GetSystemTimeAsFileTime(&FileTime);
RtlTimeToSecondsSince1970((PLARGE_INTEGER)&FileTime,
&ultmp);
RtlInitUnicodeString(&UniString,
TERMSRV_USER_SYNCTIME);
// Now store it under the citrix key in the registry
Status = NtSetValueKey(hKey,
&UniString,
0,
REG_DWORD,
&ultmp,
sizeof(ultmp));
NtClose(hKey);
} else {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
return(NT_SUCCESS(Status));
}
/*****************************************************************************
*
* TermsrvCORIniFile
*
* Copy On Reference an Ini file
*
* This function is called to copy an ini file from the system
* directory to a users local ini file directory.
*
* The path supplied is the fully translated TERMSRV INI file path,
* whichs points to a users directory.
*
* This string is used to find the system ini file, and copy it to the
* users directory.
*
* All paths are NT paths, NOT WIN32 paths.
*
* Example:
*
* \DosDevices\U:\users\default\windows\win.ini is the path given
*
* %SystemRoot%\win.ini is the "default" location with ini mapping off.
*
* If \DosDevices\U:\users\default\windows\win.ini does not exist, test to see if
* %SystemRoot%\win.ini exists, and if does, copy the system version
* to the users directory.
*
* NOTE: If the path is to the normal unmapped system directory, just
* return since there is no mapping occuring.
*
* ENTRY:
* pUserFullPath (input)
* Translated TERMSRV INI path name
*
* EXIT:
*
****************************************************************************/
VOID
TermsrvCORIniFile(
PUNICODE_STRING pUserFullPath
)
{
DWORD Result;
BOOL rc;
UNICODE_STRING SysFullPath;
UNICODE_STRING UserBasePath;
/*
* If in install mode, just return to make
* everything behave as stock NT.
*/
if ( IsSystemLUID() || TermsrvAppInstallMode() ) {
TRACE0(("TermsrvCORIniFile: INI file mapping is OFF\n"));
return;
}
if (!TermsrvPerUserWinDirMapping()) {
return;
}
/*
* If a NULL file name, just return
*/
if( (pUserFullPath == NULL) || (pUserFullPath->Buffer == NULL) ) {
TRACE0(("TermsrvCORIniFile: NULL File INI file name\n"));
return;
}
/*
* Test if user file exists
*/
if( TermsrvDoesFileExist( pUserFullPath ) ) {
TRACE0(("TermsrvCORIniFile: File %ws Exists\n",pUserFullPath->Buffer));
//
// Nothing to do if the user already has a copy
//
return;
}
else {
TRACE0(("TermsrvCORIniFile: File %ws DOES NOT Exist!\n",pUserFullPath->Buffer));
}
/*
* The requested ini file does not exist in the users local
* directory. We must change the path name to point to the system
* directory, and test if the ini file exists there.
*/
/*
* Build full system path to the Ini file.
*
* This also parses out the users base path and returns that as well.
*/
if( !TermsrvBuildSysIniPath( pUserFullPath, &SysFullPath, &UserBasePath ) ) {
TRACE0(("TermsrvCORIniFile: Error building Sys Ini Path!\n"));
return;
}
/*
* Test if system version exists
*/
if( !TermsrvDoesFileExist( &SysFullPath ) ) {
//
// It does not exist in the system directory either,
// so we just return.
//
TRACE0(("TermsrvCORIniFile: Path %ws does not exist in system dir, Length %d\n",SysFullPath.Buffer,SysFullPath.Length));
TRACE0(("TermsrvCORIniFile: UserPath %ws\n",pUserFullPath->Buffer));
RtlFreeHeap( RtlProcessHeap(), 0, SysFullPath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, UserBasePath.Buffer );
return;
}
/*
* Now Copy it.
*
* The copy routine could also translate any paths internal to the
* ini file that point to the system directory, to point to the user
* directory in the base path.
*/
rc = TermsrvCopyIniFile( &SysFullPath, &UserBasePath, pUserFullPath);
#if DBG
if( !rc ) {
DBGPRINT(("TermsrvCORIniFile: Could not copy file %ws to %ws\n",SysFullPath.Buffer,pUserFullPath->Buffer));
}
#endif
RtlFreeHeap( RtlProcessHeap(), 0, SysFullPath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, UserBasePath.Buffer );
return;
}
/*****************************************************************************
*
* 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 );
}
/*****************************************************************************
*
* TermsrvBuildSysIniPath
*
* Builds the full ini path to pointing to the system directory
* from the users private ini path.
*
* Also returns the users base ini path directory to be used by
* the ini file path conversion when copying.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
BOOL
TermsrvBuildSysIniPath(
PUNICODE_STRING pUserFullPath,
PUNICODE_STRING pSysFullPath,
PUNICODE_STRING pUserBasePath
)
{
BOOL rc = FALSE;
NTSTATUS Status;
UNICODE_STRING SysBasePath;
UNICODE_STRING IniPathTail;
UNICODE_STRING UniSysDir;
WCHAR CtxWindowsPath[MAX_PATH+1];
UNICODE_STRING CtxWindowsDir = {
sizeof(CtxWindowsPath),
sizeof(CtxWindowsPath),
CtxWindowsPath
};
OBJECT_ATTRIBUTES ObjectAttr;
HKEY hKey = 0;
ULONG ul;
PKEY_VALUE_FULL_INFORMATION pKeyValInfo;
WCHAR SystemWindowsDirectory[MAX_PATH+1];
if (!TermsrvPerUserWinDirMapping()) {
return FALSE;
}
SysBasePath.Buffer = NULL;
pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH
);
if (pKeyValInfo) {
RtlInitUnicodeString(&UniSysDir,
TERMSRV_COMPAT
);
InitializeObjectAttributes(&ObjectAttr,
&UniSysDir,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttr);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString(&UniSysDir, L"SYSDIR");
Status = NtQueryValueKey(hKey,
&UniSysDir,
KeyValueFullInformation,
pKeyValInfo,
sizeof(KEY_VALUE_FULL_INFORMATION) +
MAX_PATH,
&ul
);
if (NT_SUCCESS(Status)) {
NtClose(hKey);
if (ul = wcslen((PWCHAR)((PCHAR)pKeyValInfo +
pKeyValInfo->DataOffset))) {
RtlInitUnicodeString(&UniSysDir,
(PWCHAR)((PCHAR)pKeyValInfo +
pKeyValInfo->DataOffset)
);
// Convert to an NT path
rc = RtlDosPathNameToNtPathName_U(
UniSysDir.Buffer,
&SysBasePath,
NULL,
NULL
);
// Was this a valid path? If not, use actual system directory.
if (rc && !TermsrvDoesFileExist(&SysBasePath)) {
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
SysBasePath.Buffer = NULL;
rc = FALSE;
}
// if the path is the root, get rid of last backslash
if (ul == 3 && SysBasePath.Buffer) {
SysBasePath.Buffer[SysBasePath.Length/sizeof(WCHAR)] = L'\0';
SysBasePath.Length -= 2;
}
}
}
}
}
GetSystemWindowsDirectory(SystemWindowsDirectory,(MAX_PATH * sizeof(WCHAR)));
if (!rc) {
/*
* We must convert the SystemWindowsDirectory from a WIN32 path to
* an NT path.
*/
rc = RtlDosPathNameToNtPathName_U( SystemWindowsDirectory,
&SysBasePath,
NULL,
NULL
);
}
if (pKeyValInfo) {
RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo );
}
TRACE0(("BaseWindowsDirectory is %ws\n",SystemWindowsDirectory));
if( !rc ) {
DBGPRINT(("BuildSysIniPath: Error translating system path to NT path %ws\n",SystemWindowsDirectory));
return( FALSE );
}
TRACE0(("BuildSysIniPath: NT SYS path is %ws\n",SysBasePath.Buffer));
/*
* Get the users windows path prefix
*/
Status = GetPerUserWindowsDirectory( &CtxWindowsDir );
if( !NT_SUCCESS( Status ) ) {
DBGPRINT(("BuildSysIniPath: Could not get TermsrvWindowsDir 0x%x\n",Status));
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
return( FALSE );
}
/*
* Now convert it into an NT path
*/
rc = RtlDosPathNameToNtPathName_U(
CtxWindowsDir.Buffer,
pUserBasePath,
NULL,
NULL
);
if( !rc ) {
DBGPRINT(("BuildSysIniPath: Could not convert TermsrvWindowsDir %d\n",rc));
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
return( FALSE );
}
TRACE0(("BuildSysIniPath: Users Ini PathBase is %ws\n",pUserBasePath->Buffer));
//
// Here we have:
//
// SysBasePath, UserBasePath
//
// UserFullPath, must now build SysFullPath
//
rc = TermsrvGetUnicodeRemainder( pUserFullPath, pUserBasePath, &IniPathTail );
if( !rc ) {
WCHAR szShortPath[MAX_PATH];
WCHAR szPath[MAX_PATH];
UNICODE_STRING ShortPath;
//
// GetShortPathName doesn't take NT Path. Strip out "\??\"
//
if (!wcsncmp(pUserBasePath->Buffer,L"\\??\\",4)) {
wcsncpy(szPath,&(pUserBasePath->Buffer[4]),(pUserBasePath->Length - 4));
} else {
wcsncpy(szPath,pUserBasePath->Buffer,pUserBasePath->Length);
}
if (GetShortPathNameW(szPath,szShortPath,MAX_PATH)) {
if (!wcsncmp(pUserBasePath->Buffer,L"\\??\\",4)) {
wcscpy(szPath,L"\\??\\");
wcscat(szPath,szShortPath);
} else {
wcscpy(szPath,szShortPath);
}
RtlInitUnicodeString(&ShortPath,szPath);
rc = TermsrvGetUnicodeRemainder( pUserFullPath, &ShortPath, &IniPathTail );
}
if (!rc) {
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, pUserBasePath->Buffer );
return( FALSE );
}
}
pSysFullPath->Length = 0;
pSysFullPath->MaximumLength = (MAX_PATH+1)*sizeof(WCHAR);
pSysFullPath->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, pSysFullPath->MaximumLength );
if( pSysFullPath->Buffer == NULL ) {
DBGPRINT(("BuildSysPath: Error in memory allocate\n"));
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, IniPathTail.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, pUserBasePath->Buffer );
return( FALSE );
}
TRACE0(("BuildSysPath: IniPathTail :%ws:, Length %d\n",IniPathTail.Buffer,IniPathTail.Length));
RtlCopyUnicodeString( pSysFullPath, &SysBasePath );
if ((pSysFullPath->Buffer[pSysFullPath->Length/sizeof(WCHAR) -1 ] != L'\\') &&
(IniPathTail.Buffer[0] != L'\\')) { // check whether need "\\"
Status = RtlAppendUnicodeToString(pSysFullPath, L"\\");
if ( !NT_SUCCESS( Status) ) {
DBGPRINT(("BuildSysPath: Error appending UnicodeStirng\n",Status));
return( FALSE );
}
}
Status = RtlAppendUnicodeStringToString( pSysFullPath, &IniPathTail );
if( !NT_SUCCESS( Status ) ) {
DBGPRINT(("BuildSysPath: Error 0x%x appending UnicodeString\n",Status));
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, IniPathTail.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, pUserBasePath->Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, pSysFullPath->Buffer );
return( FALSE );
}
TRACE0(("BuildSysPath: SysFullPath :%ws:, Length %d\n",pSysFullPath->Buffer,pSysFullPath->Length));
/*
* Free the local resources allocated
*/
RtlFreeHeap( RtlProcessHeap(), 0, SysBasePath.Buffer );
RtlFreeHeap( RtlProcessHeap(), 0, IniPathTail.Buffer );
return( TRUE );
}
/*****************************************************************************
*
* TermsrvGetUnicodeRemainder
*
* Given the full path, and a prefix, return the remainder of
* the UNICODE_STRING in newly allocated buffer space.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* TRUE - no error
* FALSE - error
*
****************************************************************************/
BOOL
TermsrvGetUnicodeRemainder(
PUNICODE_STRING pFullPath,
PUNICODE_STRING pPrefix,
PUNICODE_STRING pRemainder
)
{
WCHAR c1, c2;
USHORT Index, RemIndex;
USHORT PathLen, PrefixLen, RemLen;
PathLen = pFullPath->Length / sizeof(WCHAR);
PrefixLen = pPrefix->Length / sizeof(WCHAR);
if( (PathLen == 0) || (PrefixLen == 0) ) {
TRACE1(("TermsrvGetUnicodeRemainder: 0 PathLength Full %d, Prefix %d\n",PathLen,PrefixLen));
return( FALSE );
}
Index = 0;
while( PathLen && PrefixLen ) {
c1 = pFullPath->Buffer[Index];
c2 = pPrefix->Buffer[Index];
// Do a fast case insensitive compare
if( (c1 != c2) && (towupper(c1) != towupper(c2)) ) {
TRACE1(("TermsrvGetUnicodeRemainder: Non matching character Index %d\n",Index));
return( FALSE );
}
PathLen--;
PrefixLen--;
Index++;
}
// If prefix is longer, its an error
if( PrefixLen ) {
TRACE1(("TermsrvGetUnicodeRemainder: Prefix is longer\n"));
return(FALSE);
}
// If PathLen is 0, there is no remainder.
if( PathLen == 0 ) {
RemLen = 0;
}
else {
RemLen = PathLen;
}
// Allocate memory for remainder, including a UNICODE_NULL
pRemainder->Length = RemLen*sizeof(WCHAR);
pRemainder->MaximumLength = (RemLen+1)*sizeof(WCHAR);
pRemainder->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, pRemainder->MaximumLength );
if( pRemainder->Buffer == NULL ) {
TRACE1(("TermsrvGetUnicodeRemainder: Memory allocation error\n"));
return( FALSE );
}
RemIndex = 0;
while( RemLen ) {
pRemainder->Buffer[RemIndex] = pFullPath->Buffer[Index];
Index++;
RemIndex++;
RemLen--;
}
// Now include the UNICODE_NULL
pRemainder->Buffer[RemIndex] = UNICODE_NULL;
TRACE0(("TermsrvGetUnicodeRemainder: Remainder %ws\n",pRemainder->Buffer));
return( TRUE );
}
/*****************************************************************************
*
* TermsrvCopyIniFile
*
* Copies the INI file from the system directory to the
* users directory.
*
* Any paths inside the INI file that match pUserBasePath and do not point
* to a shareable application resource will be translated.
*
* ENTRY:
* PUNICODE_STRING pSysFullPath (In) - Path of ini file in system dir (source)
* PUNICODE_STRING pUserBasePath (In) - Optional, User's windows home dir
* PUNICODE_STRING pUserFullPath (In) - Path of ini file in user's home dir (dest)
*
* Notes:
* If pUserBasePath is NULL, no path substitution is done as the ini file is
* copied from the system directory to the user's home directory.
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
BOOL
TermsrvCopyIniFile(
PUNICODE_STRING pSysFullPath,
PUNICODE_STRING pUserBasePath,
PUNICODE_STRING pUserFullPath
)
{
NTSTATUS Status;
HANDLE SrcHandle, DestHandle;
OBJECT_ATTRIBUTES SrcObja;
OBJECT_ATTRIBUTES DestObja;
IO_STATUS_BLOCK SrcIosb;
IO_STATUS_BLOCK DestIosb;
PWCHAR pwch, pwcIniName;
ULONG ulCompatFlags;
TRACE0(("TermsrvCopyIniFile: From %ws, TO -> %ws\n",pSysFullPath->Buffer,pUserFullPath->Buffer));
TRACE0(("UserBasePath %ws\n",pUserBasePath->Buffer));
/*
* This must all be done at the NT level
*/
InitializeObjectAttributes(
&SrcObja,
pSysFullPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
InitializeObjectAttributes(
&DestObja,
pUserFullPath,
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 // OpenOptions
);
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = SrcIosb.Status;
}
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("TermsrvCopyIniFile: Error 0x%x opening SrcFile %ws\n",Status,pSysFullPath->Buffer));
return( FALSE );
}
// Create the destination file
DestIosb.Status = STATUS_SUCCESS;
Status = NtCreateFile(
&DestHandle,
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&DestObja,
&DestIosb,
NULL, // Allocation size
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
FILE_SHARE_WRITE, // dwShareMode
FILE_OVERWRITE_IF, // CreateDisposition
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, // CreateFlags
NULL, // EaBuffer
0 // EaLength
);
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = DestIosb.Status;
}
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("TermsrvCopyIniFile: Error 0x%x Creating DestFile %ws\n",Status,pUserFullPath->Buffer));
NtClose( SrcHandle );
return( FALSE );
}
TRACE0(("TermsrvCopyFile: Create Disposition 0x%x\n",DestIosb.Information));
// Get the ini file name
pwch = wcsrchr(pSysFullPath->Buffer, L'\\') + 1;
pwcIniName = RtlAllocateHeap( RtlProcessHeap(),
0,
(wcslen(pwch) + 1)*sizeof(WCHAR));
if(!pwcIniName)
{
DBGPRINT(("TermsrvCopyIniFile: Error Allocating pwcIniName\n"));
NtClose( SrcHandle );
NtClose( DestHandle );
return( FALSE );
}
wcscpy(pwcIniName, pwch);
pwch = wcsrchr(pwcIniName, L'.');
if (pwch) {
*pwch = L'\0';
}
GetTermsrCompatFlags(pwcIniName, &ulCompatFlags, CompatibilityIniFile);
RtlFreeHeap( RtlProcessHeap(), 0, pwcIniName );
/*
* Now do the copy loop
*/
if (pUserBasePath && !(ulCompatFlags & TERMSRV_COMPAT_ININOSUB)) {
Status = TermsrvIniCopyAndChangeLoop( SrcHandle,
DestHandle,
pUserBasePath,
pSysFullPath
);
} else {
Status = TermsrvIniCopyLoop( SrcHandle, DestHandle );
}
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("TermsrvCopyIniFile: Error 0x%x Doing copy loop\n",Status));
NtClose( SrcHandle );
NtClose( DestHandle );
return( FALSE );
}
/*
* Close the file handles
*/
NtClose( SrcHandle );
NtClose( DestHandle );
return( TRUE );
}
/*****************************************************************************
*
* TermsrvIniCopyLoop
*
* Actual copy loop. This copies the src ini file to the destination
* ini file.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvIniCopyLoop(
HANDLE SrcHandle,
HANDLE DestHandle
)
{
NTSTATUS Status;
PCHAR pBuf = NULL;
IO_STATUS_BLOCK Iosb;
pBuf = LocalAlloc( LPTR, INI_BUF_SIZE );
if ( !pBuf ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
while( 1 ) {
Iosb.Status = STATUS_SUCCESS;
Status = NtReadFile(
SrcHandle,
NULL, // Event
NULL, // APC routine
NULL, // APC context
&Iosb,
pBuf,
INI_BUF_SIZE,
NULL, // ByteOffset (not used since in synchronous I/O)
NULL // Key
);
if( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( SrcHandle, FALSE, NULL );
}
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
if( !NT_SUCCESS(Status) ) {
if( Status == STATUS_END_OF_FILE ) {
Status = STATUS_SUCCESS;
goto Cleanup;
}
DBGPRINT(("TermsrvIniCopyLoop: Error 0x%x doing NtReadFile\n",Status));
goto Cleanup;
}
Iosb.Status = STATUS_SUCCESS;
Status = NtWriteFile(
DestHandle,
NULL, // Event
NULL, // APC routine
NULL, // APC context
&Iosb,
pBuf,
(ULONG)Iosb.Information, // Actual amount read
NULL, // ByteOffset (not used since in synchronous I/O)
NULL // Key
);
if( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( DestHandle, FALSE, NULL );
}
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("TermsrvIniCopyLoop: Error 0x%x doing NtWriteFile\n",Status));
goto Cleanup;
}
} // end while(1)
Cleanup:
if ( pBuf ) {
LocalFree( pBuf );
}
return( Status );
}
/*****************************************************************************
*
* TermsrvIniCopyAndChangeLoop
*
* Actual copy loop. This copies the src ini file to the destination
* ini file. It also handles any path translations.
*
* ENTRY:
* HANDLE SrcHandle (In) - Source file handle
* HANDLE DestHandle (In) - Destination file handle
* PUNICODE_STRING pUserFullPath (In) - Ptr to Uni string with user's home
* windows dir
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvIniCopyAndChangeLoop(
HANDLE SrcHandle,
HANDLE DestHandle,
PUNICODE_STRING pUserFullPath,
PUNICODE_STRING pSysFullPath
)
{
PCHAR pStr, pch, ptemp, pnext;
PWCHAR pwch;
NTSTATUS Status;
ULONG StringSize;
CHAR IOBuf[512];
ULONG IOBufSize = 512;
ULONG IOBufIndex = 0;
ULONG IOBufFillSize = 0;
ANSI_STRING AnsiUserDir, AnsiSysDir;
UNICODE_STRING UniString;
// Get the DOS filename from the NT file name
if (pwch = wcschr(pUserFullPath->Buffer, L':')) {
pwch--;
} else {
pwch = pUserFullPath->Buffer;
}
RtlInitUnicodeString( &UniString, pwch );
Status = RtlUnicodeStringToAnsiString( &AnsiUserDir,
&UniString,
TRUE
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("TermsrvIniCopyAndChangeLoop: Error 0x%x converting user dir\n", Status));
return(Status);
}
// Get the system directory from the fully qualified system path
if (pwch = wcschr(pSysFullPath->Buffer, L':')) {
pwch--;
} else {
pwch = pUserFullPath->Buffer;
}
RtlInitUnicodeString( &UniString, pwch );
Status = RtlUnicodeStringToAnsiString( &AnsiSysDir,
&UniString,
TRUE
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("TermsrvIniCopyAndChangeLoop: Error 0x%x converting system dir\n", Status));
RtlFreeAnsiString( &AnsiUserDir );
return(Status);
}
pch = strrchr(AnsiSysDir.Buffer, '\\');
// unless something has gone wrong, we should always have a pch since a full-path always
// has at least "\" in it, and actually in our case, we have atleast two slashes inside,
// since we are dealing with a string such as "\A\file.ini", where 'A' is a folder
// name that has at least one letter in it
if (pch)
{
if ((pch - AnsiSysDir.Buffer) > 2) {
*pch = '\0';
} else {
*(pch+1) = '\0';
}
AnsiSysDir.Length = (USHORT) strlen(AnsiSysDir.Buffer);
while( 1 ) {
pStr = NULL;
StringSize = 0;
/*
* Get a string from the source ini file
*/
Status = TermsrvGetString(
SrcHandle,
&pStr,
&StringSize,
IOBuf,
IOBufSize,
&IOBufIndex,
&IOBufFillSize
);
if( !NT_SUCCESS(Status) ) {
ASSERT( pStr == NULL );
RtlFreeAnsiString( &AnsiUserDir );
RtlFreeAnsiString( &AnsiSysDir );
if( Status == STATUS_END_OF_FILE ) {
return( STATUS_SUCCESS );
}
return( Status );
}
/*
* Process the string for any ini path translations
*/
ASSERT( pStr != NULL );
// Go through the string looking for anything that contains the system
// directory.
if (pch = Ctxstristr(pStr, AnsiSysDir.Buffer)) {
// See if this entry might point to an ini file
if ((ptemp = strchr(pch, '.')) && !(_strnicmp(ptemp, ".ini", 4))) {
// Check to make sure this is the right string to replace
pnext = pch + AnsiSysDir.Length + 1;
while (pch && (pnext < ptemp)) {
// Check for another entry
if (*pnext == ',') {
pch = Ctxstristr(pnext, AnsiSysDir.Buffer);
if (pch) {
pnext = pch + AnsiSysDir.Length + 1;
}
}
pnext++;
}
// Check that this .ini is in the system directory
pnext = pch + AnsiSysDir.Length + 1;
while (pch && (pnext < ptemp)) {
if (*pnext == '\\') {
pch = NULL;
}
pnext++;
}
if (pch && (pch < ptemp)) {
ptemp = RtlAllocateHeap( RtlProcessHeap(),
0,
StringSize + AnsiUserDir.Length );
strncpy(ptemp, pStr, (size_t)(pch - pStr)); // copy up to sys dir
ptemp[pch - pStr] = '\0';
strcat(ptemp, AnsiUserDir.Buffer); // subst user dir
if (AnsiSysDir.Length == 3) {
strcat(ptemp, "\\");
}
strcat(ptemp, pch + AnsiSysDir.Length); // append rest of line
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
StringSize = strlen(ptemp);
pStr = ptemp;
}
}
}
/*
* Write out the translated string
*/
Status = TermsrvPutString(
DestHandle,
pStr,
StringSize
);
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("TermsrvIniCopyLoop: Error 0x%x doing NtWriteFile\n",Status));
RtlFreeAnsiString( &AnsiUserDir );
RtlFreeAnsiString( &AnsiSysDir );
return( Status );
}
} // end while(1)
}
else
{
return STATUS_UNSUCCESSFUL;
}
}
/*****************************************************************************
*
* TermsrvGetString
*
* This function gets a "string" from an ini file and returns it to the
* caller. Since processing must be done in memory on the strings, they
* are returned NULL terminated, but this NULL is NOT included in the
* returned string size. Of course, buffer size calculations take this
* NULL into account. Strings retain any <CR><LF> characters and are
* not stripped out like the C runtime.
*
* The I/O buffer used is passed in by the caller. If the IoBufIndex is
* not 0, this is an indication that there is still data left in the buffer
* from a previous operation. This data is used before reading additional
* data from the file handle. This handles the case where string breaks
* do not occur at buffer boundries.
*
* Strings are returned in newly allocated memory on the process heap.
* The caller is reponsible for freeing them when done.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvGetString(
HANDLE SrcHandle,
PCHAR *ppStringPtr,
PULONG pStringSize,
PCHAR pIOBuf,
ULONG IOBufSize,
PULONG pIOBufIndex,
PULONG pIOBufFillSize
)
{
NTSTATUS Status;
IO_STATUS_BLOCK Iosb;
BOOL SawNL = FALSE;
ULONG StrSize = 0;
ULONG StrBufSize = 512;
PCHAR pStr = NULL;
/*
* first process any left over data in the current I/O buffer
*/
if( *pIOBufIndex < *pIOBufFillSize ) {
Status = TermsrvProcessBuffer(
&pStr,
&StrSize,
&StrBufSize,
&SawNL,
pIOBuf,
pIOBufIndex,
pIOBufFillSize
);
if( Status == STATUS_SUCCESS ) {
*ppStringPtr = pStr;
*pStringSize = StrSize;
return( STATUS_SUCCESS );
}
else if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
/*
* emptied the buffer
*/
*pIOBufIndex = 0;
*pIOBufFillSize = 0;
// fall through to read more data
}
else {
// Error
if( pStr ) {
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
}
*ppStringPtr = NULL;
*pStringSize = 0;
return( Status );
}
}
while( 1 ) {
ASSERT( *pIOBufIndex == 0 );
ASSERT( *pIOBufFillSize == 0 );
Iosb.Status = STATUS_SUCCESS;
Status = NtReadFile(
SrcHandle,
NULL, // Event
NULL, // APC routine
NULL, // APC context
&Iosb,
pIOBuf,
IOBufSize,
NULL, // ByteOffset (not used since in synchronous I/O)
NULL // Key
);
if( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( SrcHandle, FALSE, NULL );
}
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
if( !NT_SUCCESS(Status) ) {
if( (Status == STATUS_END_OF_FILE) && (StrSize != 0) ) {
// Force the string finished
pStr[StrSize] = (CHAR)NULL;
*pStringSize = StrSize;
*ppStringPtr = pStr;
return( STATUS_SUCCESS );
}
// Free the buffer
if( pStr ) {
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
}
*ppStringPtr = NULL;
*pStringSize = 0;
if (Status != STATUS_END_OF_FILE)
DBGPRINT(("TermsrvIniCopyLoop: Error 0x%x doing NtReadFile\n",Status));
return( Status );
}
// Fill in the count
*pIOBufFillSize = (ULONG)Iosb.Information;
/*
* Now process this buffer of data
*/
Status = TermsrvProcessBuffer(
&pStr,
&StrSize,
&StrBufSize,
&SawNL,
pIOBuf,
pIOBufIndex,
pIOBufFillSize
);
if( Status == STATUS_SUCCESS ) {
*ppStringPtr = pStr;
*pStringSize = StrSize;
return( STATUS_SUCCESS );
}
else if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
/*
* emptied the buffer
*/
*pIOBufIndex = 0;
*pIOBufFillSize = 0;
// fall through to read more data
}
else {
// Error
if( pStr ) {
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
}
*ppStringPtr = NULL;
*pStringSize = 0;
return( Status );
}
} // end while(1)
}
/*****************************************************************************
*
* TermsrvProcessBuffer
*
* Process a buffer of data.
*
* This uses state passed in by the caller since the string can be
* partially built, and the buffer may not be fully processed when
* a string completes.
*
* Can return if it completes a string with data still in buffer.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvProcessBuffer(
PCHAR *ppStr,
PULONG pStrSize,
PULONG pStrBufSize,
PBOOL pSawNL,
PCHAR pIOBuf,
PULONG pIOBufIndex,
PULONG pIOBufFillSize
)
{
PCHAR pStr;
ULONG Index;
BOOL SawNL;
/*
* See if we are starting a new string
*/
if( *ppStr == NULL ) {
pStr = RtlAllocateHeap( RtlProcessHeap(), 0, *pStrBufSize );
if( pStr == NULL ) {
DBGPRINT(("TermsrvProcessBuf: Memory allocation failure\n"));
return( STATUS_NO_MEMORY );
}
// Set it to our caller
*ppStr = pStr;
}
/*
* Get passed in state to local variables
*/
pStr = *ppStr;
Index = *pStrSize;
SawNL = *pSawNL;
while ( *pIOBufIndex < *pIOBufFillSize ) {
pStr[Index] = pIOBuf[*pIOBufIndex];
if( IS_NEWLINE_CHAR( pStr[Index] ) ) {
/*
* Mark the we saw an end of string character.
* We will keep putting them into the buffer until a
* non-NL character is encountered. This handles the
* variations for <CR><LF>, <CR> alone, or <CR><LF><CR>
* if its been mangled by a buggy editor.
*/
SawNL = TRUE;
}
else {
/*
* If we saw a previous NL character, and this character
* is not one, we do not take this one, but put a NULL in
* its place and return. NOTE: Do not bump the count, since
* the count does not include the NULL.
*/
if( SawNL ) {
pStr[Index] = (CHAR)NULL;
*pStrSize = Index;
return( STATUS_SUCCESS );
}
}
Index++;
(*pIOBufIndex)++;
if( Index >= *pStrBufSize ) {
// Grow the string buffer
if( !TermsrvReallocateBuf( &pStr, pStrBufSize, (*pStrBufSize) * 2 ) ) {
if( pStr ) {
RtlFreeHeap( RtlProcessHeap(), 0, pStr );
}
*ppStr = NULL;
DBGPRINT(("TermsrvIniCopyLoop: Memory re-allocation failure\n"));
return( STATUS_NO_MEMORY );
}
// Memory buffer has been re-allocated
*ppStr = pStr;
*pStrBufSize = (*pStrBufSize) * 2;
}
}
*pStrSize = Index;
*pSawNL = SawNL;
/*
* emptied the buffer without building a whole string
*/
return( STATUS_MORE_PROCESSING_REQUIRED );
}
/*****************************************************************************
*
* TermsrvReallocateBuf
*
* Grow the buffer, copy data to new buffer.
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
BOOL
TermsrvReallocateBuf(
PCHAR *ppStr,
PULONG pStrBufSize,
ULONG NewSize
)
{
PCHAR ptr;
ULONG CopyCount;
CopyCount = *pStrBufSize;
ptr = RtlAllocateHeap( RtlProcessHeap(), 0, NewSize );
if( ptr == NULL ) {
return( FALSE );
}
RtlMoveMemory( ptr, *ppStr, CopyCount );
RtlFreeHeap( RtlProcessHeap(), 0, *ppStr );
*ppStr = ptr;
return( TRUE );
}
/*****************************************************************************
*
* TermsrvPutString
*
* Write out the current string to the destination file handle
*
* ENTRY:
* Param1 (input/output)
* Comments
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
TermsrvPutString(
HANDLE DestHandle,
PCHAR pStr,
ULONG StringSize
)
{
NTSTATUS Status;
IO_STATUS_BLOCK Iosb;
Iosb.Status = STATUS_SUCCESS;
Status = NtWriteFile(
DestHandle,
NULL, // Event
NULL, // APC routine
NULL, // APC context
&Iosb,
pStr,
StringSize,
NULL, // ByteOffset (not used since in synchronous I/O)
NULL // Key
);
if( Status == STATUS_PENDING ) {
Status = NtWaitForSingleObject( DestHandle, FALSE, NULL );
}
if( NT_SUCCESS(Status) ) {
// Get final I/O status
Status = Iosb.Status;
}
return( Status );
}
/*****************************************************************************
*
* TermsrvCheckNewIniFiles
*
* This routine will check the timestamps of the .ini files installed by the
* system administrator, and see if any of the user's .ini file are out of
* date, and if so, they will be renamed.
*
* ENTRY:
*
* EXIT:
* No return value.
*
****************************************************************************/
void TermsrvCheckNewIniFiles(void)
{
NTSTATUS Status;
#if defined (_WIN64)
Status = TermsrvCheckNewIniFilesInternal(REG_NTAPI_SOFTWARE_WOW6432_TSERVER);
if (!NT_SUCCESS(Status)) {
return;
}
#endif // defined(_WIN64)
Status = TermsrvCheckNewIniFilesInternal(REG_NTAPI_SOFTWARE_TSERVER);
if (!NT_SUCCESS(Status)) {
return;
}
// Update the user's sync time in the registry
TermsrvSetUserSyncTime();
}
/*****************************************************************************
*
* TermsrvCheckNewIniFilesInternal
*
* This routine will check the timestamps of the .ini files installed by the
* system administrator, and see if any of the user's .ini file are out of
* date, and if so, they will be renamed.
*
* ENTRY:
* LPCWSTR wszBaseKeyName
*
* EXIT:
* No return value.
*
****************************************************************************/
NTSTATUS
TermsrvCheckNewIniFilesInternal(
IN LPCWSTR wszBaseKeyName)
{
PWCHAR pwch;
UNICODE_STRING UniString, UniUserDir, UniNTDir = {0,0,NULL};
OBJECT_ATTRIBUTES ObjectAttr;
FILE_NETWORK_OPEN_INFORMATION BasicInfo;
HANDLE hKey = NULL, hWinDir = NULL;
NTSTATUS Status;
ULONG ulcnt, ullen, ultmp;
WCHAR wcWinDir[MAX_PATH], wcbuff[MAX_PATH];
PKEY_VALUE_FULL_INFORMATION pKeyValInfo;
PKEY_BASIC_INFORMATION pKeyInfo;
IO_STATUS_BLOCK IOStatus;
g_debugIniMap = IsDebugIniMapEnabled();
// Allocate a buffer for the key value name and time info
ullen = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) +
sizeof(ULONG);
pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
ullen);
// If we didn't get the buffer, return
if (!pKeyValInfo) {
return STATUS_NO_MEMORY;
}
// Open up the registry key to get the last sync time for this user
wcscpy(wcbuff,wszBaseKeyName);
wcscat(wcbuff,TERMSRV_INIFILE_TIMES_SHORT);
RtlInitUnicodeString(&UniString,
wcbuff);
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttr);
// If we successfully opened the key, check if there are any new entries
if (NT_SUCCESS(Status)) {
// Since we already allocated a hunk of memory, use the value buffer
// for the key info query
pKeyInfo = (PKEY_BASIC_INFORMATION)pKeyValInfo;
// Get the last time anyone wrote to "IniFile Times" key
Status = NtQueryKey(hKey,
KeyBasicInformation,
pKeyInfo,
ullen,
&ultmp);
// We got the last write time OK, now get the last time we sync'd
if (NT_SUCCESS(Status) && TermsrvGetUserSyncTime(&ultmp)) {
// Convert the time value to seconds since 1970
RtlTimeToSecondsSince1970 (&pKeyInfo->LastWriteTime,
&ulcnt);
// If no .ini files or reg entries have been updated since the last
// time we sync'd this user, just return
if (ultmp >= ulcnt) {
NtClose(hKey);
RtlFreeHeap(RtlProcessHeap(), 0, pKeyValInfo);
return STATUS_SUCCESS;
}
}
TermsrvCheckNewRegEntries(wszBaseKeyName);
// Set up UniUserDir to point at wcbuff
UniUserDir.Buffer = wcWinDir;
UniUserDir.Length = 0;
UniUserDir.MaximumLength = sizeof(wcbuff);
Status = GetPerUserWindowsDirectory(&UniUserDir);
if (NT_SUCCESS(Status)) {
// Convert to an NT path
if (RtlDosPathNameToNtPathName_U(UniUserDir.Buffer,
&UniNTDir,
NULL,
NULL)) {
InitializeObjectAttributes(&ObjectAttr,
&UniNTDir,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// Open the user's windows directory
IOStatus.Status = STATUS_SUCCESS;
Status = NtOpenFile(&hWinDir,
FILE_GENERIC_READ,
&ObjectAttr,
&IOStatus,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
} else {
Status = STATUS_NO_SUCH_FILE;
}
}
// Go through each of the keys, checking if it's newer than the user's
// version of the file, and if so rename it
ulcnt = 0;
wcscat(wcWinDir, L"\\");
UniUserDir.Length += 2; // add in length of \ seperator
while (NT_SUCCESS(Status)) {
Status = NtEnumerateValueKey(hKey,
ulcnt++,
KeyValueFullInformation,
pKeyValInfo,
ullen,
&ultmp);
if (NT_SUCCESS(Status)) {
RtlMoveMemory(wcbuff, pKeyValInfo->Name, pKeyValInfo->NameLength);
wcbuff[pKeyValInfo->NameLength/sizeof(WCHAR)] = L'\0';
// Get rid of the .ini extension
if (pwch = wcschr(wcbuff, L'.')) {
*pwch = L'\0';
}
// Get the compatibility flags for this .ini file
GetTermsrCompatFlags(wcbuff,
&ultmp,
CompatibilityIniFile);
// If we removed the extension, put it back
if (pwch) {
*pwch = '.';
}
// If the INISYNC compat bit is set, don't rename the file
if ((ultmp & (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) !=
(TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) {
RtlInitUnicodeString(&UniString, wcbuff);
// Query the last write time of the .ini file
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
hWinDir,
NULL);
// Get the last write time
if (NT_SUCCESS(NtQueryFullAttributesFile( &ObjectAttr,
&BasicInfo ))) {
// Convert the last write time to seconds
RtlTimeToSecondsSince1970(&BasicInfo.LastWriteTime,
&ultmp);
// Check if the system version is newer than the user's
// version
if (*(PULONG)((PCHAR)pKeyValInfo +
pKeyValInfo->DataOffset) > ultmp) {
// Concatenate the .ini name onto the user's path
wcscpy(wcWinDir + (UniUserDir.Length/sizeof(WCHAR)),
wcbuff);
// Create the target name to rename the file
// (inifile.ctx)
wcscpy(wcbuff, wcWinDir);
pwch = wcsrchr(wcbuff, L'.');
if (pwch) {
wcscpy(pwch, L".ctx");
} else {
wcscat(pwch, L".ctx");
}
// Rename the .ini file
MoveFileExW(wcWinDir,
wcbuff,
MOVEFILE_REPLACE_EXISTING);
}
}
}
}
}
// Close the handles, if they were opened
if (hKey) {
NtClose(hKey);
}
if (hWinDir) {
NtClose(hWinDir);
}
}
// Free the memory we allocated
RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo );
if (UniNTDir.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, UniNTDir.Buffer);
}
return STATUS_SUCCESS;
}
/*****************************************************************************
*
* TermsrvCheckKeys
*
* This recursive routine will check for any subkeys under the system root
* key passed in. It will delete the corresponding key in the user's
* software registry if the user's key is older than the system key. If the
* INISYNC bit is set for this registry key or the key is still has subkeys,
* it won't be deleted. If the key doesn't exist in the user's registry,
* it will be added.
*
* ENTRY:
* HANDLE hKeySysRoot: handle to key in system section of registry
* HANDLE hKeyUsrRoot: handle to key in user section of registry
* PKEY_BASIC_INFORMATION pKeySysInfo: Ptr to buffer for key basic info struc
* PKEY_FULL_INFORMATION pKeyUsrInfo: Ptr to buffer for full key info struc
* ULONG ulcsys: Size of SysInfo buffer
* ULONG ulcusr: Size of UsrInfo buffer
*
* EXIT:
* SUCCESS:
* STATUS_SUCCESS
* FAILURE:
* NTSTATUS Return Code
*
****************************************************************************/
NTSTATUS TermsrvCheckKeys(HANDLE hKeySysRoot,
HANDLE hKeyUsrRoot,
PKEY_BASIC_INFORMATION pKeySysInfo,
PKEY_FULL_INFORMATION pKeyUsrInfo,
ULONG ulcsys,
ULONG ulcusr,
DWORD indentLevel )
{
NTSTATUS Status = STATUS_SUCCESS, Status2;
ULONG ultemp, ulcnt = 0;
UNICODE_STRING UniPath, UniString;
OBJECT_ATTRIBUTES ObjAttr;
HANDLE hKeyUsr = NULL, hKeySys = NULL;
LPWSTR wcbuff = NULL;
ULONG aulbuf[4];
PKEY_FULL_INFORMATION pKeyUsrFullInfoSaved = NULL ;
ULONG sizeFullInfo;
PKEY_VALUE_PARTIAL_INFORMATION pValKeyInfo =
(PKEY_VALUE_PARTIAL_INFORMATION)aulbuf;
++indentLevel;
Status = GetFullKeyPath(hKeyUsrRoot,NULL,&wcbuff);
if(!NT_SUCCESS(Status)) {
return Status;
}
// Get the compatibility flags for this entry
GetTermsrCompatFlags(wcbuff,
&ultemp,
CompatibilityRegEntry);
LocalFree(wcbuff);
// If the INISYNC or NOREGMAP bits are set for this entry,
// return, since there's nothing to do
if ((ultemp & TERMSRV_COMPAT_WIN32) &&
(ultemp & (TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_INISYNC))) {
return(STATUS_NO_MORE_ENTRIES);
}
// Save the current info for the current user key
// @@@
if (!hKeyUsrRoot)
{
DBGPRINT(("ERROR : LINE : %4d, why is this null? \n", __LINE__ ));
return(STATUS_NO_MORE_ENTRIES );
}
// use a zero length query to get the actual length
Status = NtQueryKey(hKeyUsrRoot,
KeyFullInformation,
pKeyUsrFullInfoSaved,
0,
&ultemp) ;
if ( !NT_SUCCESS( Status ) )
{
if (Status == STATUS_BUFFER_TOO_SMALL )
{
sizeFullInfo = ultemp;
pKeyUsrFullInfoSaved = RtlAllocateHeap(RtlProcessHeap(), 0, sizeFullInfo );
if ( ! pKeyUsrFullInfoSaved )
{
return STATUS_NO_MEMORY;
}
Status = NtQueryKey(hKeyUsrRoot,
KeyFullInformation,
pKeyUsrFullInfoSaved,
sizeFullInfo,
&ultemp);
if( !NT_SUCCESS(Status ) )
{
DBGPRINT(("ERROR : LINE : %4d, Status =0x%lx , ultemp=%d\n", __LINE__ , Status, ultemp));
RtlFreeHeap( RtlProcessHeap(), 0, pKeyUsrFullInfoSaved);
return( Status );
}
}
else
{
DBGPRINT(("ERROR : LINE : %4d, Status =0x%lx \n", __LINE__ , Status ));
return Status;
}
}
// Go through each of the subkeys, checking for user keys that are older
// than the system version of the keys
while (NT_SUCCESS(Status)) {
Status = NtEnumerateKey(hKeySysRoot,
ulcnt++,
KeyBasicInformation,
pKeySysInfo,
ulcsys,
&ultemp);
// See if there are any user keys under this key that are out of date
if (NT_SUCCESS(Status)) {
// Null terminate the key name
pKeySysInfo->Name[pKeySysInfo->NameLength/sizeof(WCHAR)] = L'\0';
// Create a unicode string for the key name
RtlInitUnicodeString(&UniPath, pKeySysInfo->Name);
InitializeObjectAttributes(&ObjAttr,
&UniPath,
OBJ_CASE_INSENSITIVE,
hKeySysRoot,
NULL);
Debug1( indentLevel, __LINE__, L"system", &UniPath );
// Open up the system key
Status2 = NtOpenKey(&hKeySys,
KEY_READ,
&ObjAttr);
// We opened up the system key, now open the user key
if (NT_SUCCESS(Status2)) {
// Setup the object attr struc for the user key
InitializeObjectAttributes(&ObjAttr,
&UniPath,
OBJ_CASE_INSENSITIVE,
hKeyUsrRoot,
NULL);
// Open up the user key
Status2 = NtOpenKey(&hKeyUsr,
MAXIMUM_ALLOWED,
&ObjAttr);
// Check if there are any subkeys under this key
if (NT_SUCCESS(Status2)) {
Debug1(indentLevel, __LINE__, L"user", &UniPath );
TermsrvCheckKeys(hKeySys,
hKeyUsr,
pKeySysInfo,
pKeyUsrInfo,
ulcsys,
ulcusr,
indentLevel);
NtClose(hKeyUsr);
}
// key doesn't exist, clone system key to user
else {
Status2 = GetFullKeyPath(hKeyUsrRoot,pKeySysInfo->Name,&wcbuff);
if(NT_SUCCESS(Status2)) {
// don't clone if mapping off for this registry entry
GetTermsrCompatFlags(wcbuff,
&ultemp,
CompatibilityRegEntry);
LocalFree(wcbuff);
if (((ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) !=
(TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP) ))
{
Status2 = NtQueryKey(hKeySys,
KeyFullInformation,
pKeyUsrInfo,
ulcusr,
&ultemp);
if (NT_SUCCESS(Status2)) {
// don't clone if key previously deleted
RtlInitUnicodeString(&UniString, TERMSRV_COPYONCEFLAG);
Status2 = NtQueryValueKey(hKeySys,
&UniString,
KeyValuePartialInformation,
pValKeyInfo,
sizeof(aulbuf),
&ultemp);
if (!(NT_SUCCESS(Status2) && (pValKeyInfo->Data))) {
// Setup the unicode string for the class
InitUnicodeStringWithLen(&UniString,
pKeyUsrInfo->ClassLength ? pKeyUsrInfo->Class : NULL,
(USHORT)pKeyUsrInfo->ClassLength);
Debug1(indentLevel, __LINE__, L"creating user key", ObjAttr.ObjectName );
Status2 = NtCreateKey(&hKeyUsr,
MAXIMUM_ALLOWED,
&ObjAttr,
0,
&UniString,
REG_OPTION_NON_VOLATILE,
&ultemp);
if (NT_SUCCESS(Status2)) {
Debug1(indentLevel, __LINE__, L"cloning key", ObjAttr.ObjectName );
TermsrvCloneKey(hKeySys,
hKeyUsr,
pKeyUsrInfo,
TRUE);
}
}
}
}
}
}
NtClose(hKeySys);
}
}
}
// Get the info for the user key
if (NtQueryKey(hKeyUsrRoot,
KeyFullInformation,
pKeyUsrInfo,
ulcusr,
&ultemp) == STATUS_SUCCESS) {
// Now get the info for the system key (again)
if (NtQueryKey(hKeySysRoot,
KeyBasicInformation,
pKeySysInfo,
ulcsys,
&ultemp) == STATUS_SUCCESS) {
// Get the compatibility flags for this registry entry
pKeySysInfo->Name[pKeySysInfo->NameLength/sizeof(WCHAR)] = L'\0';
GetTermsrCompatFlags(pKeySysInfo->Name,
&ultemp,
CompatibilityRegEntry);
//check if it's older than the system version
if( pKeyUsrFullInfoSaved->LastWriteTime.QuadPart <
pKeySysInfo->LastWriteTime.QuadPart)
{
DebugTime(indentLevel, __LINE__, L"User key time", pKeyUsrFullInfoSaved->LastWriteTime );
DebugTime(indentLevel, __LINE__, L"Sys key time", pKeySysInfo->LastWriteTime);
Debug2( indentLevel, __LINE__, L"Key Old, values being cloned", pKeySysInfo->Name, pKeySysInfo->NameLength );
if(NtQueryKey(hKeySysRoot,
KeyFullInformation,
pKeyUsrInfo,
ulcusr,
&ultemp) == STATUS_SUCCESS) {
TermsrvCloneKey(hKeySysRoot,
hKeyUsrRoot,
pKeyUsrInfo,//actually it is system key information
FALSE);
}
}
}
}
RtlFreeHeap( RtlProcessHeap(), 0, pKeyUsrFullInfoSaved);
return(Status);
}
/*****************************************************************************
*
* TermsrvCheckNewRegEntries
*
* This routine will check the user's registry keys, and see if any of them
* are older than the system versions. If so, the old key will be removed.
*
* ENTRY:
* IN LPCWSTR wszBaseKeyName
*
* EXIT:
* No return value.
*
****************************************************************************/
void
TermsrvCheckNewRegEntries(
IN LPCWSTR wszBaseKeyName)
{
NTSTATUS Status;
ULONG ulcsys, ulcusr;
UNICODE_STRING UniPath;
OBJECT_ATTRIBUTES ObjAttr;
PKEY_BASIC_INFORMATION pKeySysInfo = NULL;
PKEY_FULL_INFORMATION pKeyUserInfo = NULL;
HANDLE hKeyUser, hKeySys = NULL;
WCHAR wcuser[MAX_PATH], wcsys[MAX_PATH];
DWORD indentLevel = 0;
// Oct 15, 1999
// This is BAD ! The Status bit was not initiazted to zero, which causes intermitent
// execution by this function. The problem is that even if the status bit is init'd
// to zero, then we can get the wrong behavior which will cause Office97 installation to
// go wrong.
// See BUG ID 412419
// Here is what woudl happen: Install any app ( say TsClient). This would cause
// an update to a Key in HKDU called Explorer\ShellFolders, just a refresh (no real change).
// Then, if Admin did an logout and login, the call from UserInit.EXE into this
// function (assuming by chance Status=0 was on the stack) would cause deletion of
// that key (ShellFolder). But once Explorer starts, it writes to the ShellFolder with
// a subset of original 19 values.
// The problem is that if then, you decide to install Office97, setup.exe would look
// in the same key for a value called "template" which is now missing. Explorer
// would then create it, but it would point to some other than default location.
// When all was done, our office97 compat script would be looking to where the
// "template" value used to point (the default location), not where it is pointing now.
//
// we decide to disable this func by calling return right here, and instead, rely
// on the TS mechanism to fault in keys.
// Oct 31, 1999
// I have decided to initialize the status var and let this func run, in addition to
// marking the Explorer\ShellFolders as a do-not propagate key tree.
//
// It was discovered that after installing Office2000, when a user clicks on the
// start-menu-> Open Office Docs link, MSI starts to run since user's hive is missing some
// keys.
//
// The reason MSI does not see the keys in HKCU is because MSI has the TS-aware bit set,
// which means that there are no faulting-in for any keys. The same is true for the Explorer.
// On the other hand, when an app such as Office runs, since it is not ts-aware, we
// fault in the keys that office touches. I verified this to work as expected.
// The problem is that when you click on "Open Office Documents", you do so from the
// explorer and when Explorer opens keys in the registry, since explorer is TS-aware,
// those keys are not faulted in. I have verified that if you mark explorer as a non-TS-aware
// app, the problem goes away.
// We made a recent change in TS code (B-bug 412419, bld 2156+) to fix a different
// problem which has now uncovered the reliance of the Explorer to get keys
// faulted in during login.
// Specifically, TS used to fault in all keys at login time,
// regardless of the need. Post 2156, TS faults in only keys upon
// access by non-ts-aware apps. This was to fix a bug based on
// what we considered to be our most informed and well tested opinion. That
// has turned out to be wrong.
// It is too risky to mark explorer non-ts-aware this late, so we must change the fix for 412419.
// This should also make Bruno Amice very happy, since the fix will be as it was
// advocated by him.
Status = STATUS_SUCCESS;
// Get a buffer for the system key info
ulcsys = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH*sizeof(WCHAR);
pKeySysInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
ulcsys);
if (!pKeySysInfo) {
Status = STATUS_NO_MEMORY;
}
// Get a buffer for the user key info
if (NT_SUCCESS(Status)) {
ulcusr = sizeof(KEY_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR);
pKeyUserInfo = RtlAllocateHeap(RtlProcessHeap(),
0,
ulcusr);
if (!pKeyUserInfo) {
Status = STATUS_NO_MEMORY;
}
}
// We have the necessary buffers, start checking the keys
if (NT_SUCCESS(Status)) {
// Build a string that points to Citrix\Install\Software
wcscpy(wcsys, wszBaseKeyName);
wcscat(wcsys, TERMSRV_INSTALL_SOFTWARE_SHORT);
// Build up a string for this user's software section
Status = RtlFormatCurrentUserKeyPath( &UniPath );
if (NT_SUCCESS(Status)) {
wcscpy(wcuser, UniPath.Buffer);
wcscat(wcuser, L"\\Software");
// Free the original user path
RtlFreeHeap( RtlProcessHeap(), 0, UniPath.Buffer );
}
if (NT_SUCCESS(Status)) {
// Create a unicode string for the system key path
RtlInitUnicodeString(&UniPath, wcsys);
InitializeObjectAttributes(&ObjAttr,
&UniPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Debug1(indentLevel, __LINE__, L"system", &UniPath );
Status = NtOpenKey(&hKeySys,
KEY_READ,
&ObjAttr);
}
if (NT_SUCCESS(Status)) {
// Create a unicode string for the user key path
RtlInitUnicodeString(&UniPath, wcuser);
InitializeObjectAttributes(&ObjAttr,
&UniPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Debug1(indentLevel, __LINE__, L"user", &UniPath );
Status = NtOpenKey(&hKeyUser,
KEY_READ | DELETE,
&ObjAttr);
}
// Go through each of the keys, checking if the system version is
// newer than the user version
if (NT_SUCCESS(Status)) {
TermsrvCheckKeys(hKeySys,
hKeyUser,
pKeySysInfo,
pKeyUserInfo,
ulcsys,
ulcusr,
indentLevel );
// Close the user key
NtClose(hKeyUser);
}
// If we allocated the system key, close it
if (hKeySys) {
NtClose(hKeySys);
}
}
// Free up any memory we allocated
if (pKeySysInfo) {
RtlFreeHeap( RtlProcessHeap(), 0, pKeySysInfo);
}
if (pKeyUserInfo) {
RtlFreeHeap( RtlProcessHeap(), 0, pKeyUserInfo);
}
}
/*****************************************************************************
*
* Ctxstristr
*
* This is a case insensitive version of strstr.
*
* ENTRY:
* PCHAR pstring1 (In) - String to search in
* PCHAR pstring2 (In) - String to search for
*
* EXIT:
* TRUE - User ini file should be sync'd
* FALSE - User ini file should be sync'd
*
****************************************************************************/
PCHAR
Ctxstristr(
PCHAR pstring1,
PCHAR pstring2)
{
PCHAR pch, ps1, ps2;
pch = pstring1;
while (*pch)
{
ps1 = pch;
ps2 = pstring2;
while (*ps1 && *ps2 && !(toupper(*ps1) - toupper(*ps2))) {
ps1++;
ps2++;
}
if (!*ps2) {
return(pch);
}
pch++;
}
return(NULL);
}
/*****************************************************************************
*
* TermsrvLogInstallIniFile
*
* This routine will write the time the .ini file was last updated into the
* Terminal Server\install section of the registry.
*
* ENTRY:
*
* EXIT:
* TRUE - Success
* FALSE - Failure
*
****************************************************************************/
BOOL TermsrvLogInstallIniFile(PUNICODE_STRING NtFileName)
{
PWCHAR pwch;
UNICODE_STRING UniString;
OBJECT_ATTRIBUTES ObjectAttr;
FILE_NETWORK_OPEN_INFORMATION BasicInfo;
HANDLE hKey;
NTSTATUS Status;
ULONG ultmp;
if (!TermsrvPerUserWinDirMapping()) {
return FALSE;
}
// Open up the registry key to store the last write time of the file
RtlInitUnicodeString(&UniString,
TERMSRV_INSTALL);
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// Open or create the Terminal Server\Install path
Status = NtCreateKey(&hKey,
KEY_WRITE,
&ObjectAttr,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&ultmp);
// Now open or create the IniFile Times key
if (NT_SUCCESS(Status)) {
NtClose(hKey);
RtlInitUnicodeString(&UniString,
TERMSRV_INIFILE_TIMES);
InitializeObjectAttributes(&ObjectAttr,
&UniString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtCreateKey(&hKey,
KEY_WRITE,
&ObjectAttr,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&ultmp);
}
// Opened up the registry key, now get the last write time of the file
if (NT_SUCCESS(Status)) {
// Query the last write time of the .ini file
InitializeObjectAttributes(&ObjectAttr,
NtFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtQueryFullAttributesFile( &ObjectAttr, &BasicInfo );
// Got the last write time, convert to seconds and write it out
if (NT_SUCCESS(Status)) {
// Just save the .ini filename, get rid of the path
pwch = wcsrchr(NtFileName->Buffer, L'\\') + 1;
if (!pwch) {
pwch = NtFileName->Buffer;
}
// Convert to seconds (so it fits in a DWORD)
RtlTimeToSecondsSince1970 (&BasicInfo.LastWriteTime,
&ultmp);
RtlInitUnicodeString(&UniString,
pwch);
// Write it out the .ini file name and the last write time
Status = NtSetValueKey(hKey,
&UniString,
0,
REG_DWORD,
&ultmp,
sizeof(ultmp));
}
// Close the registry key
NtClose(hKey);
}
return(NT_SUCCESS(Status));
}
/**********************************************************************
*
* BOOL TermsrvLogInstallIniFileEx( WCHAR *pDosFileName )
*
* This wraps TermsrvLogInstallIniFile() for dos name files instead of
* NT-file-objects
*
* The file name must have the full path since func will try to get
* access time for that file.
*
* EXIT:
* TRUE - Success
* FALSE - Failure
**********************************************************************/
BOOL TermsrvLogInstallIniFileEx( WCHAR *pDosFileName )
{
UNICODE_STRING uniString;
BOOL rc= FALSE;
if ( RtlDosPathNameToNtPathName_U( pDosFileName, &uniString, 0, 0 ) )
{
if ( rc = TermsrvLogInstallIniFile( & uniString ) )
{
RtlFreeHeap( RtlProcessHeap(), 0, uniString.Buffer );
}
}
return rc;
}
/**********************************************************************
*
* GetFullKeyPath()
*
* PURPOSE:
* Creates full key path given the key handle and subkey name.
*
* PARAMETERS:
* IN HANDLE hKeyParent - key handle
* IN LPCWSTR wszKey - subkey name (may be NULL)
* OUT LPWSTR *pwszKeyPath - on return contains a full key path
* (caller must freee allocated memory with LocalFree()).
*
* EXIT: NTSTATUS
*
**********************************************************************/
NTSTATUS
GetFullKeyPath(
IN HANDLE hKeyParent,
IN LPCWSTR wszKey,
OUT LPWSTR *pwszKeyPath)
{
NTSTATUS Status = STATUS_NO_MEMORY;
PKEY_NAME_INFORMATION pNameInfo;
ULONG cbSize = 0;
*pwszKeyPath = NULL;
cbSize = sizeof(KEY_NAME_INFORMATION) + MAX_PATH*sizeof(WCHAR);
pNameInfo = (PKEY_NAME_INFORMATION) LocalAlloc(LPTR, cbSize);
if(pNameInfo)
{
Status = NtQueryKey(
hKeyParent,
KeyNameInformation,
pNameInfo,
cbSize,
&cbSize);
if(Status == STATUS_BUFFER_OVERFLOW)
{
LocalFree(pNameInfo);
pNameInfo = (PKEY_NAME_INFORMATION) LocalAlloc(LPTR, cbSize);
if(pNameInfo)
{
Status = NtQueryKey(
hKeyParent,
KeyNameInformation,
pNameInfo,
cbSize,
&cbSize);
}
else
{
return STATUS_NO_MEMORY;
}
}
if(NT_SUCCESS(Status))
{
cbSize = pNameInfo->NameLength + sizeof(WCHAR);
if(wszKey)
{
cbSize += wcslen(wszKey)*sizeof(WCHAR) + sizeof(L'\\');
}
*pwszKeyPath = (LPWSTR) LocalAlloc(LPTR, cbSize);
if(*pwszKeyPath)
{
memcpy(*pwszKeyPath,pNameInfo->Name,pNameInfo->NameLength);
(*pwszKeyPath)[pNameInfo->NameLength/sizeof(WCHAR)] = 0;
if(wszKey)
{
wcscat(*pwszKeyPath,L"\\");
wcscat(*pwszKeyPath,wszKey);
}
}
else
{
Status = STATUS_NO_MEMORY;
}
}
LocalFree(pNameInfo);
}
return Status;
}
VOID
InitUnicodeStringWithLen(
OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString,
IN USHORT StringLength
)
{
DestinationString->Buffer = (PWSTR)SourceString;
DestinationString->Length = StringLength;
if ( StringLength ) {
DestinationString->MaximumLength = StringLength + (USHORT)sizeof(UNICODE_NULL);
}
else {
DestinationString->MaximumLength = 0;
}
}