1523 lines
51 KiB
C
1523 lines
51 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sxsstorage.c
|
|
|
|
Abstract:
|
|
|
|
Side-by-side activation support for Windows/NT
|
|
|
|
Implementation of the assembly storage map.
|
|
|
|
|
|
Author:
|
|
|
|
Michael Grier (MGrier) 6/13/2000
|
|
|
|
Revision History:
|
|
Xiaoyu Wu(xiaoyuw) 7/01/2000 .local directory
|
|
Xiaoyu Wu(xiaoyuw) 8/04/2000 private assembly
|
|
Jay Krell (a-JayK) October 2000 the little bit of system default context that wasn't already done
|
|
--*/
|
|
|
|
#pragma warning(disable:4214) // bit field types other than int
|
|
#pragma warning(disable:4201) // nameless struct/union
|
|
#pragma warning(disable:4115) // named type definition in parentheses
|
|
#pragma warning(disable:4127) // condition expression is constant
|
|
|
|
#include <ntos.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <sxstypes.h>
|
|
#include "sxsp.h"
|
|
|
|
#define IS_PATH_SEPARATOR(_wch) (((_wch) == L'\\') || ((_wch) == L'/'))
|
|
#define LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX L".Local"
|
|
|
|
#if DBG
|
|
PCUNICODE_STRING RtlpGetImagePathName(VOID);
|
|
#define RtlpGetCurrentProcessId() (HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess))
|
|
#define RtlpGetCurrentThreadId() (HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread))
|
|
#endif
|
|
|
|
#if DBG
|
|
|
|
PCUNICODE_STRING RtlpGetImagePathName(VOID)
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
return (Peb->ProcessParameters != NULL) ? &Peb->ProcessParameters->ImagePathName : NULL;
|
|
}
|
|
|
|
static VOID
|
|
DbgPrintFunctionEntry(
|
|
CONST CHAR* Function
|
|
)
|
|
{
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: [pid:0x%x, tid:0x%x, %wZ] enter %s%d()\n",
|
|
RtlpGetCurrentProcessId(),
|
|
RtlpGetCurrentThreadId(),
|
|
RtlpGetImagePathName(),
|
|
Function,
|
|
(int)sizeof(PVOID) * 8
|
|
);
|
|
}
|
|
|
|
static VOID
|
|
DbgPrintFunctionExit(
|
|
CONST CHAR* Function,
|
|
NTSTATUS Status
|
|
)
|
|
{
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
NT_SUCCESS(Status) ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL,
|
|
"SXS: [0x%x.%x] %s%d() exiting with status 0x%lx\n",
|
|
RtlpGetCurrentProcessId(),
|
|
RtlpGetCurrentThreadId(),
|
|
Function,
|
|
(int)sizeof(PVOID) * 8,
|
|
Status
|
|
);
|
|
}
|
|
#else
|
|
|
|
#define DbgPrintFunctionEntry(function) /* nothing */
|
|
#define DbgPrintFunctionExit(function, status) /* nothing */
|
|
|
|
#endif // DBG
|
|
|
|
// Because we write to the peb, we must not be in 64bit code for a 32bit process,
|
|
// unless we know we are early enough in CreateProcess, which is not the case
|
|
// in this file. Also don't call the 32bit version of this in a 64bit process.
|
|
#if DBG
|
|
#define ASSERT_OK_TO_WRITE_PEB() \
|
|
{ \
|
|
PVOID Peb32 = NULL; \
|
|
NTSTATUS Status; \
|
|
\
|
|
Status = \
|
|
NtQueryInformationProcess( \
|
|
NtCurrentProcess(), \
|
|
ProcessWow64Information, \
|
|
&Peb32, \
|
|
sizeof(Peb32), \
|
|
NULL); \
|
|
/* The other Peb must be The Peb or the other Peb must not exist. */ \
|
|
ASSERT(Peb32 == NtCurrentPeb() || Peb32 == NULL); \
|
|
}
|
|
#else
|
|
#define ASSERT_OK_TO_WRITE_PEB() /* nothing */
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RtlpInitializeAssemblyStorageMap(
|
|
PASSEMBLY_STORAGE_MAP Map,
|
|
ULONG EntryCount,
|
|
PASSEMBLY_STORAGE_MAP_ENTRY *EntryArray
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
ULONG Flags = 0;
|
|
|
|
#if DBG
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"%s(Map:%p, EntryCount:0x%lx)\n",
|
|
__FUNCTION__,
|
|
Map,
|
|
EntryCount
|
|
);
|
|
|
|
ASSERT_OK_TO_WRITE_PEB();
|
|
#endif // DBG
|
|
|
|
if ((Map == NULL) ||
|
|
(EntryCount == 0)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters:\n"
|
|
"SXS: Map : 0x%lx\n"
|
|
"SXS: EntryCount : 0x%lx\n"
|
|
__FUNCTION__,
|
|
Map,
|
|
EntryCount
|
|
);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (EntryArray == NULL) {
|
|
EntryArray = (PASSEMBLY_STORAGE_MAP_ENTRY *) RtlAllocateHeap(RtlProcessHeap(), 0, EntryCount * sizeof(PASSEMBLY_STORAGE_MAP_ENTRY));
|
|
if (EntryArray == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
Flags |= ASSEMBLY_STORAGE_MAP_ASSEMBLY_ARRAY_IS_HEAP_ALLOCATED;
|
|
}
|
|
|
|
for (i=0; i<EntryCount; i++)
|
|
EntryArray[i] = NULL;
|
|
|
|
Map->Flags = Flags;
|
|
Map->AssemblyCount = EntryCount;
|
|
Map->AssemblyArray = EntryArray;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
#if DBG
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
NT_SUCCESS(Status) ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL,
|
|
"%s(Map:%p, EntryCount:0x%lx) : (Map:%p, Status:0x%lx)\n",
|
|
__FUNCTION__,
|
|
Map,
|
|
EntryCount,
|
|
Map,
|
|
Status
|
|
);
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
RtlpUninitializeAssemblyStorageMap(
|
|
PASSEMBLY_STORAGE_MAP Map
|
|
)
|
|
{
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"%s(Map:%p)\n",
|
|
__FUNCTION__,
|
|
Map
|
|
);
|
|
#endif
|
|
|
|
if (Map != NULL) {
|
|
ULONG i;
|
|
|
|
for (i=0; i<Map->AssemblyCount; i++) {
|
|
PASSEMBLY_STORAGE_MAP_ENTRY Entry = Map->AssemblyArray[i];
|
|
|
|
if (Entry != NULL) {
|
|
Entry->DosPath.Length = 0;
|
|
Entry->DosPath.MaximumLength = 0;
|
|
Entry->DosPath.Buffer = NULL;
|
|
|
|
if (Entry->Handle != NULL) {
|
|
RTL_SOFT_VERIFY(NT_SUCCESS(NtClose(Entry->Handle)));
|
|
Entry->Handle = NULL;
|
|
}
|
|
|
|
Map->AssemblyArray[i] = NULL;
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Entry);
|
|
}
|
|
}
|
|
|
|
if (Map->Flags & ASSEMBLY_STORAGE_MAP_ASSEMBLY_ARRAY_IS_HEAP_ALLOCATED) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Map->AssemblyArray);
|
|
}
|
|
|
|
Map->AssemblyArray = NULL;
|
|
Map->AssemblyCount = 0;
|
|
Map->Flags = 0;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpInsertAssemblyStorageMapEntry(
|
|
PASSEMBLY_STORAGE_MAP Map,
|
|
ULONG AssemblyRosterIndex,
|
|
PCUNICODE_STRING StorageLocation,
|
|
HANDLE* OpenDirectoryHandle
|
|
)
|
|
{
|
|
PASSEMBLY_STORAGE_MAP_ENTRY Entry = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSERT(Map != NULL);
|
|
ASSERT(AssemblyRosterIndex >= 1);
|
|
ASSERT((Map != NULL) && (AssemblyRosterIndex < Map->AssemblyCount));
|
|
ASSERT(StorageLocation != NULL);
|
|
ASSERT((StorageLocation != NULL) && (StorageLocation->Length >= sizeof(WCHAR)));
|
|
ASSERT((StorageLocation != NULL) && (StorageLocation->Buffer != NULL));
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
|
|
if ((Map == NULL) ||
|
|
(AssemblyRosterIndex < 1) ||
|
|
(AssemblyRosterIndex > Map->AssemblyCount) ||
|
|
(StorageLocation == NULL) ||
|
|
(StorageLocation->Length < sizeof(WCHAR)) ||
|
|
(StorageLocation->Buffer == NULL) ||
|
|
(OpenDirectoryHandle == NULL)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters\n"
|
|
"SXS: Map : %p\n"
|
|
"SXS: AssemblyRosterIndex : 0x%lx\n"
|
|
"SXS: Map->AssemblyCount : 0x%lx\n"
|
|
"SXS: StorageLocation : %p\n"
|
|
"SXS: StorageLocation->Length: 0x%x\n"
|
|
"SXS: StorageLocation->Buffer: %p\n"
|
|
"SXS: OpenDirectoryHandle : %p\n",
|
|
__FUNCTION__,
|
|
Map,
|
|
AssemblyRosterIndex,
|
|
Map ? Map->AssemblyCount : 0,
|
|
StorageLocation,
|
|
(StorageLocation != NULL) ? StorageLocation->Length : 0,
|
|
(StorageLocation != NULL) ? StorageLocation->Buffer : NULL,
|
|
OpenDirectoryHandle
|
|
);
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((StorageLocation->Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
Entry = (PASSEMBLY_STORAGE_MAP_ENTRY) RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(ASSEMBLY_STORAGE_MAP_ENTRY) + StorageLocation->Length + sizeof(WCHAR));
|
|
if (Entry == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
Entry->Flags = 0;
|
|
Entry->DosPath.Length = StorageLocation->Length;
|
|
Entry->DosPath.Buffer = (PWSTR) (Entry + 1);
|
|
Entry->DosPath.MaximumLength = (USHORT) (StorageLocation->Length + sizeof(WCHAR));
|
|
RtlCopyMemory(
|
|
Entry->DosPath.Buffer,
|
|
StorageLocation->Buffer,
|
|
StorageLocation->Length);
|
|
Entry->DosPath.Buffer[Entry->DosPath.Length / sizeof(WCHAR)] = L'\0';
|
|
|
|
Entry->Handle = *OpenDirectoryHandle;
|
|
|
|
// Ok, we're all set. Let's try the big interlocked switcheroo
|
|
if (InterlockedCompareExchangePointer(
|
|
(PVOID *) &Map->AssemblyArray[AssemblyRosterIndex],
|
|
(PVOID) Entry,
|
|
(PVOID) NULL) == NULL) {
|
|
// If we're the first ones in, avoid cleaning up in the exit path.
|
|
Entry = NULL;
|
|
*OpenDirectoryHandle = NULL;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
if (Entry != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Entry);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpResolveAssemblyStorageMapEntry(
|
|
PASSEMBLY_STORAGE_MAP Map,
|
|
PCACTIVATION_CONTEXT_DATA Data,
|
|
ULONG AssemblyRosterIndex,
|
|
PASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_ROUTINE Callback,
|
|
PVOID CallbackContext
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_DATA CallbackData;
|
|
PVOID ResolutionContext;
|
|
BOOLEAN ResolutionContextValid = FALSE;
|
|
UNICODE_STRING AssemblyDirectory;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER AssemblyRoster;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_ENTRY AssemblyRosterEntry;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_INFORMATION AssemblyInformation;
|
|
PVOID AssemblyInformationSectionBase;
|
|
UNICODE_STRING ResolvedPath;
|
|
WCHAR ResolvedPathBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING ResolvedDynamicPath;
|
|
PUNICODE_STRING ResolvedPathUsed;
|
|
HANDLE OpenDirectoryHandle = NULL;
|
|
WCHAR QueryPathBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING FileName;
|
|
RTL_RELATIVE_NAME RelativeName;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
SIZE_T RootCount, CurrentRootIndex;
|
|
PWSTR FreeBuffer = NULL;
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
|
|
ResolvedPath.Length = 0;
|
|
ResolvedPath.MaximumLength = sizeof(ResolvedPathBuffer);
|
|
ResolvedPath.Buffer = ResolvedPathBuffer;
|
|
|
|
ResolvedDynamicPath.Length = 0;
|
|
ResolvedDynamicPath.MaximumLength = 0;
|
|
ResolvedDynamicPath.Buffer = NULL;
|
|
|
|
FileName.Length = 0;
|
|
FileName.MaximumLength = 0;
|
|
FileName.Buffer = NULL;
|
|
|
|
ResolutionContext = NULL;
|
|
|
|
// First, let's validate parameters...
|
|
if ((Map == NULL) ||
|
|
(Data == NULL) ||
|
|
(AssemblyRosterIndex < 1) ||
|
|
(AssemblyRosterIndex > Map->AssemblyCount)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters\n"
|
|
"SXS: Map : %p\n"
|
|
"SXS: Data : %p\n"
|
|
"SXS: AssemblyRosterIndex: 0x%lx\n"
|
|
"SXS: Map->AssemblyCount : 0x%lx\n",
|
|
__FUNCTION__,
|
|
Map,
|
|
Data,
|
|
AssemblyRosterIndex,
|
|
(Map != NULL) ? Map->AssemblyCount : 0
|
|
);
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
// Is it already resolved?
|
|
if (Map->AssemblyArray[AssemblyRosterIndex] != NULL)
|
|
goto Exit;
|
|
|
|
AssemblyRoster = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) Data) + Data->AssemblyRosterOffset);
|
|
AssemblyRosterEntry = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_ENTRY) (((ULONG_PTR) Data) + AssemblyRoster->FirstEntryOffset + (AssemblyRosterIndex * sizeof(ACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_ENTRY)));
|
|
AssemblyInformation = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_INFORMATION) (((ULONG_PTR) Data) + AssemblyRosterEntry->AssemblyInformationOffset);
|
|
AssemblyInformationSectionBase = (PVOID) (((ULONG_PTR) Data) + AssemblyRoster->AssemblyInformationSectionOffset);
|
|
|
|
if (AssemblyInformation->AssemblyDirectoryNameLength > UNICODE_STRING_MAX_BYTES) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Assembly directory name stored in assembly information too long (%lu bytes) - ACTIVATION_CONTEXT_DATA at %p\n", AssemblyInformation->AssemblyDirectoryNameLength, Data);
|
|
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
// The root assembly may just be in the raw filesystem, in which case we want to resolve the path to be the
|
|
// directory containing the application.
|
|
if (AssemblyInformation->Flags & ACTIVATION_CONTEXT_DATA_ASSEMBLY_INFORMATION_PRIVATE_ASSEMBLY)
|
|
{
|
|
WCHAR * p = NULL;
|
|
WCHAR * pManifestPath = NULL;
|
|
USHORT ManifestPathLength;
|
|
|
|
//now, we have AssemblyInformation in hand, get the manifest path
|
|
ResolvedPathUsed = &ResolvedPath;
|
|
|
|
pManifestPath = (PWSTR)((ULONG_PTR)AssemblyInformationSectionBase + AssemblyInformation->ManifestPathOffset);
|
|
if ( !pManifestPath) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
p = wcsrchr(pManifestPath, L'\\');
|
|
if (!p) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
}
|
|
ManifestPathLength = (USHORT)((p - pManifestPath + 1) * sizeof(WCHAR)); // additional 1 WCHAR for "\"
|
|
ManifestPathLength += sizeof(WCHAR); // for trailing NULL
|
|
|
|
if (ManifestPathLength > sizeof(ResolvedPathBuffer)) {
|
|
if (ManifestPathLength > UNICODE_STRING_MAX_BYTES) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
ResolvedDynamicPath.MaximumLength = (USHORT) (ManifestPathLength);
|
|
|
|
ResolvedDynamicPath.Buffer = (RtlAllocateStringRoutine)(ResolvedDynamicPath.MaximumLength);
|
|
if (ResolvedDynamicPath.Buffer == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
ResolvedPathUsed = &ResolvedDynamicPath;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
ResolvedPathUsed->Buffer,
|
|
(PVOID)(pManifestPath),
|
|
ManifestPathLength-sizeof(WCHAR));
|
|
|
|
ResolvedPathUsed->Buffer[ManifestPathLength / sizeof(WCHAR) - 1] = L'\0';
|
|
ResolvedPathUsed->Length = (USHORT)ManifestPathLength-sizeof(WCHAR);
|
|
} else if ((AssemblyInformation->Flags & ACTIVATION_CONTEXT_DATA_ASSEMBLY_INFORMATION_ROOT_ASSEMBLY) &&
|
|
(AssemblyInformation->AssemblyDirectoryNameLength == 0)) {
|
|
// Get the image directory for the process
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NtCurrentPeb()->ProcessParameters;
|
|
// We don't need to image name, just the length up to the last slash.
|
|
PWSTR pszCursor;
|
|
USHORT cbOriginalLength;
|
|
USHORT cbLeft;
|
|
USHORT cbIncludingSlash;
|
|
|
|
ASSERT(ProcessParameters != NULL);
|
|
if (ProcessParameters == NULL) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
// We don't need to image name, just the length up to the last slash.
|
|
pszCursor = ProcessParameters->ImagePathName.Buffer;
|
|
cbOriginalLength = ProcessParameters->ImagePathName.Length;
|
|
cbLeft = cbOriginalLength;
|
|
cbIncludingSlash = 0;
|
|
|
|
while (cbLeft != 0) {
|
|
const WCHAR wch = *pszCursor++;
|
|
cbLeft -= sizeof(WCHAR);
|
|
|
|
if (IS_PATH_SEPARATOR(wch)) {
|
|
cbIncludingSlash = cbOriginalLength - cbLeft;
|
|
}
|
|
}
|
|
|
|
ResolvedPathUsed = &ResolvedPath;
|
|
|
|
if ((cbIncludingSlash + sizeof(WCHAR)) > sizeof(ResolvedPathBuffer)) {
|
|
if ((cbIncludingSlash + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
ResolvedDynamicPath.MaximumLength = (USHORT) (cbIncludingSlash + sizeof(WCHAR));
|
|
|
|
ResolvedDynamicPath.Buffer = (RtlAllocateStringRoutine)(ResolvedDynamicPath.MaximumLength);
|
|
if (ResolvedDynamicPath.Buffer == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
ResolvedPathUsed = &ResolvedDynamicPath;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
ResolvedPathUsed->Buffer,
|
|
ProcessParameters->ImagePathName.Buffer,
|
|
cbIncludingSlash);
|
|
|
|
ResolvedPathUsed->Buffer[cbIncludingSlash / sizeof(WCHAR)] = L'\0';
|
|
ResolvedPathUsed->Length = cbIncludingSlash;
|
|
} else {
|
|
// If the resolution is not to the root assembly path, we need to make our callbacks.
|
|
|
|
ResolvedPathUsed = NULL;
|
|
AssemblyDirectory.Length = (USHORT) AssemblyInformation->AssemblyDirectoryNameLength;
|
|
AssemblyDirectory.MaximumLength = AssemblyDirectory.Length;
|
|
AssemblyDirectory.Buffer = (PWSTR) (((ULONG_PTR) AssemblyInformationSectionBase) + AssemblyInformation->AssemblyDirectoryNameOffset);
|
|
|
|
// Get ready to fire the resolution beginning event...
|
|
CallbackData.ResolutionBeginning.Data = Data;
|
|
CallbackData.ResolutionBeginning.AssemblyRosterIndex = AssemblyRosterIndex;
|
|
CallbackData.ResolutionBeginning.ResolutionContext = NULL;
|
|
CallbackData.ResolutionBeginning.Root.Length = 0;
|
|
CallbackData.ResolutionBeginning.Root.MaximumLength = sizeof(QueryPathBuffer);
|
|
CallbackData.ResolutionBeginning.Root.Buffer = QueryPathBuffer;
|
|
CallbackData.ResolutionBeginning.KnownRoot = FALSE;
|
|
CallbackData.ResolutionBeginning.CancelResolution = FALSE;
|
|
CallbackData.ResolutionBeginning.RootCount = 0;
|
|
|
|
(*Callback)(
|
|
ASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_REASON_RESOLUTION_BEGINNING,
|
|
&CallbackData,
|
|
CallbackContext);
|
|
if (CallbackData.ResolutionBeginning.CancelResolution) {
|
|
Status = STATUS_CANCELLED;
|
|
goto Exit;
|
|
}
|
|
|
|
// If that was enough, then register it and we're outta here...
|
|
if (CallbackData.ResolutionBeginning.KnownRoot) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Storage resolution callback said that this is a well known storage root\n");
|
|
|
|
// See if it's there...
|
|
Status = RtlpProbeAssemblyStorageRootForAssembly(
|
|
0,
|
|
&CallbackData.ResolutionBeginning.Root,
|
|
&AssemblyDirectory,
|
|
&ResolvedPath,
|
|
&ResolvedDynamicPath,
|
|
&ResolvedPathUsed,
|
|
&OpenDirectoryHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Attempt to probe known root of assembly storage (\"%wZ\") failed; Status = 0x%08lx\n", &CallbackData.ResolutionBeginning.Root, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlpInsertAssemblyStorageMapEntry(
|
|
Map,
|
|
AssemblyRosterIndex,
|
|
&CallbackData.ResolutionBeginning.Root,
|
|
&OpenDirectoryHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Attempt to insert well known storage root into assembly storage map assembly roster index %lu failed; Status = 0x%08lx\n", AssemblyRosterIndex, Status);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
// Otherwise, begin the grind...
|
|
ResolutionContext = CallbackData.ResolutionBeginning.ResolutionContext;
|
|
RootCount = CallbackData.ResolutionBeginning.RootCount;
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Assembly storage resolution trying %Id roots (-1 is ok)\n", (SSIZE_T/*from SIZE_T*/)RootCount);
|
|
|
|
ResolutionContextValid = TRUE;
|
|
|
|
for (CurrentRootIndex = 0; CurrentRootIndex < RootCount; CurrentRootIndex++) {
|
|
CallbackData.GetRoot.ResolutionContext = ResolutionContext;
|
|
CallbackData.GetRoot.RootIndex = CurrentRootIndex;
|
|
CallbackData.GetRoot.Root.Length = 0;
|
|
CallbackData.GetRoot.Root.MaximumLength = sizeof(QueryPathBuffer);
|
|
CallbackData.GetRoot.Root.Buffer = QueryPathBuffer;
|
|
CallbackData.GetRoot.CancelResolution = FALSE;
|
|
CallbackData.GetRoot.NoMoreEntries = FALSE;
|
|
|
|
(*Callback)(
|
|
ASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_REASON_GET_ROOT,
|
|
&CallbackData,
|
|
CallbackContext);
|
|
|
|
if (CallbackData.GetRoot.CancelResolution) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Callback routine cancelled storage root resolution on root number %Iu\n", CurrentRootIndex);
|
|
|
|
Status = STATUS_CANCELLED;
|
|
goto Exit;
|
|
}
|
|
|
|
if (CallbackData.GetRoot.NoMoreEntries) {
|
|
if (CallbackData.GetRoot.Root.Length == 0) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Storage resolution finished because callback indicated no more entries on root number %Iu\n", CurrentRootIndex);
|
|
|
|
// we're done...
|
|
RootCount = CurrentRootIndex;
|
|
break;
|
|
}
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Storage resolution callback has indicated that this is the last root to process: number %Iu\n", CurrentRootIndex);
|
|
|
|
RootCount = CurrentRootIndex + 1;
|
|
}
|
|
|
|
// Allow the caller to skip this index.
|
|
if (CallbackData.GetRoot.Root.Length == 0) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Storage resolution for root number %lu returned blank root; skipping probing logic and moving to next.\n", CurrentRootIndex);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (OpenDirectoryHandle != NULL) {
|
|
RTL_SOFT_VERIFY(NT_SUCCESS(NtClose(OpenDirectoryHandle)));
|
|
OpenDirectoryHandle = NULL;
|
|
}
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Assembly storage map probing root %wZ for assembly directory %wZ\n", &CallbackData.GetRoot.Root, &AssemblyDirectory);
|
|
|
|
// See if it's there...
|
|
Status = RtlpProbeAssemblyStorageRootForAssembly(
|
|
0,
|
|
&CallbackData.GetRoot.Root,
|
|
&AssemblyDirectory,
|
|
&ResolvedPath,
|
|
&ResolvedDynamicPath,
|
|
&ResolvedPathUsed,
|
|
&OpenDirectoryHandle);
|
|
|
|
// If we got it, leave the loop.
|
|
if (NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: Found good storage root for %wZ at index %Iu\n", &AssemblyDirectory, CurrentRootIndex);
|
|
break;
|
|
}
|
|
|
|
if (Status != STATUS_SXS_ASSEMBLY_NOT_FOUND) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Attempt to probe assembly storage root %wZ for assembly directory %wZ failed with status = 0x%08lx\n", &CallbackData.GetRoot.Root, &AssemblyDirectory, Status);
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (CurrentRootIndex == RootCount) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Unable to resolve storage root for assembly directory %wZ in %Iu tries\n", &AssemblyDirectory, CurrentRootIndex);
|
|
|
|
Status = STATUS_SXS_ASSEMBLY_NOT_FOUND;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// sometimes at this point probing has simultaneously opened the directory,
|
|
// sometimes it has not.
|
|
//
|
|
if (OpenDirectoryHandle == NULL) {
|
|
|
|
//create Handle for this directory
|
|
if (!RtlDosPathNameToNtPathName_U(
|
|
ResolvedPathUsed->Buffer,
|
|
&FileName,
|
|
NULL,
|
|
&RelativeName
|
|
))
|
|
{
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Attempt to translate DOS path name \"%S\" to NT format failed\n", ResolvedPathUsed->Buffer);
|
|
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto Exit;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
if (RelativeName.RelativeName.Length != 0)
|
|
{
|
|
FileName = *((PUNICODE_STRING) &RelativeName.RelativeName);
|
|
} else
|
|
{
|
|
RelativeName.ContainingDirectory = NULL;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RelativeName.ContainingDirectory,
|
|
NULL
|
|
);
|
|
|
|
// Open the directory to prevent deletion, just like set current working directory does...
|
|
Status = NtOpenFile(
|
|
&OpenDirectoryHandle,
|
|
FILE_TRAVERSE | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Don't map this to like SXS_blah_NOT_FOUND, because
|
|
// probing says this is definitely where we expect to get stuff.
|
|
//
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Unable to open assembly directory under storage root \"%S\"; Status = 0x%08lx\n", ResolvedPathUsed->Buffer, Status);
|
|
goto Exit;
|
|
} else
|
|
{
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_TRACE_LEVEL,
|
|
"SXS: It is resolved!!!, GOOD");
|
|
}
|
|
}
|
|
|
|
// Hey, we made it. Add us to the list!
|
|
Status = RtlpInsertAssemblyStorageMapEntry(
|
|
Map,
|
|
AssemblyRosterIndex,
|
|
ResolvedPathUsed,
|
|
&OpenDirectoryHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Storage resolution failed to insert entry to storage map; Status = 0x%08lx\n", Status);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
|
|
// Let the caller run down their context...
|
|
if (ResolutionContextValid) {
|
|
CallbackData.ResolutionEnding.ResolutionContext = ResolutionContext;
|
|
|
|
(*Callback)(
|
|
ASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_REASON_RESOLUTION_ENDING,
|
|
&CallbackData,
|
|
CallbackContext);
|
|
}
|
|
|
|
if (ResolvedDynamicPath.Buffer != NULL) {
|
|
(RtlFreeStringRoutine)(ResolvedDynamicPath.Buffer);
|
|
}
|
|
|
|
//
|
|
// RtlpInsertAssemblyStorageMapEntry gives ownership to the storage map, and
|
|
// NULLs out our local, when successful.
|
|
//
|
|
if (OpenDirectoryHandle != NULL) {
|
|
RTL_SOFT_VERIFY(NT_SUCCESS(NtClose(OpenDirectoryHandle)));
|
|
}
|
|
|
|
if (FreeBuffer != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpProbeAssemblyStorageRootForAssembly(
|
|
ULONG Flags,
|
|
PCUNICODE_STRING Root,
|
|
PCUNICODE_STRING AssemblyDirectory,
|
|
PUNICODE_STRING PreAllocatedString,
|
|
PUNICODE_STRING DynamicString,
|
|
PUNICODE_STRING *StringUsed,
|
|
HANDLE *OpenDirectoryHandle
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WCHAR Buffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING String = {0};
|
|
SIZE_T TotalLength;
|
|
BOOLEAN SeparatorNeededAfterRoot = FALSE;
|
|
PWSTR Cursor;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING FileName = {0};
|
|
RTL_RELATIVE_NAME RelativeName;
|
|
PWSTR FreeBuffer = NULL;
|
|
HANDLE TempDirectoryHandle = NULL;
|
|
BOOLEAN fExistDir;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
|
|
if (StringUsed != NULL)
|
|
*StringUsed = NULL;
|
|
|
|
if (OpenDirectoryHandle != NULL)
|
|
*OpenDirectoryHandle = NULL;
|
|
|
|
if ((Flags != 0) ||
|
|
(Root == NULL) ||
|
|
(AssemblyDirectory == NULL) ||
|
|
(PreAllocatedString == NULL) ||
|
|
(DynamicString == NULL) ||
|
|
(StringUsed == NULL) ||
|
|
(OpenDirectoryHandle == NULL)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters\n"
|
|
"SXS: Flags: 0x%lx\n"
|
|
// %p is good enough because the checks are only against NULL
|
|
"SXS: Root: %p\n"
|
|
"SXS: AssemblyDirectory: %p\n"
|
|
"SXS: PreAllocatedString: %p\n"
|
|
"SXS: DynamicString: %p\n"
|
|
"SXS: StringUsed: %p\n"
|
|
"SXS: OpenDirectoryHandle: %p\n",
|
|
__FUNCTION__,
|
|
Flags,
|
|
Root,
|
|
AssemblyDirectory,
|
|
PreAllocatedString,
|
|
DynamicString,
|
|
StringUsed,
|
|
OpenDirectoryHandle
|
|
);
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
TotalLength = Root->Length;
|
|
|
|
if (Root->Length != 0) {
|
|
if (!IS_PATH_SEPARATOR(Root->Buffer[(Root->Length / sizeof(WCHAR)) - 1])) {
|
|
SeparatorNeededAfterRoot = TRUE;
|
|
TotalLength += sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
TotalLength += AssemblyDirectory->Length;
|
|
|
|
// And space for the trailing slash
|
|
TotalLength += sizeof(WCHAR);
|
|
|
|
// And space for a trailing null character because the path functions want one
|
|
TotalLength += sizeof(WCHAR);
|
|
|
|
//
|
|
// We do not add in space for the trailing slash so as to not cause a dynamic
|
|
// allocation until necessary in the boundary condition. If the name of the
|
|
// directory we're probing fits fine in the stack-allocated buffer, we'll do
|
|
// the heap allocation if the probe succeeds. Otherwise we'll not bother.
|
|
//
|
|
// Maybe the relative complexity of the extra "+ sizeof(WCHAR)"s that are
|
|
// around aren't worth it, but extra unnecessary heap allocations are my
|
|
// hot button.
|
|
//
|
|
|
|
// Check to see if the string, plus a trailing slash that we don't write until
|
|
// the end of this function plus the trailing null accounted for above
|
|
// fits into a UNICODE_STRING. If not, bail out.
|
|
if (TotalLength > UNICODE_STRING_MAX_BYTES) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Assembly storage resolution failing probe because combined path length does not fit in an UNICODE_STRING.\n");
|
|
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
if (TotalLength > sizeof(Buffer)) {
|
|
String.MaximumLength = (USHORT) TotalLength;
|
|
|
|
String.Buffer = (RtlAllocateStringRoutine)(String.MaximumLength);
|
|
if (String.Buffer == NULL) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Assembly storage resolution failing probe because attempt to allocate %u bytes failed.\n", String.MaximumLength);
|
|
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
String.Buffer = Buffer;
|
|
String.MaximumLength = sizeof(Buffer);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
String.Buffer,
|
|
Root->Buffer,
|
|
Root->Length);
|
|
|
|
Cursor = (PWSTR) (((ULONG_PTR) String.Buffer) + Root->Length);
|
|
|
|
if (SeparatorNeededAfterRoot) {
|
|
*Cursor++ = L'\\';
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Cursor,
|
|
AssemblyDirectory->Buffer,
|
|
AssemblyDirectory->Length);
|
|
|
|
Cursor = (PWSTR) (((ULONG_PTR) Cursor) + AssemblyDirectory->Length);
|
|
|
|
*Cursor = L'\0';
|
|
|
|
String.Length =
|
|
Root->Length +
|
|
(SeparatorNeededAfterRoot ? sizeof(WCHAR) : 0) +
|
|
AssemblyDirectory->Length;
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(
|
|
String.Buffer,
|
|
&FileName,
|
|
NULL,
|
|
&RelativeName
|
|
)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Attempt to translate DOS path name \"%S\" to NT format failed\n", String.Buffer);
|
|
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto Exit;
|
|
}
|
|
|
|
FreeBuffer = FileName.Buffer;
|
|
|
|
if (RelativeName.RelativeName.Length != 0) {
|
|
FileName = *((PUNICODE_STRING) &RelativeName.RelativeName);
|
|
} else {
|
|
RelativeName.ContainingDirectory = NULL;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RelativeName.ContainingDirectory,
|
|
NULL
|
|
);
|
|
// check the existence of directories
|
|
Status = NtQueryAttributesFile(
|
|
&Obja,
|
|
&BasicInfo
|
|
);
|
|
|
|
fExistDir = FALSE;
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
if ( (Status == STATUS_SHARING_VIOLATION) || (Status == STATUS_ACCESS_DENIED) )
|
|
fExistDir = TRUE;
|
|
else
|
|
fExistDir = FALSE;
|
|
}
|
|
else
|
|
fExistDir = TRUE;
|
|
|
|
if (! fExistDir) {
|
|
if (( Status == STATUS_NO_SUCH_FILE) || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND)
|
|
Status = STATUS_SXS_ASSEMBLY_NOT_FOUND;
|
|
else
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Unable to open assembly directory under storage root \"%S\"; Status = 0x%08lx\n", String.Buffer, Status);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the directory to prevent deletion, just like set current working directory does...
|
|
Status = NtOpenFile(
|
|
&TempDirectoryHandle,
|
|
FILE_TRAVERSE | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
// If we failed, remap no such file to STATUS_SXS_ASSEMBLY_NOT_FOUND.
|
|
if (Status == STATUS_NO_SUCH_FILE) {
|
|
Status = STATUS_SXS_ASSEMBLY_NOT_FOUND;
|
|
} else {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: Unable to open assembly directory under storage root \"%S\"; Status = 0x%08lx\n", String.Buffer, Status);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Hey, we found it!
|
|
// add a slash to the path on the way out and we're done!
|
|
|
|
if (TotalLength <= PreAllocatedString->MaximumLength) {
|
|
// The caller's static string is big enough; just use it.
|
|
RtlCopyMemory(
|
|
PreAllocatedString->Buffer,
|
|
String.Buffer,
|
|
String.Length);
|
|
|
|
*StringUsed = PreAllocatedString;
|
|
} else {
|
|
// If we already have a dynamic string, just give them our pointer.
|
|
if (String.Buffer != Buffer) {
|
|
DynamicString->Buffer = String.Buffer;
|
|
String.Buffer = NULL;
|
|
} else {
|
|
// Otherwise we do our first allocation on the way out...
|
|
DynamicString->Buffer = (RtlAllocateStringRoutine)(TotalLength);
|
|
if (DynamicString->Buffer == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
DynamicString->Buffer,
|
|
String.Buffer,
|
|
String.Length);
|
|
}
|
|
|
|
DynamicString->MaximumLength = (USHORT) TotalLength;
|
|
*StringUsed = DynamicString;
|
|
}
|
|
|
|
Cursor = (PWSTR) (((ULONG_PTR) (*StringUsed)->Buffer) + String.Length);
|
|
*Cursor++ = L'\\';
|
|
*Cursor++ = L'\0';
|
|
(*StringUsed)->Length = (USHORT) (String.Length + sizeof(WCHAR)); // aka "TotalLength - sizeof(WCHAR)" but this seemed cleaner
|
|
|
|
*OpenDirectoryHandle = TempDirectoryHandle;
|
|
TempDirectoryHandle = NULL;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
|
|
if (FreeBuffer != NULL) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
|
}
|
|
|
|
if ((String.Buffer != NULL) && (String.Buffer != Buffer)) {
|
|
(RtlFreeStringRoutine)(String.Buffer);
|
|
}
|
|
|
|
if (TempDirectoryHandle != NULL) {
|
|
RTL_SOFT_VERIFY(NT_SUCCESS(NtClose(TempDirectoryHandle)));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if 0 /* dead code */
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlResolveAssemblyStorageMapEntry(
|
|
IN ULONG Flags,
|
|
IN PACTIVATION_CONTEXT ActivationContext,
|
|
IN ULONG AssemblyRosterIndex,
|
|
IN PASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_ROUTINE Callback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PCACTIVATION_CONTEXT_DATA ActivationContextData = NULL;
|
|
PASSEMBLY_STORAGE_MAP Map = NULL;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER AssemblyRosterHeader = NULL;
|
|
PPEB Peb = NtCurrentPeb();
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
ASSERT_OK_TO_WRITE_PEB();
|
|
RTLP_DISALLOW_THE_EMPTY_ACTIVATION_CONTEXT(ActivationContext);
|
|
|
|
Status = RtlpGetActivationContextDataStorageMapAndRosterHeader(
|
|
0,
|
|
Peb,
|
|
ActivationContext,
|
|
&ActivationContextData,
|
|
&Map,
|
|
&AssemblyRosterHeader);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
|
|
if (ActivationContextData == NULL) {
|
|
ASSERT(ActivationContext == NULL);
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: RtlResolveAssemblyStorageMapEntry() asked to resolve an assembly storage entry when no activation context data is available.\n");
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (AssemblyRosterIndex >= AssemblyRosterHeader->EntryCount) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters: AssemblyRosterIndex 0x%lx >= AssemblyRosterHeader->EntryCount 0x%lx\n",
|
|
__FUNCTION__,
|
|
AssemblyRosterIndex,
|
|
AssemblyRosterHeader->EntryCount
|
|
);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlpResolveAssemblyStorageMapEntry(Map, ActivationContextData, AssemblyRosterIndex, Callback, CallbackContext);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif /* dead code */
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetAssemblyStorageRoot(
|
|
IN ULONG Flags,
|
|
IN PACTIVATION_CONTEXT ActivationContext,
|
|
IN ULONG AssemblyRosterIndex,
|
|
OUT PCUNICODE_STRING *AssemblyStorageRoot,
|
|
IN PASSEMBLY_STORAGE_MAP_RESOLUTION_CALLBACK_ROUTINE Callback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PCACTIVATION_CONTEXT_DATA ActivationContextData = NULL;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER AssemblyRosterHeader = NULL;
|
|
PASSEMBLY_STORAGE_MAP AssemblyStorageMap = NULL;
|
|
|
|
const PPEB Peb = NtCurrentPeb();
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
ASSERT_OK_TO_WRITE_PEB();
|
|
RTLP_DISALLOW_THE_EMPTY_ACTIVATION_CONTEXT(ActivationContext);
|
|
|
|
if (AssemblyStorageRoot != NULL) {
|
|
*AssemblyStorageRoot = NULL;
|
|
}
|
|
|
|
if ((Flags & ~(RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_PROCESS_DEFAULT
|
|
| RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_SYSTEM_DEFAULT))
|
|
||
|
|
(AssemblyRosterIndex < 1) ||
|
|
(AssemblyStorageRoot == NULL) ||
|
|
(Callback == NULL)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters:\n"
|
|
"SXS: Flags : 0x%lx\n"
|
|
"SXS: AssemblyRosterIndex: 0x%lx\n"
|
|
"SXS: AssemblyStorageRoot: %p\n"
|
|
"SXS: Callback : %p\n",
|
|
__FUNCTION__,
|
|
Flags,
|
|
AssemblyRosterIndex,
|
|
AssemblyStorageRoot,
|
|
Callback
|
|
);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
// Simple implementation: just resolve it and if it resolves OK, return the string in the
|
|
// storage map.
|
|
Status =
|
|
RtlpGetActivationContextDataStorageMapAndRosterHeader(
|
|
((Flags & RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_PROCESS_DEFAULT)
|
|
? RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_PROCESS_DEFAULT
|
|
: 0)
|
|
| ((Flags & RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_SYSTEM_DEFAULT)
|
|
? RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_SYSTEM_DEFAULT
|
|
: 0),
|
|
Peb,
|
|
ActivationContext,
|
|
&ActivationContextData,
|
|
&AssemblyStorageMap,
|
|
&AssemblyRosterHeader
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: RtlGetAssemblyStorageRoot() unable to get activation context data, storage map and assembly roster header. Status = 0x%08lx\n", Status);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// It's possible that there wasn't anything...
|
|
if (ActivationContextData != NULL) {
|
|
ASSERT(AssemblyRosterHeader != NULL);
|
|
ASSERT(AssemblyStorageMap != NULL);
|
|
|
|
if ((AssemblyRosterHeader == NULL) || (AssemblyStorageMap == NULL)) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
if (AssemblyRosterIndex >= AssemblyRosterHeader->EntryCount) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters AssemblyRosterIndex 0x%lx "
|
|
">= AssemblyRosterHeader->EntryCount: 0x%lx\n",
|
|
__FUNCTION__,
|
|
AssemblyRosterIndex,
|
|
AssemblyRosterHeader->EntryCount
|
|
);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlpResolveAssemblyStorageMapEntry(AssemblyStorageMap, ActivationContextData, AssemblyRosterIndex, Callback, CallbackContext);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: RtlGetAssemblyStorageRoot() unable to resolve storage map entry. Status = 0x%08lx\n", Status);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// I guess we're done!
|
|
ASSERT(AssemblyStorageMap->AssemblyArray[AssemblyRosterIndex] != NULL);
|
|
if (AssemblyStorageMap->AssemblyArray[AssemblyRosterIndex] == NULL) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
*AssemblyStorageRoot = &AssemblyStorageMap->AssemblyArray[AssemblyRosterIndex]->DosPath;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlpGetActivationContextDataStorageMapAndRosterHeader(
|
|
ULONG Flags,
|
|
PPEB Peb,
|
|
PACTIVATION_CONTEXT ActivationContext,
|
|
PCACTIVATION_CONTEXT_DATA *ActivationContextData,
|
|
PASSEMBLY_STORAGE_MAP *AssemblyStorageMap,
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER *AssemblyRosterHeader
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER TempAssemblyRosterHeader = NULL;
|
|
PCACTIVATION_CONTEXT_DATA* TempActivationContextData = NULL;
|
|
PASSEMBLY_STORAGE_MAP* TempAssemblyStorageMap = NULL;
|
|
WCHAR LocalAssemblyDirectoryBuffer[DOS_MAX_PATH_LENGTH];
|
|
UNICODE_STRING LocalAssemblyDirectory = {0};
|
|
|
|
DbgPrintFunctionEntry(__FUNCTION__);
|
|
LocalAssemblyDirectoryBuffer[0] = 0;
|
|
LocalAssemblyDirectory.Length = 0;
|
|
LocalAssemblyDirectory.MaximumLength = sizeof(WCHAR);
|
|
LocalAssemblyDirectory.Buffer = LocalAssemblyDirectoryBuffer;
|
|
|
|
ASSERT(Peb != NULL);
|
|
RTLP_DISALLOW_THE_EMPTY_ACTIVATION_CONTEXT(ActivationContext);
|
|
|
|
if (ActivationContextData != NULL) {
|
|
*ActivationContextData = NULL;
|
|
}
|
|
|
|
if (AssemblyStorageMap != NULL) {
|
|
*AssemblyStorageMap = NULL;
|
|
}
|
|
|
|
if (AssemblyRosterHeader != NULL) {
|
|
*AssemblyRosterHeader = NULL;
|
|
}
|
|
|
|
if (
|
|
(Flags & ~(RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_PROCESS_DEFAULT
|
|
| RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_SYSTEM_DEFAULT))
|
|
||
|
|
(Peb == NULL) ||
|
|
(ActivationContextData == NULL) ||
|
|
(AssemblyStorageMap == NULL)) {
|
|
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s() bad parameters:\n"
|
|
"SXS: Flags : 0x%lx\n"
|
|
"SXS: Peb : %p\n"
|
|
"SXS: ActivationContextData: %p\n"
|
|
"SXS: AssemblyStorageMap : %p\n"
|
|
__FUNCTION__,
|
|
Flags,
|
|
Peb,
|
|
ActivationContextData,
|
|
AssemblyStorageMap
|
|
);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ActivationContext == ACTCTX_PROCESS_DEFAULT
|
|
|| ActivationContext == ACTCTX_SYSTEM_DEFAULT
|
|
|| (Flags & (
|
|
RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_PROCESS_DEFAULT
|
|
| RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_SYSTEM_DEFAULT))) {
|
|
|
|
//
|
|
// NOTE the ambiguity here. Maybe we'll clean this up.
|
|
//
|
|
// The flags override.
|
|
// ActivationContext == ACTCTX_PROCESS_DEFAULT could still be system default.
|
|
//
|
|
|
|
if (ActivationContext == ACTCTX_SYSTEM_DEFAULT
|
|
|| (Flags & RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_SYSTEM_DEFAULT)
|
|
) {
|
|
TempActivationContextData = (PACTIVATION_CONTEXT_DATA*)(&Peb->SystemDefaultActivationContextData);
|
|
TempAssemblyStorageMap = (PASSEMBLY_STORAGE_MAP*)(&Peb->SystemAssemblyStorageMap);
|
|
|
|
if (*TempActivationContextData != NULL) {
|
|
TempAssemblyRosterHeader = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) *TempActivationContextData) + (*TempActivationContextData)->AssemblyRosterOffset);
|
|
}
|
|
}
|
|
else if (ActivationContext == ACTCTX_PROCESS_DEFAULT || (Flags & RTLP_GET_ACTIVATION_CONTEXT_DATA_STORAGE_MAP_AND_ROSTER_HEADER_USE_PROCESS_DEFAULT)) {
|
|
TempActivationContextData = (PACTIVATION_CONTEXT_DATA*)(&Peb->ActivationContextData);
|
|
TempAssemblyStorageMap = (PASSEMBLY_STORAGE_MAP*)(&Peb->ProcessAssemblyStorageMap);
|
|
|
|
if (*TempActivationContextData != NULL) {
|
|
TempAssemblyRosterHeader = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) *TempActivationContextData) + (*TempActivationContextData)->AssemblyRosterOffset);
|
|
if (*TempAssemblyStorageMap == NULL) {
|
|
UNICODE_STRING ImagePathName;
|
|
|
|
// Capture the image path name so that we don't overrun allocated buffers because someone's
|
|
// randomly tweaking the RTL_USER_PROCESS_PARAMETERS.
|
|
ImagePathName = Peb->ProcessParameters->ImagePathName;
|
|
|
|
// The process default local assembly directory is the image name plus ".local".
|
|
// The process default private assembly directory is the image path.
|
|
if ((ImagePathName.Length + sizeof(LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX)) > sizeof(LocalAssemblyDirectoryBuffer)) {
|
|
if ((ImagePathName.Length + sizeof(LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX)) > UNICODE_STRING_MAX_BYTES) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
LocalAssemblyDirectory.MaximumLength = (USHORT) (ImagePathName.Length + sizeof(LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX));
|
|
|
|
LocalAssemblyDirectory.Buffer = (RtlAllocateStringRoutine)(LocalAssemblyDirectory.MaximumLength);
|
|
if (LocalAssemblyDirectory.Buffer == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
LocalAssemblyDirectory.MaximumLength = sizeof(LocalAssemblyDirectoryBuffer);
|
|
LocalAssemblyDirectory.Buffer = LocalAssemblyDirectoryBuffer;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
LocalAssemblyDirectory.Buffer,
|
|
ImagePathName.Buffer,
|
|
ImagePathName.Length);
|
|
|
|
RtlCopyMemory(
|
|
&LocalAssemblyDirectory.Buffer[ImagePathName.Length / sizeof(WCHAR)],
|
|
LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX,
|
|
sizeof(LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX));
|
|
|
|
LocalAssemblyDirectory.Length = ImagePathName.Length + sizeof(LOCAL_ASSEMBLY_STORAGE_DIR_SUFFIX) - sizeof(WCHAR);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
if (*TempActivationContextData != NULL) {
|
|
if (*TempAssemblyStorageMap == NULL) {
|
|
PASSEMBLY_STORAGE_MAP Map = (PASSEMBLY_STORAGE_MAP) RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(ASSEMBLY_STORAGE_MAP) + (TempAssemblyRosterHeader->EntryCount * sizeof(PASSEMBLY_STORAGE_MAP_ENTRY)));
|
|
if (Map == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
Status = RtlpInitializeAssemblyStorageMap(Map, TempAssemblyRosterHeader->EntryCount, (PASSEMBLY_STORAGE_MAP_ENTRY *) (Map + 1));
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Map);
|
|
goto Exit;
|
|
}
|
|
|
|
if (InterlockedCompareExchangePointer(TempAssemblyStorageMap, Map, NULL) != NULL) {
|
|
// We were not the first ones in. Free ours and use the one allocated.
|
|
RtlpUninitializeAssemblyStorageMap(Map);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Map);
|
|
}
|
|
}
|
|
} else {
|
|
ASSERT(*TempAssemblyStorageMap == NULL);
|
|
}
|
|
*AssemblyStorageMap = (PASSEMBLY_STORAGE_MAP) *TempAssemblyStorageMap;
|
|
} else {
|
|
TempActivationContextData = (PACTIVATION_CONTEXT_DATA*)(&ActivationContext->ActivationContextData);
|
|
|
|
ASSERT(*TempActivationContextData != NULL);
|
|
if (*TempActivationContextData == NULL) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
TempAssemblyRosterHeader = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) *TempActivationContextData) + (*TempActivationContextData)->AssemblyRosterOffset);
|
|
*AssemblyStorageMap = &ActivationContext->StorageMap;
|
|
}
|
|
|
|
if (ActivationContextData != NULL)
|
|
*ActivationContextData = *TempActivationContextData;
|
|
|
|
if (AssemblyRosterHeader != NULL)
|
|
*AssemblyRosterHeader = TempAssemblyRosterHeader;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
DbgPrintFunctionExit(__FUNCTION__, Status);
|
|
if ((LocalAssemblyDirectory.Buffer != NULL) &&
|
|
(LocalAssemblyDirectory.Buffer != LocalAssemblyDirectoryBuffer)) {
|
|
RtlFreeUnicodeString(&LocalAssemblyDirectory);
|
|
}
|
|
return Status;
|
|
}
|