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.

520 lines
14 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. rtlutils.cpp
  5. Abstract:
  6. Contains functions from ntdll on XP
  7. that are not available on W2K.
  8. History:
  9. 09/10/2001 rparsons Created
  10. --*/
  11. #include "rtlutils.h"
  12. namespace ShimLib
  13. {
  14. #define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
  15. extern const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING( L"\\??\\" );
  16. extern const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING( L"\\??\\UNC\\" );
  17. const UNICODE_STRING RtlpEmptyString = RTL_CONSTANT_STRING(L"");
  18. //
  19. // Taken from %SDXROOT%\public\sdk\inc\ntrtl.h
  20. //
  21. #if DBG
  22. #undef ASSERT
  23. #define ASSERT( exp ) \
  24. ((!(exp)) ? \
  25. (RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
  26. TRUE)
  27. #else
  28. #undef ASSERT
  29. #define ASSERT( exp ) ((void) 0)
  30. #endif
  31. #define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
  32. || (x) == STATUS_OBJECT_NAME_NOT_FOUND \
  33. ) \
  34. ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
  35. //
  36. // These functions were taken from:
  37. // %SDXROOT%\base\ntdll\ldrinit.c
  38. //
  39. PVOID
  40. ShimAllocateStringRoutine(
  41. SIZE_T NumberOfBytes
  42. )
  43. {
  44. return RtlAllocateHeap(RtlProcessHeap(), 0, NumberOfBytes);
  45. }
  46. VOID
  47. ShimFreeStringRoutine(
  48. PVOID Buffer
  49. )
  50. {
  51. RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
  52. }
  53. //
  54. // These functions were pulled from:
  55. // %SDXROOT%\base\ntdll\curdir.c
  56. //
  57. RTL_PATH_TYPE
  58. NTAPI
  59. ShimDetermineDosPathNameType_Ustr(
  60. IN PCUNICODE_STRING String
  61. )
  62. /*++
  63. Routine Description:
  64. This function examines the Dos format file name and determines the
  65. type of file name (i.e. UNC, DriveAbsolute, Current Directory
  66. rooted, or Relative.)
  67. Arguments:
  68. DosFileName - Supplies the Dos format file name whose type is to be
  69. determined.
  70. Return Value:
  71. RtlPathTypeUnknown - The path type can not be determined
  72. RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
  73. in the format \\server-name\sharename\rest-of-path
  74. RtlPathTypeLocalDevice - The path specifies a local device in the format
  75. \\.\rest-of-path or \\?\rest-of-path. This can be used for any device
  76. where the nt and Win32 names are the same. For example mailslots.
  77. RtlPathTypeRootLocalDevice - The path specifies the root of the local
  78. devices in the format \\. or \\?
  79. RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
  80. path in the form drive:\rest-of-path
  81. RtlPathTypeDriveRelative - The path specifies a drive letter relative
  82. path in the form drive:rest-of-path
  83. RtlPathTypeRooted - The path is rooted relative to the current disk
  84. designator (either Unc disk, or drive). The form is \rest-of-path.
  85. RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
  86. --*/
  87. {
  88. RTL_PATH_TYPE ReturnValue;
  89. const PCWSTR DosFileName = String->Buffer;
  90. #define ENOUGH_CHARS(_cch) (String->Length >= ((_cch) * sizeof(WCHAR)))
  91. if ( ENOUGH_CHARS(1) && IS_PATH_SEPARATOR_U(*DosFileName) ) {
  92. if ( ENOUGH_CHARS(2) && IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
  93. if ( ENOUGH_CHARS(3) && (DosFileName[2] == '.' ||
  94. DosFileName[2] == '?') ) {
  95. if ( ENOUGH_CHARS(4) && IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
  96. // "\\.\" or "\\?\"
  97. ReturnValue = RtlPathTypeLocalDevice;
  98. }
  99. //
  100. // Bogosity ahead, the code is confusing length and nuls,
  101. // because it was copy/pasted from the PCWSTR version.
  102. //
  103. else if ( ENOUGH_CHARS(4) && (*(DosFileName+3)) == UNICODE_NULL ){
  104. // "\\.\0" or \\?\0"
  105. ReturnValue = RtlPathTypeRootLocalDevice;
  106. }
  107. else {
  108. // "\\.x" or "\\." or "\\?x" or "\\?"
  109. ReturnValue = RtlPathTypeUncAbsolute;
  110. }
  111. }
  112. else {
  113. // "\\x"
  114. ReturnValue = RtlPathTypeUncAbsolute;
  115. }
  116. }
  117. else {
  118. // "\x"
  119. ReturnValue = RtlPathTypeRooted;
  120. }
  121. }
  122. //
  123. // the "*DosFileName" is left over from the PCWSTR version
  124. // Win32 and DOS don't allow embedded nuls and much code limits
  125. // drive letters to strictly 7bit a-zA-Z so it's ok.
  126. //
  127. else if (ENOUGH_CHARS(2) && *DosFileName && *(DosFileName+1)==L':') {
  128. if (ENOUGH_CHARS(3) && IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
  129. // "x:\"
  130. ReturnValue = RtlPathTypeDriveAbsolute;
  131. }
  132. else {
  133. // "c:x"
  134. ReturnValue = RtlPathTypeDriveRelative;
  135. }
  136. }
  137. else {
  138. // "x", first char is not a slash / second char is not colon
  139. ReturnValue = RtlPathTypeRelative;
  140. }
  141. return ReturnValue;
  142. #undef ENOUGH_CHARS
  143. }
  144. NTSTATUS
  145. NTAPI
  146. ShimNtPathNameToDosPathName(
  147. IN ULONG Flags,
  148. IN OUT PRTL_UNICODE_STRING_BUFFER Path,
  149. OUT ULONG* Disposition OPTIONAL,
  150. IN OUT PWSTR* FilePart OPTIONAL
  151. )
  152. {
  153. NTSTATUS Status = STATUS_SUCCESS;
  154. SIZE_T NtFilePartOffset = 0;
  155. SIZE_T DosFilePartOffset = 0;
  156. BOOLEAN Unc = FALSE;
  157. const static UNICODE_STRING DosUncPrefix = RTL_CONSTANT_STRING(L"\\\\");
  158. PCUNICODE_STRING NtPrefix = NULL;
  159. PCUNICODE_STRING DosPrefix = NULL;
  160. RTL_STRING_LENGTH_TYPE Cch = 0;
  161. if (ARGUMENT_PRESENT(Disposition)) {
  162. *Disposition = 0;
  163. }
  164. if ( !RTL_SOFT_VERIFY(Path != NULL)
  165. || !RTL_SOFT_VERIFY(Flags == 0)
  166. ) {
  167. Status = STATUS_INVALID_PARAMETER;
  168. goto Exit;
  169. }
  170. if (ARGUMENT_PRESENT(FilePart) && *FilePart != NULL) {
  171. NtFilePartOffset = *FilePart - Path->String.Buffer;
  172. if (!RTL_SOFT_VERIFY(NtFilePartOffset < RTL_STRING_GET_LENGTH_CHARS(&Path->String))
  173. ) {
  174. Status = STATUS_INVALID_PARAMETER;
  175. goto Exit;
  176. }
  177. }
  178. if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesUncPrefix), &Path->String, TRUE)
  179. ) {
  180. NtPrefix = &RtlpDosDevicesUncPrefix;
  181. DosPrefix = &DosUncPrefix;
  182. if (ARGUMENT_PRESENT(Disposition)) {
  183. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC;
  184. }
  185. }
  186. else if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), &Path->String, TRUE)
  187. ) {
  188. NtPrefix = &RtlpDosDevicesPrefix;
  189. DosPrefix = &RtlpEmptyString;
  190. if (ARGUMENT_PRESENT(Disposition)) {
  191. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE;
  192. }
  193. }
  194. else {
  195. //
  196. // It is not recognizably an Nt path produced by RtlDosPathNameToNtPathName_U.
  197. //
  198. if (ARGUMENT_PRESENT(Disposition)) {
  199. RTL_PATH_TYPE PathType = ShimDetermineDosPathNameType_Ustr(&Path->String);
  200. switch (PathType) {
  201. case RtlPathTypeUnknown:
  202. case RtlPathTypeRooted: // NT paths are identified as this
  203. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
  204. break;
  205. //
  206. // "already" dospaths, but not gotten from this function, let's
  207. // give a less good disposition
  208. //
  209. case RtlPathTypeDriveRelative:
  210. case RtlPathTypeRelative:
  211. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
  212. break;
  213. // these are pretty clearly dospaths already
  214. case RtlPathTypeUncAbsolute:
  215. case RtlPathTypeDriveAbsolute:
  216. case RtlPathTypeLocalDevice: // "\\?\" or "\\.\" or "\\?\blah" or "\\.\blah"
  217. case RtlPathTypeRootLocalDevice: // "\\?" or "\\."
  218. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS;
  219. break;
  220. }
  221. }
  222. goto Exit;
  223. }
  224. Cch =
  225. RTL_STRING_GET_LENGTH_CHARS(&Path->String)
  226. + RTL_STRING_GET_LENGTH_CHARS(DosPrefix)
  227. - RTL_STRING_GET_LENGTH_CHARS(NtPrefix);
  228. Status =
  229. ShimEnsureUnicodeStringBufferSizeChars(Path, Cch);
  230. if (!NT_SUCCESS(Status)) {
  231. goto Exit;
  232. }
  233. //
  234. // overlapping buffer shuffle...careful.
  235. //
  236. RtlMoveMemory(
  237. Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(DosPrefix),
  238. Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(NtPrefix),
  239. Path->String.Length - NtPrefix->Length
  240. );
  241. RtlMoveMemory(
  242. Path->String.Buffer,
  243. DosPrefix->Buffer,
  244. DosPrefix->Length
  245. );
  246. Path->String.Length = Cch * sizeof(Path->String.Buffer[0]);
  247. RTL_NUL_TERMINATE_STRING(&Path->String);
  248. if (NtFilePartOffset != 0) {
  249. // review/test..
  250. *FilePart = Path->String.Buffer + (NtFilePartOffset - RTL_STRING_GET_LENGTH_CHARS(NtPrefix) + RTL_STRING_GET_LENGTH_CHARS(DosPrefix));
  251. }
  252. Status = STATUS_SUCCESS;
  253. Exit:
  254. /* KdPrintEx((
  255. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  256. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status)); */
  257. return Status;
  258. }
  259. NTSTATUS
  260. ShimValidateUnicodeString(
  261. ULONG Flags,
  262. const UNICODE_STRING *String
  263. )
  264. {
  265. NTSTATUS Status = STATUS_SUCCESS;
  266. ASSERT(Flags == 0);
  267. if (Flags != 0) {
  268. Status = STATUS_INVALID_PARAMETER;
  269. goto Exit;
  270. }
  271. if (String != NULL) {
  272. if (((String->Length % 2) != 0) ||
  273. ((String->MaximumLength % 2) != 0) ||
  274. (String->Length > String->MaximumLength)) {
  275. Status = STATUS_INVALID_PARAMETER;
  276. goto Exit;
  277. }
  278. if (((String->Length != 0) ||
  279. (String->MaximumLength != 0)) &&
  280. (String->Buffer == NULL)) {
  281. Status = STATUS_INVALID_PARAMETER;
  282. goto Exit;
  283. }
  284. }
  285. Status = STATUS_SUCCESS;
  286. Exit:
  287. return Status;
  288. }
  289. //
  290. // This function was taken from:
  291. // %SDXROOT%\base\ntos\rtl\nls.c
  292. //
  293. NTSTATUS
  294. ShimDuplicateUnicodeString(
  295. ULONG Flags,
  296. PCUNICODE_STRING StringIn,
  297. PUNICODE_STRING StringOut
  298. )
  299. {
  300. NTSTATUS Status = STATUS_SUCCESS;
  301. USHORT Length = 0;
  302. USHORT NewMaximumLength = 0;
  303. PWSTR Buffer = NULL;
  304. if (((Flags & ~(
  305. RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
  306. RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING)) != 0) ||
  307. (StringOut == NULL)) {
  308. Status = STATUS_INVALID_PARAMETER;
  309. goto Exit;
  310. }
  311. // It doesn't make sense to force allocation of a null string unless you
  312. // want null termination.
  313. if ((Flags & RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING) &&
  314. !(Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)) {
  315. Status = STATUS_INVALID_PARAMETER;
  316. goto Exit;
  317. }
  318. Status = ShimValidateUnicodeString(0, StringIn);
  319. if (!NT_SUCCESS(Status))
  320. goto Exit;
  321. if (StringIn != NULL)
  322. Length = StringIn->Length;
  323. if ((Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) &&
  324. (Length == UNICODE_STRING_MAX_BYTES)) {
  325. Status = STATUS_NAME_TOO_LONG;
  326. goto Exit;
  327. }
  328. if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
  329. NewMaximumLength = (USHORT) (Length + sizeof(WCHAR));
  330. else
  331. NewMaximumLength = Length;
  332. // If it's a zero length string in, force the allocation length to zero
  333. // unless the caller said that they want zero length strings allocated.
  334. if (((Flags & RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING) == 0) &&
  335. (Length == 0)) {
  336. NewMaximumLength = 0;
  337. }
  338. if (NewMaximumLength != 0) {
  339. Buffer = (PWSTR)(RtlAllocateStringRoutine)(NewMaximumLength);
  340. if (Buffer == NULL) {
  341. Status = STATUS_NO_MEMORY;
  342. goto Exit;
  343. }
  344. // If there's anything to copy, copy it. We explicitly test Length because
  345. // StringIn could be a NULL pointer, so dereferencing it to get the Buffer
  346. // pointer would access violate.
  347. if (Length != 0) {
  348. RtlCopyMemory(
  349. Buffer,
  350. StringIn->Buffer,
  351. Length);
  352. }
  353. if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) {
  354. Buffer[Length / sizeof(WCHAR)] = L'\0';
  355. }
  356. }
  357. StringOut->Buffer = Buffer;
  358. StringOut->MaximumLength = NewMaximumLength;
  359. StringOut->Length = Length;
  360. Status = STATUS_SUCCESS;
  361. Exit:
  362. return Status;
  363. }
  364. //
  365. // This function was pulled from:
  366. // %SDXROOT%\base\ntdll\buffer.c
  367. //
  368. NTSTATUS
  369. NTAPI
  370. ShimpEnsureBufferSize(
  371. IN ULONG Flags,
  372. IN OUT PRTL_BUFFER Buffer,
  373. IN SIZE_T Size
  374. )
  375. /*++
  376. Routine Description:
  377. This function ensures Buffer can hold Size bytes, or returns
  378. an error. It either bumps Buffer->Size closer to Buffer->StaticSize,
  379. or heap allocates.
  380. Arguments:
  381. Buffer - a Buffer object, see also RtlInitBuffer.
  382. Size - the number of bytes the caller wishes to store in Buffer->Buffer.
  383. Return Value:
  384. STATUS_SUCCESS
  385. STATUS_NO_MEMORY
  386. --*/
  387. {
  388. NTSTATUS Status = STATUS_SUCCESS;
  389. PUCHAR Temp = NULL;
  390. if ((Flags & ~(RTL_ENSURE_BUFFER_SIZE_NO_COPY)) != 0) {
  391. Status = STATUS_INVALID_PARAMETER;
  392. goto Exit;
  393. }
  394. if (Buffer == NULL) {
  395. Status = STATUS_INVALID_PARAMETER;
  396. goto Exit;
  397. }
  398. if (Size <= Buffer->Size) {
  399. Status = STATUS_SUCCESS;
  400. goto Exit;
  401. }
  402. // Size <= Buffer->StaticSize does not imply static allocation, it
  403. // could be heap allocation that the client poked smaller.
  404. if (Buffer->Buffer == Buffer->StaticBuffer && Size <= Buffer->StaticSize) {
  405. Buffer->Size = Size;
  406. Status = STATUS_SUCCESS;
  407. goto Exit;
  408. }
  409. //
  410. // The realloc case was messed up in Whistler, and got removed.
  411. // Put it back in Blackcomb.
  412. //
  413. Temp = (PUCHAR)RtlAllocateStringRoutine(Size);
  414. if (Temp == NULL) {
  415. Status = STATUS_NO_MEMORY;
  416. goto Exit;
  417. }
  418. if ((Flags & RTL_ENSURE_BUFFER_SIZE_NO_COPY) == 0) {
  419. RtlCopyMemory(Temp, Buffer->Buffer, Buffer->Size);
  420. }
  421. if (RTLP_BUFFER_IS_HEAP_ALLOCATED(Buffer)) {
  422. RtlFreeStringRoutine(Buffer->Buffer);
  423. Buffer->Buffer = NULL;
  424. }
  425. ASSERT(Temp != NULL);
  426. Buffer->Buffer = Temp;
  427. Buffer->Size = Size;
  428. Status = STATUS_SUCCESS;
  429. Exit:
  430. return Status;
  431. }
  432. } // end of namespace ShimLib