/*++ Copyright (c) 1993 Microsoft Corporation Module Name: AliasSup.c Abstract: This module implements alias support for the Named Pipe file system. Author: Chuck Lenzmeier [chuckl] 16-Nov-1993 Revision History: --*/ #include "NpProcs.h" // // Registry path (relative to Services key) to alias list // #define ALIAS_PATH L"Npfs\\Aliases" // // The Alias record defines an aliased pipe name -- what the original // name is, and what it should be translated to. Alias records are // linked together in singly linked lists. // typedef struct _ALIAS { SINGLE_LIST_ENTRY ListEntry; PUNICODE_STRING TranslationString; UNICODE_STRING AliasString; } ALIAS, *PALIAS; // // ALIAS_CONTEXT is used during initialization to pass context to the // ReadAlias routine, which is called by RtlQueryRegistryValues. // typedef struct _ALIAS_CONTEXT { BOOLEAN Phase1; ULONG RequiredSize; ULONG AliasCount; ULONG TranslationCount; PALIAS NextAlias; PUNICODE_STRING NextTranslation; PWCH NextStringData; } ALIAS_CONTEXT, *PALIAS_CONTEXT; // // Forward declarations. // NTSTATUS ReadAlias ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,NpInitializeAliases) #pragma alloc_text(INIT,ReadAlias) #pragma alloc_text(PAGE, NpTranslateAlias) #endif NTSTATUS NpInitializeAliases ( VOID ) /*++ Routine Description: This routine initializes the alias package. It reads the registry, builds the alias list, and sorts it. Arguments: None. Return Value: NTSTATUS - Returns an error if the contents of the registry contents are invalid or if an allocation fails. --*/ { RTL_QUERY_REGISTRY_TABLE QueryTable[2]; ALIAS_CONTEXT Context; NTSTATUS Status; PALIAS Alias; ULONG i; ULONG Length; PSINGLE_LIST_ENTRY PreviousEntry; PSINGLE_LIST_ENTRY Entry; PALIAS TestAlias; // // Phase 1: Calculate number of aliases and size of alias buffer. // QueryTable[0].QueryRoutine = ReadAlias; QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; QueryTable[0].Name = NULL; QueryTable[0].EntryContext = NULL; QueryTable[0].DefaultType = REG_NONE; QueryTable[0].DefaultData = NULL; QueryTable[0].DefaultLength = 0; QueryTable[1].QueryRoutine = NULL; QueryTable[1].Flags = 0; QueryTable[1].Name = NULL; Context.Phase1 = TRUE; Context.RequiredSize = 0; Context.AliasCount = 0; Context.TranslationCount = 0; Status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, ALIAS_PATH, QueryTable, &Context, NULL ); // // If an error occurred, return that error, unless the alias // key wasn't present, which is not an error. Also, if the key // was there, but was empty, this is not an error. // if (!NT_SUCCESS(Status)) { if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) { Status = STATUS_SUCCESS; } return Status; } if (Context.RequiredSize == 0) { return STATUS_SUCCESS; } // // Allocate a buffer to hold the alias information. // NpAliases = ExAllocatePoolWithTag( NonPagedPool, Context.RequiredSize, 'sfpN' ); if (NpAliases == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Phase 2: Read alias information into the alias buffer. // Context.Phase1 = FALSE; Context.NextTranslation = (PUNICODE_STRING)NpAliases; Alias = Context.NextAlias = (PALIAS)(Context.NextTranslation + Context.TranslationCount); Context.NextStringData = (PWCH)(Context.NextAlias + Context.AliasCount); Status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, ALIAS_PATH, QueryTable, &Context, NULL ); if (!NT_SUCCESS(Status)) { ExFreePool( NpAliases ); NpAliases = NULL; return Status; } // // Phase 3: Link aliases into alias lists. // for ( i = 0; i < Context.AliasCount; i++, Alias++ ) { // // Point to the appropriate list head. // Length = Alias->AliasString.Length; if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)]; } else { PreviousEntry = &NpAliasList; } // // Walk the list to determine the proper place for this alias. // for ( Entry = PreviousEntry->Next; Entry != NULL; PreviousEntry = Entry, Entry = Entry->Next ) { TestAlias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); // // If the test alias is longer than the new alias, we want to // insert the new alias in front of the test alias. If the // test alias is shorter, we need to continue walking the list. // if ( TestAlias->AliasString.Length > Length ) break; if ( TestAlias->AliasString.Length < Length ) continue; // // The aliases are the same length. Compare them. If the new // alias is lexically before the test alias, we want to insert // it in front of the test alias. If it's after, we need to // keep walking. // // Alias and TestAlias should never have the same string, but // if they do, we'll insert the second occurrence of the string // immediately after the first one, and all will be well. // if ( _wcsicmp( Alias->AliasString.Buffer, TestAlias->AliasString.Buffer ) < 0 ) { break; } } // // We have found the place where this alias belongs. PreviousEntry // points to the alias that the new alias should follow. // (PreviousEntry may point to the list head.) // Alias->ListEntry.Next = PreviousEntry->Next; PreviousEntry->Next = &Alias->ListEntry; } #if 0 for ( Length = MIN_LENGTH_ALIAS_ARRAY; Length <= MAX_LENGTH_ALIAS_ARRAY + 2; Length += 2 ) { if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)]; DbgPrint( "Length %d list:\n", Length ); } else { PreviousEntry = &NpAliasList; DbgPrint( "Odd length list:\n" ); } for ( Entry = PreviousEntry->Next; Entry != NULL; Entry = Entry->Next ) { Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); DbgPrint( " %wZ -> %wZ\n", &Alias->AliasString, Alias->TranslationString ); } } #endif return STATUS_SUCCESS; } NTSTATUS ReadAlias ( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { PALIAS_CONTEXT Ctx = Context; USHORT Length; PWCH p; PUNICODE_STRING TranslationString; PALIAS Alias; // // The value must be a REG_MULTI_SZ value. // if (ValueType != REG_MULTI_SZ) { return STATUS_INVALID_PARAMETER; } // // In phase 1, we calculate the required size of the alias buffer. // In phase 2, we build the alias descriptors. // if ( Ctx->Phase1 ) { // // The value name is the translation. The value data is one or // more strings that are aliases for the translation. // // The "1+" and "sizeof(WCHAR)+" are for the '\' that will be // placed in front of the translation string and the alias string. // Ctx->TranslationCount++; Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR)); Ctx->RequiredSize += Length + sizeof(UNICODE_STRING); p = ValueData; while ( *p != 0 ) { Ctx->AliasCount++; Length = (USHORT)((wcslen(p) + 1) * sizeof(WCHAR)); Ctx->RequiredSize += sizeof(WCHAR) + Length + sizeof(ALIAS); p = (PWCH)((PCHAR)p + Length); } } else { // // Build a string descriptor for the translation string. // TranslationString = Ctx->NextTranslation++; Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR)); TranslationString->Length = Length - sizeof(WCHAR); TranslationString->MaximumLength = Length; TranslationString->Buffer = Ctx->NextStringData; Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length); // // Copy the string data. Place a '\' at the beginning. // TranslationString->Buffer[0] = L'\\'; RtlCopyMemory( &TranslationString->Buffer[1], ValueName, Length - sizeof(WCHAR) ); // // Build aliases descriptors. // p = ValueData; while ( *p != 0 ) { Alias = Ctx->NextAlias++; // // Point the alias descriptor to the translation string. // Alias->TranslationString = TranslationString; // // Build the alias string descriptor. // Length = (USHORT)((1 + wcslen(p) + 1) * sizeof(WCHAR)); Alias->AliasString.Length = Length - sizeof(WCHAR); Alias->AliasString.MaximumLength = Length; Alias->AliasString.Buffer = Ctx->NextStringData; Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length); // // Copy the string data. Place a '\' at the beginning. // Alias->AliasString.Buffer[0] = L'\\'; RtlCopyMemory( &Alias->AliasString.Buffer[1], p, Length - sizeof(WCHAR) ); // // Upcase the string. // RtlUpcaseUnicodeString( &Alias->AliasString, &Alias->AliasString, FALSE ); p = (PWCH)((PCHAR)p + Length - sizeof(WCHAR)); } } return STATUS_SUCCESS; } NTSTATUS NpTranslateAlias ( IN OUT PUNICODE_STRING String ) /*++ Routine Description: This routine translates a pipe name string based on information obtained from the registry at boot time. This translation is used to allow RPC services that had different names in NT 1.0 to have common names in 1.0a and beyond. Arguments: String - Supplies the input string to search for; returns the output string, if the name was translated. If so, the string points to a buffer allocated from paged pool. The caller should NOT free this buffer. Return Value: NTSTATUS - Returns STATUS_SUCCESS unless an allocation failure occurs. The status does NOT indicate whether the name was translated. --*/ { NTSTATUS Status; UNICODE_STRING UpcaseString; ULONG Length; PSINGLE_LIST_ENTRY Entry; PALIAS Alias; PWCH sp, ap; WCHAR a, s; BOOLEAN NoSlash; WCHAR UpcaseBuffer[MAX_LENGTH_ALIAS_ARRAY]; BOOLEAN FreeUpcaseBuffer; PAGED_CODE(); // // Before upcasing the string (a relatively expensive operation), // make sure that the string length matches at least one alias. // Length = String->Length; if ( Length == 0 ) { return STATUS_SUCCESS; } if ( *String->Buffer != L'\\' ) { Length += sizeof(WCHAR); NoSlash = TRUE; } else { NoSlash = FALSE; } if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { Entry = NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)].Next; Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); } else { Entry = NpAliasList.Next; while ( Entry != NULL ) { Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); if ( Alias->AliasString.Length == Length ) { break; } if ( Alias->AliasString.Length > Length ) { return STATUS_SUCCESS; } Entry = Entry->Next; } } if ( Entry == NULL ) { return STATUS_SUCCESS; } // // The string's length matches at least one alias. Upcase the string. // if ( Length <= MAX_LENGTH_ALIAS_ARRAY ) { UpcaseString.MaximumLength = MAX_LENGTH_ALIAS_ARRAY; UpcaseString.Buffer = UpcaseBuffer; Status = RtlUpcaseUnicodeString( &UpcaseString, String, FALSE ); ASSERT( NT_SUCCESS(Status) ); FreeUpcaseBuffer = FALSE; } else { Status = RtlUpcaseUnicodeString( &UpcaseString, String, TRUE ); if ( !NT_SUCCESS(Status) ) { return Status; } FreeUpcaseBuffer = TRUE; } ASSERT( UpcaseString.Length == (Length - (NoSlash ? sizeof(WCHAR) : 0)) ); // // At this point, Entry points to an alias list entry whose length // matches that of the input string. This list entry may be the // first element of a length-specific list (in which all entries // have the same length), or it may be an element of a length-ordered // list (in which case we'll need to check each next entry to see if // it's the same length. In both cases, strings of the same length // are in lexical order. // // Try to match the upcased string up to an alias. // do { sp = UpcaseString.Buffer; ap = Alias->AliasString.Buffer; if ( NoSlash ) { ap++; } while ( TRUE ) { a = *ap; if ( a == 0 ) { *String = *Alias->TranslationString; if ( NoSlash ) { String->Length -= sizeof(WCHAR); String->Buffer++; } goto exit; } s = *sp; if ( s < a ) goto exit; if ( s > a ) break; sp++; ap++; } // // The input string doesn't match the current alias. Move to // the next one. // Entry = Entry->Next; if ( Entry == NULL ) { goto exit; } Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); } while ( Alias->AliasString.Length == Length ); exit: if (FreeUpcaseBuffer) { ASSERT( UpcaseString.Buffer != UpcaseBuffer ); ExFreePool( UpcaseString.Buffer ); } return STATUS_SUCCESS; }