1064 lines
35 KiB
C
1064 lines
35 KiB
C
/*++
|
|
|
|
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 )
|
|
#pragma alloc_text(INIT,RtlSetEnvironmentStrings)
|
|
#endif
|
|
|
|
BOOLEAN RtlpEnvironCacheValid;
|
|
|
|
NTSTATUS
|
|
RtlCreateEnvironment(
|
|
IN BOOLEAN CloneCurrentEnvironment OPTIONAL,
|
|
OUT PVOID *Environment
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
PVOID pNew, pOld;
|
|
PPEB Peb;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
|
|
Peb = NtCurrentPeb ();
|
|
|
|
ProcessParameters = Peb->ProcessParameters;
|
|
|
|
//
|
|
// 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 = 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 {
|
|
RtlReleasePebLock ();
|
|
|
|
if (Status == STATUS_ACCESS_VIOLATION) {
|
|
if (pNew != NULL) {
|
|
ZwFreeVirtualMemory (NtCurrentProcess(),
|
|
&pNew,
|
|
&MemoryInformation.RegionSize,
|
|
MEM_RELEASE);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
PPEB Peb;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
|
|
//
|
|
// Acquire the Peb Lock for the duration while we munge the environment
|
|
// variable storage block.
|
|
//
|
|
|
|
Peb = NtCurrentPeb ();
|
|
|
|
ProcessParameters = Peb->ProcessParameters;
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
RtlAcquirePebLock ();
|
|
|
|
RtlpEnvironCacheValid = FALSE;
|
|
|
|
//
|
|
// Capture current process's environment variable block pointer to
|
|
// return to caller or destroy.
|
|
//
|
|
|
|
pOld = ProcessParameters->Environment;
|
|
|
|
//
|
|
// Change current process's environment variable block pointer to
|
|
// point to the passed block.
|
|
//
|
|
|
|
ProcessParameters->Environment = Environment;
|
|
|
|
//
|
|
// Release the Peb Lock
|
|
//
|
|
|
|
RtlReleasePebLock ();
|
|
|
|
//
|
|
// 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;
|
|
} else {
|
|
//
|
|
// 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 PCUNICODE_STRING Name,
|
|
IN OUT PUNICODE_STRING Value
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING CurrentName;
|
|
UNICODE_STRING CurrentValue;
|
|
PWSTR p, q;
|
|
PPEB Peb;
|
|
BOOLEAN PebLockLocked = FALSE;
|
|
SIZE_T len;
|
|
SIZE_T NameLength, NameChars;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
|
|
Status = STATUS_VARIABLE_NOT_FOUND;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
ProcessParameters = Peb->ProcessParameters;
|
|
|
|
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.
|
|
//
|
|
|
|
PebLockLocked = TRUE;
|
|
|
|
RtlAcquirePebLock ();
|
|
|
|
//
|
|
// Capture the pointer to the current process's environment variable
|
|
// block.
|
|
//
|
|
|
|
p = ProcessParameters->Environment;
|
|
|
|
}
|
|
|
|
if (RtlpEnvironCacheValid && p == 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.
|
|
//
|
|
NameLength = Name->Length;
|
|
NameChars = NameLength / sizeof (WCHAR);
|
|
|
|
if (p != NULL) while (1) {
|
|
|
|
//
|
|
// Get the length of the terminated string. This should be in the
|
|
// form 'keyword=value'
|
|
//
|
|
len = wcslen (p);
|
|
if (len == 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if this environment variable is big enough to be our target.
|
|
// If must be at least one bigger than the name we are searching
|
|
// for since it must contain an '=' sign.
|
|
//
|
|
|
|
if (NameChars < len) {
|
|
q = &p[NameChars];
|
|
//
|
|
// We have a possible match. See if there is an '=' at the correct point.
|
|
//
|
|
if (*q == L'=') {
|
|
//
|
|
// We have a possible match. Now compare the string out right
|
|
//
|
|
CurrentName.Length = (USHORT) NameLength;
|
|
CurrentName.Buffer = p;
|
|
|
|
//
|
|
// After comparing the strings we want to make sure we are not
|
|
// matching something that we shouldn't. For example if somebody
|
|
// did a lookup of "FRED=BOB" we don't want this to match "FRED=BOB=ALBERT".
|
|
// The lookup name is invalid and the environment variable is really FRED here
|
|
// not "FRED=BOB". To eliminate this case we make sure that the "=" character
|
|
// is at the appropriate place
|
|
// There are environment variable of the for =C:
|
|
// In order not to eliminate these we skip the first character in our search.
|
|
//
|
|
if (RtlEqualUnicodeString (Name, &CurrentName, TRUE) &&
|
|
(wcschr (p+1, L'=') == q)) {
|
|
//
|
|
// 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.
|
|
//
|
|
CurrentValue.Buffer = q+1;
|
|
CurrentValue.Length = (USHORT) ((len - 1) * sizeof (WCHAR) - NameLength);
|
|
|
|
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 == ProcessParameters->Environment) {
|
|
RtlpEnvironCacheValid = TRUE;
|
|
RtlpEnvironCacheName = CurrentName;
|
|
RtlpEnvironCacheValue = CurrentValue;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
p += len + 1;
|
|
}
|
|
|
|
// 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) {
|
|
PebLockLocked = TRUE;
|
|
RtlAcquirePebLock();
|
|
}
|
|
|
|
// get cdw here...
|
|
CurrentValue = ProcessParameters->CurrentDirectory.DosPath;
|
|
Status = STATUS_SUCCESS;
|
|
} else if (RtlEqualUnicodeString(Name, &ApplicationDirectoryPseudoVariable, TRUE)) {
|
|
USHORT PrefixLength = 0;
|
|
|
|
if (!PebLockLocked) {
|
|
PebLockLocked = TRUE;
|
|
RtlAcquirePebLock();
|
|
}
|
|
|
|
// get appdir here
|
|
CurrentValue = 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 PCUNICODE_STRING Name,
|
|
IN PCUNICODE_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;
|
|
PPEB Peb;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
|
|
//
|
|
// 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();
|
|
}
|
|
|
|
Peb = NtCurrentPeb ();
|
|
|
|
ProcessParameters = Peb->ProcessParameters;
|
|
|
|
Status = STATUS_VARIABLE_NOT_FOUND;
|
|
|
|
pNew = NULL;
|
|
InsertionPoint = NULL;
|
|
|
|
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 = ProcessParameters->Environment;
|
|
}
|
|
|
|
RtlpEnvironCacheValid = FALSE;
|
|
|
|
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 {
|
|
ProcessParameters->Environment = pNew;
|
|
Peb->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 {
|
|
ProcessParameters->Environment = pNew;
|
|
Peb->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 );
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlSetEnvironmentStrings(
|
|
IN PWCHAR NewEnvironment,
|
|
IN SIZE_T NewEnvironmentSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allows the replacement of the current environment block with a new one.
|
|
|
|
Arguments:
|
|
|
|
NewEnvironment - Pointer to a set of zero terminated strings terminated by two terminators
|
|
|
|
NewEnvironmentSize - Size of the block to put in place in bytes
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of function call
|
|
|
|
--*/
|
|
{
|
|
PPEB Peb;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
PVOID pOld, pNew;
|
|
NTSTATUS Status, Status1;
|
|
SIZE_T NewSize, OldSize;
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
|
|
|
|
//
|
|
// Assert if the block is not well formed
|
|
//
|
|
ASSERT (NewEnvironmentSize > sizeof (WCHAR) * 2);
|
|
ASSERT ((NewEnvironmentSize & (sizeof (WCHAR) - 1)) == 0);
|
|
ASSERT (NewEnvironment[NewEnvironmentSize/sizeof(WCHAR)-1] == L'\0');
|
|
ASSERT (NewEnvironment[NewEnvironmentSize/sizeof(WCHAR)-2] == L'\0');
|
|
|
|
Peb = NtCurrentPeb ();
|
|
|
|
ProcessParameters = Peb->ProcessParameters;
|
|
|
|
RtlAcquirePebLock ();
|
|
|
|
pOld = ProcessParameters->Environment;
|
|
|
|
Status = ZwQueryVirtualMemory (NtCurrentProcess (),
|
|
pOld,
|
|
MemoryBasicInformation,
|
|
&MemoryInformation,
|
|
sizeof (MemoryInformation),
|
|
NULL);
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto unlock_and_exit;
|
|
}
|
|
|
|
if (MemoryInformation.RegionSize >= NewEnvironmentSize) {
|
|
RtlpEnvironCacheValid = FALSE;
|
|
RtlCopyMemory (pOld, NewEnvironment, NewEnvironmentSize);
|
|
Status = STATUS_SUCCESS;
|
|
goto unlock_and_exit;
|
|
}
|
|
|
|
//
|
|
// Drop the lock around expensive operations
|
|
//
|
|
|
|
RtlReleasePebLock ();
|
|
|
|
pOld = NULL;
|
|
pNew = NULL;
|
|
|
|
NewSize = NewEnvironmentSize;
|
|
|
|
Status = ZwAllocateVirtualMemory (NtCurrentProcess (),
|
|
&pNew,
|
|
0,
|
|
&NewSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Fill in the new block.
|
|
//
|
|
RtlCopyMemory (pNew, NewEnvironment, NewEnvironmentSize);
|
|
|
|
//
|
|
// Reacquire the lock. The existing block may have been reallocated
|
|
// and so may now be big enough. Ignore this and use the block we
|
|
// have created anyway.
|
|
//
|
|
|
|
RtlAcquirePebLock ();
|
|
|
|
pOld = ProcessParameters->Environment;
|
|
|
|
ProcessParameters->Environment = pNew;
|
|
|
|
RtlpEnvironCacheValid = FALSE;
|
|
|
|
RtlReleasePebLock ();
|
|
|
|
|
|
//
|
|
// Release the old block.
|
|
//
|
|
|
|
OldSize = 0;
|
|
|
|
Status1 = ZwFreeVirtualMemory (NtCurrentProcess(),
|
|
&pOld,
|
|
&OldSize,
|
|
MEM_RELEASE);
|
|
|
|
ASSERT (NT_SUCCESS (Status1));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
unlock_and_exit:;
|
|
RtlReleasePebLock ();
|
|
return Status;
|
|
}
|