|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
environ.c
Abstract:
Environment Variable support
Author:
Steven R. Wood (stevewo) 30-Jan-1991
Revision History:
--*/
#include "ntrtlp.h"
#include "zwapi.h"
#include "nturtl.h"
#include "string.h"
#include "ntrtlpath.h"
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(INIT,RtlCreateEnvironment )
#pragma alloc_text(INIT,RtlDestroyEnvironment )
#pragma alloc_text(INIT,RtlSetCurrentEnvironment )
#pragma alloc_text(INIT,RtlQueryEnvironmentVariable_U )
#pragma alloc_text(INIT,RtlSetEnvironmentVariable )
#endif
BOOLEAN RtlpEnvironCacheValid;
NTSTATUS RtlCreateEnvironment( IN BOOLEAN CloneCurrentEnvironment OPTIONAL, OUT PVOID *Environment ) { NTSTATUS Status; MEMORY_BASIC_INFORMATION MemoryInformation; PVOID pNew, pOld;
//
// If not cloning a copy of the current process's environment variable
// block, just allocate a block of committed memory and return its
// address.
//
pNew = NULL; if (!CloneCurrentEnvironment) { createEmptyEnvironment: MemoryInformation.RegionSize = 1; Status = ZwAllocateVirtualMemory( NtCurrentProcess(), &pNew, 0, &MemoryInformation.RegionSize, MEM_COMMIT, PAGE_READWRITE ); if (NT_SUCCESS( Status )) { *Environment = pNew; }
return( Status ); }
//
// Acquire the Peb Lock for the duration while we munge the environment
// variable storage block.
//
RtlAcquirePebLock();
//
// Capture the pointer to the current process's environment variable
// block and initialize the new pointer to null for our finally clause.
//
pOld = NtCurrentPeb()->ProcessParameters->Environment; if (pOld == NULL) { RtlReleasePebLock(); goto createEmptyEnvironment; }
try { try { //
// Query the current size of the current process's environment
// variable block. Return status if failure.
//
Status = ZwQueryVirtualMemory( NtCurrentProcess(), pOld, MemoryBasicInformation, &MemoryInformation, sizeof( MemoryInformation ), NULL ); if (!NT_SUCCESS( Status )) { leave; }
//
// Allocate memory to contain a copy of the current process's
// environment variable block. Return status if failure.
//
Status = ZwAllocateVirtualMemory( NtCurrentProcess(), &pNew, 0, &MemoryInformation.RegionSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { leave; }
//
// Copy the current process's environment to the allocated memory
// and return a pointer to the copy.
//
RtlCopyMemory( pNew, pOld, MemoryInformation.RegionSize ); *Environment = pNew; } except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ACCESS_VIOLATION; } } finally { if (Status == STATUS_ACCESS_VIOLATION) { if (pNew != NULL) { ZwFreeVirtualMemory( NtCurrentProcess(), &pNew, &MemoryInformation.RegionSize, MEM_RELEASE ); } }
RtlReleasePebLock(); }
return( Status ); }
NTSTATUS RtlDestroyEnvironment( IN PVOID Environment ) { NTSTATUS Status; SIZE_T RegionSize;
//
// Free the specified environment variable block.
//
RtlpEnvironCacheValid = FALSE;
RegionSize = 0; Status = ZwFreeVirtualMemory( NtCurrentProcess(), &Environment, &RegionSize, MEM_RELEASE ); //
// Return status.
//
return( Status ); }
NTSTATUS RtlSetCurrentEnvironment( IN PVOID Environment, OUT PVOID *PreviousEnvironment OPTIONAL ) { NTSTATUS Status; PVOID pOld;
//
// Acquire the Peb Lock for the duration while we munge the environment
// variable storage block.
//
RtlpEnvironCacheValid = FALSE;
RtlAcquirePebLock();
Status = STATUS_SUCCESS; try { //
// Capture current process's environment variable block pointer to
// return to caller or destroy.
//
pOld = NtCurrentPeb()->ProcessParameters->Environment;
//
// Change current process's environment variable block pointer to
// point to the passed block.
//
NtCurrentPeb()->ProcessParameters->Environment = Environment;
//
// If caller requested it, return the pointer to the previous
// process environment variable block and set the local variable
// to NULL so we dont destroy it below.
//
if (ARGUMENT_PRESENT( PreviousEnvironment )) { *PreviousEnvironment = pOld; pOld = NULL; } } except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ACCESS_VIOLATION; pOld = NULL; }
//
// Release the Peb Lock
//
RtlReleasePebLock();
//
// If old environment not returned to caller, destroy it.
//
if (pOld != NULL) { RtlDestroyEnvironment( pOld ); }
//
// Return status
//
return( Status ); }
UNICODE_STRING RtlpEnvironCacheName; UNICODE_STRING RtlpEnvironCacheValue;
NTSTATUS RtlQueryEnvironmentVariable_U( IN PVOID Environment OPTIONAL, IN PUNICODE_STRING Name, IN OUT PUNICODE_STRING Value ) { NTSTATUS Status; UNICODE_STRING CurrentName; UNICODE_STRING CurrentValue; PWSTR p; PPEB Peb; BOOLEAN PebLockLocked = FALSE;
Status = STATUS_VARIABLE_NOT_FOUND; Peb = NtCurrentPeb();
try { if (ARGUMENT_PRESENT( Environment )) { p = Environment; if (*p == UNICODE_NULL) { leave; } } else { //
// Acquire the Peb Lock for the duration while we munge the
// environment variable storage block.
//
RtlAcquirePebLock(); PebLockLocked = TRUE;
//
// Capture the pointer to the current process's environment variable
// block.
//
p = Peb->ProcessParameters->Environment;
}
if ( RtlpEnvironCacheValid && p == Peb->ProcessParameters->Environment ) { if (RtlEqualUnicodeString( Name, &RtlpEnvironCacheName, TRUE )) {
//
// Names are equal. Always return the length of the
// value string, excluding the terminating null. If
// there is room in the caller's buffer, return a copy
// of the value string and success status. Otherwise
// return an error status. In the latter case, the caller
// can examine the length field of their value string
// so they can determine much memory is needed.
//
Value->Length = RtlpEnvironCacheValue.Length; if (Value->MaximumLength >= RtlpEnvironCacheValue.Length) { RtlCopyMemory( Value->Buffer, RtlpEnvironCacheValue.Buffer, RtlpEnvironCacheValue.Length ); //
// Null terminate returned string if there is room.
//
if (Value->MaximumLength > RtlpEnvironCacheValue.Length) { Value->Buffer[ RtlpEnvironCacheValue.Length/sizeof(WCHAR) ] = L'\0'; }
Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_TOO_SMALL; } leave; } }
//
// The environment variable block consists of zero or more null
// terminated UNICODE strings. Each string is of the form:
//
// name=value
//
// where the null termination is after the value.
//
if (p != NULL) while (*p) { //
// Determine the size of the name and value portions of
// the current string of the environment variable block.
//
CurrentName.Buffer = p; CurrentName.Length = 0; CurrentName.MaximumLength = 0; while (*p) { //
// If we see an equal sign, then compute the size of
// the name portion and scan for the end of the value.
//
if (*p == L'=' && p != CurrentName.Buffer) { CurrentName.Length = (USHORT)(p - CurrentName.Buffer)*sizeof(WCHAR); CurrentName.MaximumLength = (USHORT)(CurrentName.Length+sizeof(WCHAR)); CurrentValue.Buffer = ++p;
while(*p) { p++; } CurrentValue.Length = (USHORT)(p - CurrentValue.Buffer)*sizeof(WCHAR); CurrentValue.MaximumLength = (USHORT)(CurrentValue.Length+sizeof(WCHAR));
//
// At this point we have the length of both the name
// and value portions, so exit the loop so we can
// do the compare.
//
break; } else { p++; } }
//
// Skip over the terminating null character for this name=value
// pair in preparation for the next iteration of the loop.
//
p++;
//
// Compare the current name with the one requested, ignore
// case.
//
if (RtlEqualUnicodeString( Name, &CurrentName, TRUE )) { //
// Names are equal. Always return the length of the
// value string, excluding the terminating null. If
// there is room in the caller's buffer, return a copy
// of the value string and success status. Otherwise
// return an error status. In the latter case, the caller
// can examine the length field of their value string
// so they can determine much memory is needed.
//
Value->Length = CurrentValue.Length; if (Value->MaximumLength >= CurrentValue.Length) { RtlCopyMemory( Value->Buffer, CurrentValue.Buffer, CurrentValue.Length ); //
// Null terminate returned string if there is room.
//
if (Value->MaximumLength > CurrentValue.Length) { Value->Buffer[ CurrentValue.Length/sizeof(WCHAR) ] = L'\0'; }
if ( !Environment || Environment == Peb->ProcessParameters->Environment) { RtlpEnvironCacheValid = TRUE; RtlpEnvironCacheName = CurrentName; RtlpEnvironCacheValue = CurrentValue; }
Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_TOO_SMALL; } break; } }
// If it's not in the real env block, let's see if it's a pseudo environment variable
if (Status == STATUS_VARIABLE_NOT_FOUND) { static const UNICODE_STRING CurrentWorkingDirectoryPseudoVariable = RTL_CONSTANT_STRING(L"__CD__"); static const UNICODE_STRING ApplicationDirectoryPseudoVariable = RTL_CONSTANT_STRING(L"__APPDIR__");
if (RtlEqualUnicodeString(Name, &CurrentWorkingDirectoryPseudoVariable, TRUE)) { // Get the PEB lock if we don't already have it.
if (!PebLockLocked) { RtlAcquirePebLock(); PebLockLocked = TRUE; }
// get cdw here...
CurrentValue = NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath; Status = STATUS_SUCCESS; } else if (RtlEqualUnicodeString(Name, &ApplicationDirectoryPseudoVariable, TRUE)) { USHORT PrefixLength = 0;
if (!PebLockLocked) { RtlAcquirePebLock(); PebLockLocked = TRUE; }
// get appdir here
CurrentValue = NtCurrentPeb()->ProcessParameters->ImagePathName;
Status = RtlFindCharInUnicodeString( RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &CurrentValue, &RtlDosPathSeperatorsString, &PrefixLength); if (NT_SUCCESS(Status)) { CurrentValue.Length = PrefixLength + sizeof(WCHAR); } else if (Status == STATUS_NOT_FOUND) { // Use the whole thing; just translate the status to successs.
Status = STATUS_SUCCESS; } }
if (NT_SUCCESS(Status)) { Value->Length = CurrentValue.Length; if (Value->MaximumLength >= CurrentValue.Length) { RtlCopyMemory(Value->Buffer, CurrentValue.Buffer, CurrentValue.Length);
//
// Null terminate returned string if there is room.
//
if (Value->MaximumLength > CurrentValue.Length) Value->Buffer[ CurrentValue.Length/sizeof(WCHAR) ] = L'\0'; } } }
} except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_ACCESS_VIOLATION; }
//
// Release the Peb lock.
//
if (PebLockLocked) RtlReleasePebLock();
//
// Return status.
//
return Status; }
NTSTATUS RtlSetEnvironmentVariable( IN OUT PVOID *Environment OPTIONAL, IN PUNICODE_STRING Name, IN PUNICODE_STRING Value OPTIONAL ) { NTSTATUS Status; MEMORY_BASIC_INFORMATION MemoryInformation; UNICODE_STRING CurrentName; UNICODE_STRING CurrentValue; PVOID pOld, pNew; ULONG n, Size; SIZE_T NewSize; LONG CompareResult; PWSTR p, pStart, pEnd; PWSTR InsertionPoint;
//
// Validate passed in name and reject if zero length or anything but the first
// character is an equal sign.
//
n = Name->Length / sizeof( WCHAR ); if (n == 0) { return STATUS_INVALID_PARAMETER; }
try { p = Name->Buffer; while (--n) { if (*++p == L'=') { return STATUS_INVALID_PARAMETER; } } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
RtlpEnvironCacheValid = FALSE; Status = STATUS_VARIABLE_NOT_FOUND; if (ARGUMENT_PRESENT( Environment )) { pOld = *Environment; } else { //
// Acquire the Peb Lock for the duration while we munge the
// environment variable storage block.
//
RtlAcquirePebLock();
//
// Capture the pointer to the current process's environment variable
// block.
//
pOld = NtCurrentPeb()->ProcessParameters->Environment; }
pNew = NULL; InsertionPoint = NULL;
try { try {
//
// The environment variable block consists of zero or more null
// terminated UNICODE strings. Each string is of the form:
//
// name=value
//
// where the null termination is after the value.
//
p = pOld; pEnd = NULL; if (p != NULL) while (*p) { //
// Determine the size of the name and value portions of
// the current string of the environment variable block.
//
CurrentName.Buffer = p; CurrentName.Length = 0; CurrentName.MaximumLength = 0; while (*p) { //
// If we see an equal sign, then compute the size of
// the name portion and scan for the end of the value.
//
if (*p == L'=' && p != CurrentName.Buffer) { CurrentName.Length = (USHORT)(p - CurrentName.Buffer) * sizeof(WCHAR); CurrentName.MaximumLength = (USHORT)(CurrentName.Length+sizeof(WCHAR)); CurrentValue.Buffer = ++p;
while(*p) { p++; } CurrentValue.Length = (USHORT)(p - CurrentValue.Buffer) * sizeof(WCHAR); CurrentValue.MaximumLength = (USHORT)(CurrentValue.Length+sizeof(WCHAR));
//
// At this point we have the length of both the name
// and value portions, so exit the loop so we can
// do the compare.
//
break; } else { p++; } }
//
// Skip over the terminating null character for this name=value
// pair in preparation for the next iteration of the loop.
//
p++;
//
// Compare the current name with the one requested, ignore
// case.
//
if (!(CompareResult = RtlCompareUnicodeString( Name, &CurrentName, TRUE ))) { //
// Names are equal. Now find the end of the current
// environment variable block.
//
pEnd = p; while (*pEnd) { while (*pEnd++) { } } pEnd++;
if (!ARGUMENT_PRESENT( Value )) { //
// If the caller did not specify a new value, then delete
// the entire name=value pair by copying up the remainder
// of the environment variable block.
//
RtlMoveMemory( CurrentName.Buffer, p, (ULONG) ((pEnd - p)*sizeof(WCHAR)) ); Status = STATUS_SUCCESS; } else if (Value->Length <= CurrentValue.Length) { //
// New value is smaller, so copy new value, then null
// terminate it, and then move up the remainder of the
// variable block so it is immediately after the new
// null terminated value.
//
pStart = CurrentValue.Buffer; RtlMoveMemory( pStart, Value->Buffer, Value->Length ); pStart += Value->Length/sizeof(WCHAR); *pStart++ = L'\0';
RtlMoveMemory( pStart, p,(ULONG)((pEnd - p)*sizeof(WCHAR)) ); Status = STATUS_SUCCESS; } else { //
// New value is larger, so query the current size of the
// environment variable block. Return status if failure.
//
Status = ZwQueryVirtualMemory( NtCurrentProcess(), pOld, MemoryBasicInformation, &MemoryInformation, sizeof( MemoryInformation ), NULL ); if (!NT_SUCCESS( Status )) { leave; }
//
// See if there is room for new, larger value. If not
// allocate a new copy of the environment variable
// block.
//
NewSize = (pEnd - (PWSTR)pOld)*sizeof(WCHAR) + Value->Length - CurrentValue.Length; if (NewSize >= MemoryInformation.RegionSize) { //
// Allocate memory to contain a copy of the current
// process's environment variable block. Return
// status if failure.
//
Status = ZwAllocateVirtualMemory( NtCurrentProcess(), &pNew, 0, &NewSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { leave; }
//
// Copy the current process's environment to the allocated memory
// inserting the new value as we do the copy.
//
Size = (ULONG) (CurrentValue.Buffer - (PWSTR)pOld); RtlMoveMemory( pNew, pOld, Size*sizeof(WCHAR) ); pStart = (PWSTR)pNew + Size; RtlMoveMemory( pStart, Value->Buffer, Value->Length ); pStart += Value->Length/sizeof(WCHAR); *pStart++ = L'\0'; RtlMoveMemory( pStart, p,(ULONG)((pEnd - p)*sizeof(WCHAR))); if (ARGUMENT_PRESENT( Environment )) *Environment = pNew; else { NtCurrentPeb()->ProcessParameters->Environment = pNew; NtCurrentPeb()->EnvironmentUpdateCount += 1; }
ZwFreeVirtualMemory( NtCurrentProcess(), &pOld, &MemoryInformation.RegionSize, MEM_RELEASE ); pNew = pOld; } else { pStart = CurrentValue.Buffer + Value->Length/sizeof(WCHAR) + 1; RtlMoveMemory( pStart, p,(ULONG)((pEnd - p)*sizeof(WCHAR))); *--pStart = L'\0';
RtlMoveMemory( pStart - Value->Length/sizeof(WCHAR), Value->Buffer, Value->Length ); } }
break; } else if (CompareResult < 0) { //
// Requested name is less than the current name. Save this
// spot in case the variable is not in a sorted position.
// The insertion point for the new variable is before the
// variable just examined.
//
if (InsertionPoint == NULL) { InsertionPoint = CurrentName.Buffer; } } }
//
// If we found an insertion point, reset the string
// pointer back to it.
//
if (InsertionPoint != NULL) { p = InsertionPoint; }
//
// If variable name not found and a new value parameter was specified
// then insert the new variable name and its value at the appropriate
// place in the environment variable block (i.e. where p points to).
//
if (pEnd == NULL && ARGUMENT_PRESENT( Value )) { if (p != NULL) { //
// Name not found. Now find the end of the current
// environment variable block.
//
pEnd = p; while (*pEnd) { while (*pEnd++) { } } pEnd++;
//
// New value is present, so query the current size of the
// environment variable block. Return status if failure.
//
Status = ZwQueryVirtualMemory( NtCurrentProcess(), pOld, MemoryBasicInformation, &MemoryInformation, sizeof( MemoryInformation ), NULL ); if (!NT_SUCCESS( Status )) { leave; }
//
// See if there is room for new, larger value. If not
// allocate a new copy of the environment variable
// block.
//
NewSize = (pEnd - (PWSTR)pOld) * sizeof(WCHAR) + Name->Length + sizeof(WCHAR) + Value->Length + sizeof(WCHAR); } else { NewSize = Name->Length + sizeof(WCHAR) + Value->Length + sizeof(WCHAR); MemoryInformation.RegionSize = 0; }
if (NewSize >= MemoryInformation.RegionSize) { //
// Allocate memory to contain a copy of the current
// process's environment variable block. Return
// status if failure.
//
Status = ZwAllocateVirtualMemory( NtCurrentProcess(), &pNew, 0, &NewSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { leave; }
//
// Copy the current process's environment to the allocated memory
// inserting the new value as we do the copy.
//
if (p != NULL) { Size = (ULONG)(p - (PWSTR)pOld); RtlMoveMemory( pNew, pOld, Size*sizeof(WCHAR) ); } else { Size = 0; } pStart = (PWSTR)pNew + Size; RtlMoveMemory( pStart, Name->Buffer, Name->Length ); pStart += Name->Length/sizeof(WCHAR); *pStart++ = L'='; RtlMoveMemory( pStart, Value->Buffer, Value->Length ); pStart += Value->Length/sizeof(WCHAR); *pStart++ = L'\0'; if (p != NULL) { RtlMoveMemory( pStart, p,(ULONG)((pEnd - p)*sizeof(WCHAR)) ); }
if (ARGUMENT_PRESENT( Environment )) { *Environment = pNew; } else { NtCurrentPeb()->ProcessParameters->Environment = pNew; NtCurrentPeb()->EnvironmentUpdateCount += 1; } ZwFreeVirtualMemory( NtCurrentProcess(), &pOld, &MemoryInformation.RegionSize, MEM_RELEASE ); } else { pStart = p + Name->Length/sizeof(WCHAR) + 1 + Value->Length/sizeof(WCHAR) + 1; RtlMoveMemory( pStart, p,(ULONG)((pEnd - p)*sizeof(WCHAR)) ); RtlMoveMemory( p, Name->Buffer, Name->Length ); p += Name->Length/sizeof(WCHAR); *p++ = L'='; RtlMoveMemory( p, Value->Buffer, Value->Length ); p += Value->Length/sizeof(WCHAR); *p++ = L'\0'; } } } except(EXCEPTION_EXECUTE_HANDLER) { //
// If abnormally terminating, assume access violation.
//
Status = STATUS_ACCESS_VIOLATION; } } finally { //
// Release the Peb lock.
//
if (!ARGUMENT_PRESENT( Environment )) { RtlReleasePebLock(); } }
//
// Return status.
//
return( Status ); }
|