Leaked source code of windows server 2003
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.

464 lines
14 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name:
  4. strcnv.c
  5. Abstract:
  6. This module implements Ansi/Unicode conversion with a specific code page.
  7. Environment:
  8. Kernel mode
  9. --*/
  10. #include "precomp.hxx"
  11. #define TRC_FILE "strcnv"
  12. #include "trc.h"
  13. #include <wchar.h>
  14. #define DEBUG_MODULE MODULE_STRCNV
  15. /****************************************************************************/
  16. /* Code page based driver compatible Unicode translations */
  17. /****************************************************************************/
  18. #ifdef __cplusplus
  19. extern "C" {
  20. #endif // __cplusplus
  21. NTSYSAPI
  22. VOID
  23. NTAPI
  24. RtlGetDefaultCodePage(
  25. OUT PUSHORT AnsiCodePage,
  26. OUT PUSHORT OemCodePage
  27. );
  28. #ifdef __cplusplus
  29. } // extern "C"
  30. #endif // __cplusplus
  31. // Note these are initialized and LastNlsTableBuffer is freed in ntdd.c
  32. // at driver entry and exit.
  33. FAST_MUTEX fmCodePage;
  34. ULONG LastCodePageTranslated; // I'm assuming 0 is not a valid codepage
  35. PVOID LastNlsTableBuffer;
  36. CPTABLEINFO LastCPTableInfo;
  37. UINT NlsTableUseCount;
  38. #define NLS_TABLE_KEY \
  39. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\CodePage"
  40. VOID
  41. CodePageConversionInitialize(
  42. )
  43. {
  44. BEGIN_FN("CodePageConversionInitialize");
  45. ExInitializeFastMutex(&fmCodePage);
  46. LastCodePageTranslated = 0;
  47. LastNlsTableBuffer = NULL;
  48. NlsTableUseCount = 0;
  49. }
  50. VOID
  51. CodePageConversionCleanup(
  52. )
  53. {
  54. BEGIN_FN("CodePageConversionCleanup");
  55. if (LastNlsTableBuffer != NULL) {
  56. delete LastNlsTableBuffer;
  57. LastNlsTableBuffer = NULL;
  58. }
  59. }
  60. BOOL GetNlsTablePath(
  61. UINT CodePage,
  62. PWCHAR PathBuffer
  63. )
  64. /*++
  65. Routine Description:
  66. This routine takes a code page identifier, queries the registry to find the
  67. appropriate NLS table for that code page, and then returns a path to the
  68. table.
  69. Arguments;
  70. CodePage - specifies the code page to look for
  71. PathBuffer - Specifies a buffer into which to copy the path of the NLS
  72. file. This routine assumes that the size is at least MAX_PATH
  73. Return Value:
  74. TRUE if successful, FALSE otherwise.
  75. Gerrit van Wingerden [gerritv] 1/22/96
  76. -*/
  77. {
  78. NTSTATUS NtStatus;
  79. BOOL Result = FALSE;
  80. HANDLE RegistryKeyHandle;
  81. OBJECT_ATTRIBUTES ObjectAttributes;
  82. UNICODE_STRING UnicodeString;
  83. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  84. BEGIN_FN("GetNlsTablePath");
  85. RtlInitUnicodeString(&UnicodeString, NLS_TABLE_KEY);
  86. InitializeObjectAttributes(&ObjectAttributes,
  87. &UnicodeString,
  88. OBJ_CASE_INSENSITIVE,
  89. NULL,
  90. NULL);
  91. NtStatus = ZwOpenKey(&RegistryKeyHandle, GENERIC_READ, &ObjectAttributes);
  92. if(NT_SUCCESS(NtStatus))
  93. {
  94. WCHAR *ResultBuffer;
  95. ULONG BufferSize = sizeof(WCHAR) * MAX_PATH +
  96. sizeof(KEY_VALUE_FULL_INFORMATION);
  97. ResultBuffer = new(PagedPool) WCHAR[BufferSize];
  98. if(ResultBuffer)
  99. {
  100. ULONG ValueReturnedLength;
  101. WCHAR CodePageStringBuffer[20];
  102. RtlZeroMemory(ResultBuffer, BufferSize);
  103. swprintf(CodePageStringBuffer, L"%d", CodePage);
  104. RtlInitUnicodeString(&UnicodeString,CodePageStringBuffer);
  105. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) ResultBuffer;
  106. NtStatus = ZwQueryValueKey(RegistryKeyHandle,
  107. &UnicodeString,
  108. KeyValuePartialInformation,
  109. KeyValueInformation,
  110. BufferSize,
  111. &BufferSize);
  112. if(NT_SUCCESS(NtStatus))
  113. {
  114. swprintf(PathBuffer,L"\\SystemRoot\\System32\\%ws",
  115. &(KeyValueInformation->Data[0]));
  116. Result = TRUE;
  117. }
  118. else
  119. {
  120. TRC_ERR((TB, "GetNlsTablePath failed to get NLS table"));
  121. }
  122. delete ResultBuffer;
  123. }
  124. else
  125. {
  126. TRC_ERR((TB, "GetNlsTablePath out of memory"));
  127. }
  128. ZwClose(RegistryKeyHandle);
  129. }
  130. else
  131. {
  132. TRC_ERR((TB, "GetNlsTablePath failed to open NLS key"));
  133. }
  134. return(Result);
  135. }
  136. INT ConvertToAndFromWideChar(
  137. UINT CodePage,
  138. LPWSTR WideCharString,
  139. INT BytesInWideCharString,
  140. LPSTR MultiByteString,
  141. INT BytesInMultiByteString,
  142. BOOLEAN ConvertToWideChar
  143. )
  144. /*++
  145. Routine Description:
  146. This routine converts a character string to or from a wide char string
  147. assuming a specified code page. Most of the actual work is done inside
  148. RtlCustomCPToUnicodeN, but this routine still needs to manage the loading
  149. of the NLS files before passing them to the RtlRoutine. We will cache
  150. the mapped NLS file for the most recently used code page which ought to
  151. suffice for out purposes.
  152. Arguments:
  153. CodePage - the code page to use for doing the translation.
  154. WideCharString - buffer the string is to be translated into.
  155. BytesInWideCharString - number of bytes in the WideCharString buffer
  156. if converting to wide char and the buffer isn't large enough then the
  157. string in truncated and no error results.
  158. MultiByteString - the multibyte string to be translated to Unicode.
  159. BytesInMultiByteString - number of bytes in the multibyte string if
  160. converting to multibyte and the buffer isn't large enough the string
  161. is truncated and no error results
  162. ConvertToWideChar - if TRUE then convert from multibyte to widechar
  163. otherwise convert from wide char to multibyte
  164. Return Value:
  165. Success - The number of bytes in the converted WideCharString
  166. Failure - -1
  167. Gerrit van Wingerden [gerritv] 1/22/96
  168. -*/
  169. {
  170. NTSTATUS NtStatus;
  171. USHORT OemCodePage, AnsiCodePage;
  172. CPTABLEINFO LocalTableInfo;
  173. PCPTABLEINFO TableInfo = NULL;
  174. PVOID LocalTableBase = NULL;
  175. ULONG BytesConverted = 0;
  176. BEGIN_FN("ConvertToAndFromWideChar");
  177. RtlGetDefaultCodePage(&AnsiCodePage,&OemCodePage);
  178. // see if we can use the default translation routinte
  179. if ((AnsiCodePage == CodePage) || (CodePage == 0))
  180. {
  181. if(ConvertToWideChar)
  182. {
  183. NtStatus = RtlMultiByteToUnicodeN(WideCharString,
  184. BytesInWideCharString,
  185. &BytesConverted,
  186. MultiByteString,
  187. BytesInMultiByteString);
  188. }
  189. else
  190. {
  191. NtStatus = RtlUnicodeToMultiByteN(MultiByteString,
  192. BytesInMultiByteString,
  193. &BytesConverted,
  194. WideCharString,
  195. BytesInWideCharString);
  196. }
  197. if(NT_SUCCESS(NtStatus))
  198. {
  199. return(BytesConverted);
  200. }
  201. else
  202. {
  203. return(-1);
  204. }
  205. }
  206. ExAcquireFastMutex(&fmCodePage);
  207. if(CodePage == LastCodePageTranslated)
  208. {
  209. // we can use the cached code page information
  210. TableInfo = &LastCPTableInfo;
  211. NlsTableUseCount += 1;
  212. }
  213. ExReleaseFastMutex(&fmCodePage);
  214. if(TableInfo == NULL)
  215. {
  216. // get a pointer to the path of the NLS table
  217. WCHAR NlsTablePath[MAX_PATH];
  218. if(GetNlsTablePath(CodePage,NlsTablePath))
  219. {
  220. UNICODE_STRING UnicodeString;
  221. IO_STATUS_BLOCK IoStatus;
  222. HANDLE NtFileHandle;
  223. OBJECT_ATTRIBUTES ObjectAttributes;
  224. RtlInitUnicodeString(&UnicodeString,NlsTablePath);
  225. InitializeObjectAttributes(&ObjectAttributes,
  226. &UnicodeString,
  227. OBJ_CASE_INSENSITIVE,
  228. NULL,
  229. NULL);
  230. NtStatus = ZwCreateFile(&NtFileHandle,
  231. SYNCHRONIZE | FILE_READ_DATA,
  232. &ObjectAttributes,
  233. &IoStatus,
  234. NULL,
  235. 0,
  236. FILE_SHARE_READ,
  237. FILE_OPEN,
  238. FILE_SYNCHRONOUS_IO_NONALERT,
  239. NULL,
  240. 0);
  241. if(NT_SUCCESS(NtStatus))
  242. {
  243. FILE_STANDARD_INFORMATION StandardInfo;
  244. // Query the object to determine its length.
  245. NtStatus = ZwQueryInformationFile(NtFileHandle,
  246. &IoStatus,
  247. &StandardInfo,
  248. sizeof(FILE_STANDARD_INFORMATION),
  249. FileStandardInformation);
  250. if(NT_SUCCESS(NtStatus))
  251. {
  252. UINT LengthOfFile = StandardInfo.EndOfFile.LowPart;
  253. LocalTableBase = new(PagedPool) BYTE[LengthOfFile];
  254. if(LocalTableBase)
  255. {
  256. RtlZeroMemory(LocalTableBase, LengthOfFile);
  257. // Read the file into our buffer.
  258. NtStatus = ZwReadFile(NtFileHandle,
  259. NULL,
  260. NULL,
  261. NULL,
  262. &IoStatus,
  263. LocalTableBase,
  264. LengthOfFile,
  265. NULL,
  266. NULL);
  267. if(!NT_SUCCESS(NtStatus))
  268. {
  269. TRC_ERR((TB, "WDMultiByteToWideChar unable to read file"));
  270. delete LocalTableBase;
  271. LocalTableBase = NULL;
  272. }
  273. }
  274. else
  275. {
  276. TRC_ERR((TB, "WDMultiByteToWideChar out of memory"));
  277. }
  278. }
  279. else
  280. {
  281. TRC_ERR((TB, "WDMultiByteToWideChar unable query NLS file"));
  282. }
  283. ZwClose(NtFileHandle);
  284. }
  285. else
  286. {
  287. TRC_ERR((TB, "EngMultiByteToWideChar unable to open NLS file"));
  288. }
  289. }
  290. else
  291. {
  292. TRC_ERR((TB, "EngMultiByteToWideChar get registry entry for NLS file failed"));
  293. }
  294. if(LocalTableBase == NULL)
  295. {
  296. return(-1);
  297. }
  298. // now that we've got the table use it to initialize the CodePage table
  299. RtlInitCodePageTable((USHORT *)LocalTableBase,&LocalTableInfo);
  300. TableInfo = &LocalTableInfo;
  301. }
  302. // Once we are here TableInfo points to the the CPTABLEINFO struct we want
  303. if(ConvertToWideChar)
  304. {
  305. NtStatus = RtlCustomCPToUnicodeN(TableInfo,
  306. WideCharString,
  307. BytesInWideCharString,
  308. &BytesConverted,
  309. MultiByteString,
  310. BytesInMultiByteString);
  311. }
  312. else
  313. {
  314. NtStatus = RtlUnicodeToCustomCPN(TableInfo,
  315. MultiByteString,
  316. BytesInMultiByteString,
  317. &BytesConverted,
  318. WideCharString,
  319. BytesInWideCharString);
  320. }
  321. if(!NT_SUCCESS(NtStatus))
  322. {
  323. // signal failure
  324. BytesConverted = -1;
  325. }
  326. // see if we need to update the cached CPTABLEINFO information
  327. if(TableInfo != &LocalTableInfo)
  328. {
  329. // we must have used the cached CPTABLEINFO data for the conversion
  330. // simple decrement the reference count
  331. ExAcquireFastMutex(&fmCodePage);
  332. NlsTableUseCount -= 1;
  333. ExReleaseFastMutex(&fmCodePage);
  334. }
  335. else
  336. {
  337. PVOID FreeTable;
  338. // we must have just allocated a new CPTABLE structure so cache it
  339. // unless another thread is using current cached entry
  340. ExAcquireFastMutex(&fmCodePage);
  341. if(!NlsTableUseCount)
  342. {
  343. LastCodePageTranslated = CodePage;
  344. RtlMoveMemory(&LastCPTableInfo, TableInfo, sizeof(CPTABLEINFO));
  345. FreeTable = LastNlsTableBuffer;
  346. LastNlsTableBuffer = LocalTableBase;
  347. }
  348. else
  349. {
  350. FreeTable = LocalTableBase;
  351. }
  352. ExReleaseFastMutex(&fmCodePage);
  353. // Now free the memory for either the old table or the one we allocated
  354. // depending on whether we update the cache. Note that if this is
  355. // the first time we are adding a cached value to the local table, then
  356. // FreeTable will be NULL since LastNlsTableBuffer will be NULL
  357. if(FreeTable)
  358. {
  359. delete FreeTable;
  360. }
  361. }
  362. // we are done
  363. return(BytesConverted);
  364. }