Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

603 lines
15 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. AliasSup.c
  5. Abstract:
  6. This module implements alias support for the Named Pipe file system.
  7. Author:
  8. Chuck Lenzmeier [chuckl] 16-Nov-1993
  9. Revision History:
  10. --*/
  11. #include "NpProcs.h"
  12. //
  13. // Registry path (relative to Services key) to alias list
  14. //
  15. #define ALIAS_PATH L"Npfs\\Aliases"
  16. //
  17. // The Alias record defines an aliased pipe name -- what the original
  18. // name is, and what it should be translated to. Alias records are
  19. // linked together in singly linked lists.
  20. //
  21. typedef struct _ALIAS {
  22. SINGLE_LIST_ENTRY ListEntry;
  23. PUNICODE_STRING TranslationString;
  24. UNICODE_STRING AliasString;
  25. } ALIAS, *PALIAS;
  26. //
  27. // ALIAS_CONTEXT is used during initialization to pass context to the
  28. // ReadAlias routine, which is called by RtlQueryRegistryValues.
  29. //
  30. typedef struct _ALIAS_CONTEXT {
  31. BOOLEAN Phase1;
  32. ULONG RequiredSize;
  33. ULONG AliasCount;
  34. ULONG TranslationCount;
  35. PALIAS NextAlias;
  36. PUNICODE_STRING NextTranslation;
  37. PWCH NextStringData;
  38. } ALIAS_CONTEXT, *PALIAS_CONTEXT;
  39. //
  40. // Forward declarations.
  41. //
  42. NTSTATUS
  43. NpReadAlias (
  44. IN PWSTR ValueName,
  45. IN ULONG ValueType,
  46. IN PVOID ValueData,
  47. IN ULONG ValueLength,
  48. IN PVOID Context,
  49. IN PVOID EntryContext
  50. );
  51. #ifdef ALLOC_PRAGMA
  52. #pragma alloc_text(INIT, NpInitializeAliases)
  53. #pragma alloc_text(INIT, NpReadAlias)
  54. #pragma alloc_text(PAGE, NpTranslateAlias)
  55. #pragma alloc_text(PAGE, NpUninitializeAliases)
  56. #endif
  57. NTSTATUS
  58. NpInitializeAliases (
  59. VOID
  60. )
  61. /*++
  62. Routine Description:
  63. This routine initializes the alias package. It reads the registry,
  64. builds the alias list, and sorts it.
  65. Arguments:
  66. None.
  67. Return Value:
  68. NTSTATUS - Returns an error if the contents of the registry contents
  69. are invalid or if an allocation fails.
  70. --*/
  71. {
  72. RTL_QUERY_REGISTRY_TABLE QueryTable[2];
  73. ALIAS_CONTEXT Context;
  74. NTSTATUS Status;
  75. PALIAS Alias;
  76. ULONG i;
  77. ULONG Length;
  78. PSINGLE_LIST_ENTRY PreviousEntry;
  79. PSINGLE_LIST_ENTRY Entry;
  80. PALIAS TestAlias;
  81. //
  82. // Phase 1: Calculate number of aliases and size of alias buffer.
  83. //
  84. QueryTable[0].QueryRoutine = NpReadAlias;
  85. QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
  86. QueryTable[0].Name = NULL;
  87. QueryTable[0].EntryContext = NULL;
  88. QueryTable[0].DefaultType = REG_NONE;
  89. QueryTable[0].DefaultData = NULL;
  90. QueryTable[0].DefaultLength = 0;
  91. QueryTable[1].QueryRoutine = NULL;
  92. QueryTable[1].Flags = 0;
  93. QueryTable[1].Name = NULL;
  94. Context.Phase1 = TRUE;
  95. Context.RequiredSize = 0;
  96. Context.AliasCount = 0;
  97. Context.TranslationCount = 0;
  98. Status = RtlQueryRegistryValues(
  99. RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
  100. ALIAS_PATH,
  101. QueryTable,
  102. &Context,
  103. NULL
  104. );
  105. //
  106. // If an error occurred, return that error, unless the alias
  107. // key wasn't present, which is not an error. Also, if the key
  108. // was there, but was empty, this is not an error.
  109. //
  110. if (!NT_SUCCESS(Status)) {
  111. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  112. Status = STATUS_SUCCESS;
  113. }
  114. return Status;
  115. }
  116. if (Context.RequiredSize == 0) {
  117. return STATUS_SUCCESS;
  118. }
  119. //
  120. // Allocate a buffer to hold the alias information.
  121. //
  122. NpAliases = NpAllocateNonPagedPool( Context.RequiredSize, 'sfpN');
  123. if (NpAliases == NULL) {
  124. return STATUS_INSUFFICIENT_RESOURCES;
  125. }
  126. //
  127. // Phase 2: Read alias information into the alias buffer.
  128. //
  129. Context.Phase1 = FALSE;
  130. Context.NextTranslation = (PUNICODE_STRING)NpAliases;
  131. Alias = Context.NextAlias =
  132. (PALIAS)(Context.NextTranslation + Context.TranslationCount);
  133. Context.NextStringData = (PWCH)(Context.NextAlias + Context.AliasCount);
  134. Status = RtlQueryRegistryValues(
  135. RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
  136. ALIAS_PATH,
  137. QueryTable,
  138. &Context,
  139. NULL
  140. );
  141. if (!NT_SUCCESS(Status)) {
  142. NpFreePool( NpAliases );
  143. NpAliases = NULL;
  144. return Status;
  145. }
  146. //
  147. // Phase 3: Link aliases into alias lists.
  148. //
  149. for ( i = 0;
  150. i < Context.AliasCount;
  151. i++, Alias++ ) {
  152. //
  153. // Point to the appropriate list head.
  154. //
  155. Length = Alias->AliasString.Length;
  156. if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) &&
  157. (Length <= MAX_LENGTH_ALIAS_ARRAY) ) {
  158. PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)];
  159. } else {
  160. PreviousEntry = &NpAliasList;
  161. }
  162. //
  163. // Walk the list to determine the proper place for this alias.
  164. //
  165. for ( Entry = PreviousEntry->Next;
  166. Entry != NULL;
  167. PreviousEntry = Entry, Entry = Entry->Next ) {
  168. TestAlias = CONTAINING_RECORD( Entry, ALIAS, ListEntry );
  169. //
  170. // If the test alias is longer than the new alias, we want to
  171. // insert the new alias in front of the test alias. If the
  172. // test alias is shorter, we need to continue walking the list.
  173. //
  174. if ( TestAlias->AliasString.Length > Length ) break;
  175. if ( TestAlias->AliasString.Length < Length ) continue;
  176. //
  177. // The aliases are the same length. Compare them. If the new
  178. // alias is lexically before the test alias, we want to insert
  179. // it in front of the test alias. If it's after, we need to
  180. // keep walking.
  181. //
  182. // Alias and TestAlias should never have the same string, but
  183. // if they do, we'll insert the second occurrence of the string
  184. // immediately after the first one, and all will be well.
  185. //
  186. if ( _wcsicmp( Alias->AliasString.Buffer,
  187. TestAlias->AliasString.Buffer ) < 0 ) {
  188. break;
  189. }
  190. }
  191. //
  192. // We have found the place where this alias belongs. PreviousEntry
  193. // points to the alias that the new alias should follow.
  194. // (PreviousEntry may point to the list head.)
  195. //
  196. Alias->ListEntry.Next = PreviousEntry->Next;
  197. PreviousEntry->Next = &Alias->ListEntry;
  198. }
  199. #if 0
  200. for ( Length = MIN_LENGTH_ALIAS_ARRAY;
  201. Length <= MAX_LENGTH_ALIAS_ARRAY + 2;
  202. Length += 2 ) {
  203. if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) &&
  204. (Length <= MAX_LENGTH_ALIAS_ARRAY) ) {
  205. PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)];
  206. DbgPrint( "Length %d list:\n", Length );
  207. } else {
  208. PreviousEntry = &NpAliasList;
  209. DbgPrint( "Odd length list:\n" );
  210. }
  211. for ( Entry = PreviousEntry->Next;
  212. Entry != NULL;
  213. Entry = Entry->Next ) {
  214. Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry );
  215. DbgPrint( " %wZ -> %wZ\n", &Alias->AliasString, Alias->TranslationString );
  216. }
  217. }
  218. #endif
  219. return STATUS_SUCCESS;
  220. }
  221. NTSTATUS
  222. NpReadAlias (
  223. IN PWSTR ValueName,
  224. IN ULONG ValueType,
  225. IN PVOID ValueData,
  226. IN ULONG ValueLength,
  227. IN PVOID Context,
  228. IN PVOID EntryContext
  229. )
  230. {
  231. PALIAS_CONTEXT Ctx = Context;
  232. USHORT Length;
  233. PWCH p;
  234. PUNICODE_STRING TranslationString;
  235. PALIAS Alias;
  236. //
  237. // The value must be a REG_MULTI_SZ value.
  238. //
  239. if (ValueType != REG_MULTI_SZ) {
  240. return STATUS_INVALID_PARAMETER;
  241. }
  242. //
  243. // In phase 1, we calculate the required size of the alias buffer.
  244. // In phase 2, we build the alias descriptors.
  245. //
  246. if ( Ctx->Phase1 ) {
  247. //
  248. // The value name is the translation. The value data is one or
  249. // more strings that are aliases for the translation.
  250. //
  251. // The "1+" and "sizeof(WCHAR)+" are for the '\' that will be
  252. // placed in front of the translation string and the alias string.
  253. //
  254. Ctx->TranslationCount++;
  255. Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR));
  256. Ctx->RequiredSize += Length + sizeof(UNICODE_STRING);
  257. p = ValueData;
  258. while ( *p != 0 ) {
  259. Ctx->AliasCount++;
  260. Length = (USHORT)((wcslen(p) + 1) * sizeof(WCHAR));
  261. Ctx->RequiredSize += sizeof(WCHAR) + Length + sizeof(ALIAS);
  262. p = (PWCH)((PCHAR)p + Length);
  263. }
  264. } else {
  265. //
  266. // Build a string descriptor for the translation string.
  267. //
  268. TranslationString = Ctx->NextTranslation++;
  269. Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR));
  270. TranslationString->Length = Length - sizeof(WCHAR);
  271. TranslationString->MaximumLength = Length;
  272. TranslationString->Buffer = Ctx->NextStringData;
  273. Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length);
  274. //
  275. // Copy the string data. Place a '\' at the beginning.
  276. //
  277. TranslationString->Buffer[0] = L'\\';
  278. RtlCopyMemory( &TranslationString->Buffer[1],
  279. ValueName,
  280. Length - sizeof(WCHAR) );
  281. //
  282. // Upcase the string.
  283. //
  284. RtlUpcaseUnicodeString( TranslationString,
  285. TranslationString,
  286. FALSE );
  287. //
  288. // Build aliases descriptors.
  289. //
  290. p = ValueData;
  291. while ( *p != 0 ) {
  292. Alias = Ctx->NextAlias++;
  293. //
  294. // Point the alias descriptor to the translation string.
  295. //
  296. Alias->TranslationString = TranslationString;
  297. //
  298. // Build the alias string descriptor.
  299. //
  300. Length = (USHORT)((1 + wcslen(p) + 1) * sizeof(WCHAR));
  301. Alias->AliasString.Length = Length - sizeof(WCHAR);
  302. Alias->AliasString.MaximumLength = Length;
  303. Alias->AliasString.Buffer = Ctx->NextStringData;
  304. Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length);
  305. //
  306. // Copy the string data. Place a '\' at the beginning.
  307. //
  308. Alias->AliasString.Buffer[0] = L'\\';
  309. RtlCopyMemory( &Alias->AliasString.Buffer[1],
  310. p,
  311. Length - sizeof(WCHAR) );
  312. //
  313. // Upcase the string.
  314. //
  315. RtlUpcaseUnicodeString( &Alias->AliasString,
  316. &Alias->AliasString,
  317. FALSE );
  318. p = (PWCH)((PCHAR)p + Length - sizeof(WCHAR));
  319. }
  320. }
  321. return STATUS_SUCCESS;
  322. }
  323. NTSTATUS
  324. NpTranslateAlias (
  325. IN OUT PUNICODE_STRING String
  326. )
  327. /*++
  328. Routine Description:
  329. This routine translates a pipe name string based on information
  330. obtained from the registry at boot time. This translation is used
  331. to allow RPC services that had different names in NT 1.0 to have
  332. common names in 1.0a and beyond.
  333. Arguments:
  334. String - Supplies the input string to search for; returns the output
  335. string, if the name was translated. If so, the string points to
  336. a buffer allocated from paged pool. The caller should NOT free
  337. this buffer.
  338. Return Value:
  339. NTSTATUS - Returns STATUS_SUCCESS unless an allocation failure occurs.
  340. The status does NOT indicate whether the name was translated.
  341. --*/
  342. {
  343. NTSTATUS Status;
  344. UNICODE_STRING UpcaseString;
  345. ULONG Length;
  346. PSINGLE_LIST_ENTRY Entry;
  347. PALIAS Alias;
  348. PWCH sp, ap;
  349. WCHAR a, s;
  350. BOOLEAN NoSlash;
  351. WCHAR UpcaseBuffer[MAX_LENGTH_ALIAS_ARRAY];
  352. BOOLEAN FreeUpcaseBuffer;
  353. PAGED_CODE();
  354. //
  355. // Before upcasing the string (a relatively expensive operation),
  356. // make sure that the string length matches at least one alias.
  357. //
  358. Length = String->Length;
  359. if ( Length == 0 ) {
  360. return STATUS_SUCCESS;
  361. }
  362. if ( *String->Buffer != L'\\' ) {
  363. Length += sizeof(WCHAR);
  364. NoSlash = TRUE;
  365. } else {
  366. NoSlash = FALSE;
  367. }
  368. if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) &&
  369. (Length <= MAX_LENGTH_ALIAS_ARRAY) ) {
  370. Entry = NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)].Next;
  371. Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry );
  372. } else {
  373. Entry = NpAliasList.Next;
  374. while ( Entry != NULL ) {
  375. Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry );
  376. if ( Alias->AliasString.Length == Length ) {
  377. break;
  378. }
  379. if ( Alias->AliasString.Length > Length ) {
  380. return STATUS_SUCCESS;
  381. }
  382. Entry = Entry->Next;
  383. }
  384. }
  385. if ( Entry == NULL ) {
  386. return STATUS_SUCCESS;
  387. }
  388. //
  389. // The string's length matches at least one alias. Upcase the string.
  390. //
  391. if ( Length <= MAX_LENGTH_ALIAS_ARRAY ) {
  392. UpcaseString.MaximumLength = MAX_LENGTH_ALIAS_ARRAY;
  393. UpcaseString.Buffer = UpcaseBuffer;
  394. Status = RtlUpcaseUnicodeString( &UpcaseString, String, FALSE );
  395. ASSERT( NT_SUCCESS(Status) );
  396. FreeUpcaseBuffer = FALSE;
  397. } else {
  398. Status = RtlUpcaseUnicodeString( &UpcaseString, String, TRUE );
  399. if ( !NT_SUCCESS(Status) ) {
  400. return Status;
  401. }
  402. FreeUpcaseBuffer = TRUE;
  403. }
  404. ASSERT( UpcaseString.Length == (Length - (NoSlash ? sizeof(WCHAR) : 0)) );
  405. //
  406. // At this point, Entry points to an alias list entry whose length
  407. // matches that of the input string. This list entry may be the
  408. // first element of a length-specific list (in which all entries
  409. // have the same length), or it may be an element of a length-ordered
  410. // list (in which case we'll need to check each next entry to see if
  411. // it's the same length. In both cases, strings of the same length
  412. // are in lexical order.
  413. //
  414. // Try to match the upcased string up to an alias.
  415. //
  416. do {
  417. sp = UpcaseString.Buffer;
  418. ap = Alias->AliasString.Buffer;
  419. if ( NoSlash ) {
  420. ap++;
  421. }
  422. while ( TRUE ) {
  423. a = *ap;
  424. if ( a == 0 ) {
  425. *String = *Alias->TranslationString;
  426. if ( NoSlash ) {
  427. String->Length -= sizeof(WCHAR);
  428. String->Buffer++;
  429. }
  430. goto exit;
  431. }
  432. s = *sp;
  433. if ( s < a ) goto exit;
  434. if ( s > a ) break;
  435. sp++;
  436. ap++;
  437. }
  438. //
  439. // The input string doesn't match the current alias. Move to
  440. // the next one.
  441. //
  442. Entry = Entry->Next;
  443. if ( Entry == NULL ) {
  444. goto exit;
  445. }
  446. Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry );
  447. } while ( Alias->AliasString.Length == Length );
  448. exit:
  449. if (FreeUpcaseBuffer) {
  450. ASSERT( UpcaseString.Buffer != UpcaseBuffer );
  451. NpFreePool( UpcaseString.Buffer );
  452. }
  453. return STATUS_SUCCESS;
  454. }
  455. VOID
  456. NpUninitializeAliases (
  457. VOID
  458. )
  459. /*++
  460. Routine Description:
  461. This routine uninitializes the alias package.
  462. Arguments:
  463. None.
  464. Return Value:
  465. None
  466. --*/
  467. {
  468. NpFreePool( NpAliases );
  469. }