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.

1115 lines
34 KiB

  1. // from base\ntdll\curdir.c
  2. // belongs in base\ntos\rtl\path.c
  3. /*++
  4. Copyright (c) Microsoft Corporation
  5. Module Name:
  6. path.c
  7. Abstract:
  8. Author:
  9. Jay Krell (JayKrell)
  10. Revision History:
  11. Environment:
  12. ntdll.dll and setupdd.sys, not ntoskrnl.exe
  13. --*/
  14. #include "spprecmp.h"
  15. #pragma warning(disable:4201) // nameless struct/union
  16. #define _NTOS_ /* prevent #including ntos.h, only use functions exports from ntdll/ntoskrnl */
  17. #include "nt.h"
  18. #include "ntrtl.h"
  19. #include "nturtl.h"
  20. #include "ntrtlp.h"
  21. #define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
  22. extern const UNICODE_STRING RtlpEmptyString = RTL_CONSTANT_STRING(L"");
  23. extern const UNICODE_STRING RtlpSlashSlashDot = RTL_CONSTANT_STRING( L"\\\\.\\" );
  24. extern const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING( L"\\??\\" );
  25. //
  26. // \\? is referred to as the "Win32Nt" prefix or root.
  27. // Paths that start with \\? are referred to as "Win32Nt" paths.
  28. // Fudging the \\? to \?? converts the path to an Nt path.
  29. //
  30. extern const UNICODE_STRING RtlpWin32NtRoot = RTL_CONSTANT_STRING( L"\\\\?" );
  31. extern const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\" );
  32. extern const UNICODE_STRING RtlpWin32NtUncRoot = RTL_CONSTANT_STRING( L"\\\\?\\UNC" );
  33. extern const UNICODE_STRING RtlpWin32NtUncRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\UNC\\" );
  34. #define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
  35. || (x) == STATUS_OBJECT_NAME_NOT_FOUND \
  36. ) \
  37. ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
  38. RTL_PATH_TYPE
  39. NTAPI
  40. RtlDetermineDosPathNameType_Ustr(
  41. IN PCUNICODE_STRING String
  42. )
  43. /*++
  44. Routine Description:
  45. This function examines the Dos format file name and determines the
  46. type of file name (i.e. UNC, DriveAbsolute, Current Directory
  47. rooted, or Relative.)
  48. Arguments:
  49. DosFileName - Supplies the Dos format file name whose type is to be
  50. determined.
  51. Return Value:
  52. RtlPathTypeUnknown - The path type can not be determined
  53. RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
  54. in the format \\server-name\sharename\rest-of-path
  55. RtlPathTypeLocalDevice - The path specifies a local device in the format
  56. \\.\rest-of-path or \\?\rest-of-path. This can be used for any device
  57. where the nt and Win32 names are the same. For example mailslots.
  58. RtlPathTypeRootLocalDevice - The path specifies the root of the local
  59. devices in the format \\. or \\?
  60. RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
  61. path in the form drive:\rest-of-path
  62. RtlPathTypeDriveRelative - The path specifies a drive letter relative
  63. path in the form drive:rest-of-path
  64. RtlPathTypeRooted - The path is rooted relative to the current disk
  65. designator (either Unc disk, or drive). The form is \rest-of-path.
  66. RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
  67. --*/
  68. {
  69. RTL_PATH_TYPE ReturnValue;
  70. const PCWSTR DosFileName = String->Buffer;
  71. #define ENOUGH_CHARS(_cch) (String->Length >= ((_cch) * sizeof(WCHAR)))
  72. if ( ENOUGH_CHARS(1) && IS_PATH_SEPARATOR_U(*DosFileName) ) {
  73. if ( ENOUGH_CHARS(2) && IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
  74. if ( ENOUGH_CHARS(3) && (DosFileName[2] == '.' ||
  75. DosFileName[2] == '?') ) {
  76. if ( ENOUGH_CHARS(4) && IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
  77. // "\\.\" or "\\?\"
  78. ReturnValue = RtlPathTypeLocalDevice;
  79. }
  80. else if ( String->Length == (3 * sizeof(WCHAR)) ){
  81. // "\\." or \\?"
  82. ReturnValue = RtlPathTypeRootLocalDevice;
  83. }
  84. else {
  85. // "\\.x" or "\\?x"
  86. ReturnValue = RtlPathTypeUncAbsolute;
  87. }
  88. }
  89. else {
  90. // "\\x"
  91. ReturnValue = RtlPathTypeUncAbsolute;
  92. }
  93. }
  94. else {
  95. // "\x"
  96. ReturnValue = RtlPathTypeRooted;
  97. }
  98. }
  99. //
  100. // the "*DosFileName" is left over from the PCWSTR version
  101. // Win32 and DOS don't allow embedded nuls and much code limits
  102. // drive letters to strictly 7bit a-zA-Z so it's ok.
  103. //
  104. else if (ENOUGH_CHARS(2) && *DosFileName && *(DosFileName+1)==L':') {
  105. if (ENOUGH_CHARS(3) && IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
  106. // "x:\"
  107. ReturnValue = RtlPathTypeDriveAbsolute;
  108. }
  109. else {
  110. // "c:x"
  111. ReturnValue = RtlPathTypeDriveRelative;
  112. }
  113. }
  114. else {
  115. // "x", first char is not a slash / second char is not colon
  116. ReturnValue = RtlPathTypeRelative;
  117. }
  118. return ReturnValue;
  119. #undef ENOUGH_CHARS
  120. }
  121. NTSTATUS
  122. NTAPI
  123. RtlpDetermineDosPathNameTypeEx(
  124. IN ULONG InFlags,
  125. IN PCUNICODE_STRING DosPath,
  126. OUT RTL_PATH_TYPE* OutType,
  127. OUT ULONG* OutFlags
  128. )
  129. {
  130. NTSTATUS Status = STATUS_SUCCESS;
  131. RTL_PATH_TYPE PathType = 0;
  132. BOOLEAN Win32Nt = FALSE;
  133. BOOLEAN Win32NtUncAbsolute = FALSE;
  134. BOOLEAN Win32NtDriveAbsolute = FALSE;
  135. BOOLEAN IncompleteRoot = FALSE;
  136. RTL_PATH_TYPE PathTypeAfterWin32Nt = 0;
  137. if (OutType != NULL
  138. ) {
  139. *OutType = RtlPathTypeUnknown;
  140. }
  141. if (OutFlags != NULL
  142. ) {
  143. *OutFlags = 0;
  144. }
  145. if (
  146. !RTL_SOFT_VERIFY(DosPath != NULL)
  147. || !RTL_SOFT_VERIFY(OutType != NULL)
  148. || !RTL_SOFT_VERIFY(OutFlags != NULL)
  149. || !RTL_SOFT_VERIFY(
  150. (InFlags & ~(RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_OLD | RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT))
  151. == 0)
  152. ) {
  153. Status = STATUS_INVALID_PARAMETER;
  154. goto Exit;
  155. }
  156. PathType = RtlDetermineDosPathNameType_Ustr(DosPath);
  157. *OutType = PathType;
  158. if (InFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_OLD)
  159. goto Exit;
  160. if (DosPath->Length == sizeof(L"\\\\") - sizeof(DosPath->Buffer[0])
  161. ) {
  162. IncompleteRoot = TRUE;
  163. }
  164. else if (RtlEqualUnicodeString(&RtlpWin32NtRoot, DosPath, TRUE)
  165. ) {
  166. IncompleteRoot = TRUE;
  167. Win32Nt = TRUE;
  168. }
  169. else if (RtlEqualUnicodeString(&RtlpWin32NtRootSlash, DosPath, TRUE)
  170. ) {
  171. IncompleteRoot = TRUE;
  172. Win32Nt = TRUE;
  173. }
  174. else if (RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, DosPath, TRUE)
  175. ) {
  176. Win32Nt = TRUE;
  177. }
  178. if (Win32Nt) {
  179. if (RtlEqualUnicodeString(&RtlpWin32NtUncRoot, DosPath, TRUE)
  180. ) {
  181. IncompleteRoot = TRUE;
  182. Win32NtUncAbsolute = TRUE;
  183. }
  184. else if (RtlEqualUnicodeString(&RtlpWin32NtUncRootSlash, DosPath, TRUE)
  185. ) {
  186. IncompleteRoot = TRUE;
  187. Win32NtUncAbsolute = TRUE;
  188. }
  189. else if (RtlPrefixUnicodeString(&RtlpWin32NtUncRootSlash, DosPath, TRUE)
  190. ) {
  191. Win32NtUncAbsolute = TRUE;
  192. }
  193. if (Win32NtUncAbsolute
  194. ) {
  195. Win32NtDriveAbsolute = FALSE;
  196. } else if (!IncompleteRoot) {
  197. const RTL_STRING_LENGTH_TYPE i = RtlpWin32NtRootSlash.Length;
  198. UNICODE_STRING PathAfterWin32Nt = *DosPath;
  199. PathAfterWin32Nt.Buffer += i / sizeof(PathAfterWin32Nt.Buffer[0]);
  200. PathAfterWin32Nt.Length = PathAfterWin32Nt.Length - i;
  201. PathAfterWin32Nt.MaximumLength = PathAfterWin32Nt.MaximumLength - i;
  202. PathTypeAfterWin32Nt = RtlDetermineDosPathNameType_Ustr(&PathAfterWin32Nt);
  203. if (PathTypeAfterWin32Nt == RtlPathTypeDriveAbsolute) {
  204. Win32NtDriveAbsolute = TRUE;
  205. }
  206. else {
  207. Win32NtDriveAbsolute = FALSE;
  208. }
  209. if (InFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT
  210. ) {
  211. if (!RTL_SOFT_VERIFY(Win32NtDriveAbsolute
  212. )) {
  213. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INVALID;
  214. // we still succeed the function call
  215. }
  216. }
  217. }
  218. }
  219. ASSERT(RTLP_IMPLIES(Win32NtDriveAbsolute, Win32Nt));
  220. ASSERT(RTLP_IMPLIES(Win32NtUncAbsolute, Win32Nt));
  221. ASSERT(!(Win32NtUncAbsolute && Win32NtDriveAbsolute));
  222. if (IncompleteRoot)
  223. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INCOMPLETE_ROOT;
  224. if (Win32Nt)
  225. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT;
  226. if (Win32NtUncAbsolute)
  227. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT_UNC_ABSOLUTE;
  228. if (Win32NtDriveAbsolute)
  229. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_WIN32NT_DRIVE_ABSOLUTE;
  230. Status = STATUS_SUCCESS;
  231. Exit:
  232. return Status;
  233. }
  234. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT (0x00000001)
  235. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS (0x00000002)
  236. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT (0x00000003)
  237. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS (0x00000004)
  238. NTSTATUS
  239. NTAPI
  240. RtlpGetLengthWithoutLastPathElement(
  241. IN ULONG Flags,
  242. IN ULONG PathType,
  243. IN PCUNICODE_STRING Path,
  244. OUT ULONG* LengthOut
  245. )
  246. /*++
  247. Routine Description:
  248. Report how long Path would be if you remove its last element.
  249. This is much simpler than RtlRemoveLastDosPathElement.
  250. It is used to implement the other RtlRemoveLast*PathElement.
  251. Arguments:
  252. Flags - room for future expansion
  253. Path - the path is is an NT path or a full DOS path; the various relative DOS
  254. path types do not work, see RtlRemoveLastDosPathElement for them.
  255. Return Value:
  256. STATUS_SUCCESS - the usual hunky-dory
  257. STATUS_NO_MEMORY - the usual stress
  258. STATUS_INVALID_PARAMETER - the usual bug
  259. --*/
  260. {
  261. ULONG Length = 0;
  262. NTSTATUS Status = STATUS_SUCCESS;
  263. RTL_PATH_TYPE DosPathType = RtlPathTypeUnknown;
  264. ULONG DosPathFlags = 0;
  265. ULONG AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted)
  266. | (1UL << RtlPathTypeUncAbsolute)
  267. | (1UL << RtlPathTypeDriveAbsolute)
  268. | (1UL << RtlPathTypeLocalDevice) // "\\?\"
  269. | (1UL << RtlPathTypeRootLocalDevice) // "\\?"
  270. ;
  271. WCHAR PathSeperators[2] = { '/', '\\' };
  272. #define LOCAL_IS_PATH_SEPARATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
  273. if (LengthOut != NULL) {
  274. *LengthOut = 0;
  275. }
  276. if ( !RTL_SOFT_VERIFY(Path != NULL)
  277. || !RTL_SOFT_VERIFY(Flags == 0)
  278. || !RTL_SOFT_VERIFY(LengthOut != NULL)
  279. ) {
  280. Status = STATUS_INVALID_PARAMETER;
  281. goto Exit;
  282. }
  283. Length = RTL_STRING_GET_LENGTH_CHARS(Path);
  284. switch (PathType)
  285. {
  286. default:
  287. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS:
  288. Status = STATUS_INVALID_PARAMETER;
  289. goto Exit;
  290. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT:
  291. //
  292. // RtlpDetermineDosPathNameTypeEx calls it "rooted"
  293. // only backslashes are seperators
  294. // path must start with backslash
  295. // second char must not be backslash
  296. //
  297. AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted);
  298. PathSeperators[0] = '\\';
  299. if (Length > 0 && Path->Buffer[0] != '\\'
  300. ) {
  301. Status = STATUS_INVALID_PARAMETER;
  302. goto Exit;
  303. }
  304. if (Length > 1 && Path->Buffer[1] == '\\'
  305. ) {
  306. Status = STATUS_INVALID_PARAMETER;
  307. goto Exit;
  308. }
  309. break;
  310. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS:
  311. AllowedDosPathTypeBits &= ~(1UL << RtlPathTypeRooted);
  312. break;
  313. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT:
  314. break;
  315. }
  316. if (Length == 0) {
  317. goto Exit;
  318. }
  319. Status = RtlpDetermineDosPathNameTypeEx(
  320. RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_IN_FLAG_STRICT_WIN32NT,
  321. Path,
  322. &DosPathType,
  323. &DosPathFlags
  324. );
  325. if (!RTL_SOFT_VERIFY(NT_SUCCESS(Status))) {
  326. goto Exit;
  327. }
  328. if (!RTL_SOFT_VERIFY((1UL << DosPathType) & AllowedDosPathTypeBits)
  329. ) {
  330. //KdPrintEx();
  331. Status = STATUS_INVALID_PARAMETER;
  332. goto Exit;
  333. }
  334. if (!RTL_SOFT_VERIFY(
  335. (DosPathFlags & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_EX_OUT_FLAG_INVALID) == 0
  336. )) {
  337. Status = STATUS_INVALID_PARAMETER;
  338. goto Exit;
  339. }
  340. // skip one or more trailing path seperators
  341. for ( ; Length != 0 && LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
  342. // nothing
  343. }
  344. // skip trailing path element
  345. for ( ; Length != 0 && !LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
  346. // nothing
  347. }
  348. // skip one or more in between path seperators
  349. for ( ; Length != 0 && LOCAL_IS_PATH_SEPARATOR(Path->Buffer[Length - 1]) ; --Length) {
  350. // nothing
  351. }
  352. // put back a trailing path seperator, for the sake of c:\ vs. c:
  353. if (Length != 0) {
  354. ++Length;
  355. }
  356. //
  357. // Should optionally check for "bad dos roots" here.
  358. //
  359. *LengthOut = Length;
  360. Status = STATUS_SUCCESS;
  361. Exit:
  362. return Status;
  363. #undef LOCAL_IS_PATH_SEPARATOR
  364. }
  365. NTSTATUS
  366. NTAPI
  367. RtlGetLengthWithoutLastNtPathElement(
  368. IN ULONG Flags,
  369. IN PCUNICODE_STRING Path,
  370. OUT ULONG* LengthOut
  371. )
  372. /*++
  373. Routine Description:
  374. Report how long Path would be if you remove its last element.
  375. Arguments:
  376. Flags - room for future expansion
  377. Path - the path is is an NT path; the various DOS path types
  378. do not work, see RtlRemoveLastDosPathElement for them.
  379. Return Value:
  380. STATUS_SUCCESS - the usual hunky-dory
  381. STATUS_NO_MEMORY - the usual stress
  382. STATUS_INVALID_PARAMETER - the usual bug
  383. --*/
  384. {
  385. NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT, Path, LengthOut);
  386. return Status;
  387. }
  388. NTSTATUS
  389. NTAPI
  390. RtlGetLengthWithoutLastFullDosOrNtPathElement(
  391. IN ULONG Flags,
  392. IN PCUNICODE_STRING Path,
  393. OUT ULONG* LengthOut
  394. )
  395. /*++
  396. Routine Description:
  397. Report how long Path would be if you remove its last element.
  398. Arguments:
  399. Flags - room for future expansion
  400. Path - the path is is an NT path; the various DOS path types
  401. do not work, see RtlRemoveLastDosPathElement for them.
  402. Return Value:
  403. STATUS_SUCCESS - the usual hunky-dory
  404. STATUS_NO_MEMORY - the usual stress
  405. STATUS_INVALID_PARAMETER - the usual bug
  406. --*/
  407. {
  408. NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT, Path, LengthOut);
  409. return Status;
  410. }
  411. NTSTATUS
  412. RtlpCheckForBadDosRootPath(
  413. IN ULONG Flags,
  414. IN PCUNICODE_STRING Path,
  415. OUT ULONG* RootType
  416. )
  417. /*++
  418. Routine Description:
  419. Arguments:
  420. Flags - room for future binary compatible expansion
  421. Path - the path to be checked
  422. RootType - fairly specifically what the string is
  423. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX - \\? or \\?\
  424. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX - \\?\unc or \\?\unc\
  425. RTLP_BAD_DOS_ROOT_PATH_NT_PATH - \??\ but this i only a rough check
  426. RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE - \\machine or \\?\unc\machine
  427. RTLP_GOOD_DOS_ROOT_PATH - none of the above, seems ok
  428. Return Value:
  429. STATUS_SUCCESS -
  430. STATUS_INVALID_PARAMETER -
  431. Path is NULL
  432. or Flags uses undefined values
  433. --*/
  434. {
  435. ULONG Length = 0;
  436. ULONG Index = 0;
  437. BOOLEAN Unc = FALSE;
  438. BOOLEAN Unc1 = FALSE;
  439. BOOLEAN Unc2 = FALSE;
  440. ULONG PiecesSeen = 0;
  441. if (RootType != NULL) {
  442. *RootType = 0;
  443. }
  444. if (!RTL_SOFT_VERIFY(Path != NULL) ||
  445. !RTL_SOFT_VERIFY(RootType != NULL) ||
  446. !RTL_SOFT_VERIFY(Flags == 0)) {
  447. return STATUS_INVALID_PARAMETER;
  448. }
  449. Length = Path->Length / sizeof(Path->Buffer[0]);
  450. if (Length < 3 || !RTL_IS_PATH_SEPARATOR(Path->Buffer[0])) {
  451. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  452. return STATUS_SUCCESS;
  453. }
  454. // prefix \??\ (heuristic, doesn't catch many NT paths)
  455. if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), RTL_CONST_CAST(PUNICODE_STRING)(Path), TRUE)) {
  456. *RootType = RTLP_BAD_DOS_ROOT_PATH_NT_PATH;
  457. return STATUS_SUCCESS;
  458. }
  459. if (!RTL_IS_PATH_SEPARATOR(Path->Buffer[1])) {
  460. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  461. return STATUS_SUCCESS;
  462. }
  463. // == \\?
  464. if (RtlEqualUnicodeString(Path, &RtlpWin32NtRoot, TRUE)) {
  465. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
  466. return STATUS_SUCCESS;
  467. }
  468. if (RtlEqualUnicodeString(Path, &RtlpWin32NtRootSlash, TRUE)) {
  469. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
  470. return STATUS_SUCCESS;
  471. }
  472. // == \\?\unc
  473. if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRoot, TRUE)) {
  474. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
  475. return STATUS_SUCCESS;
  476. }
  477. if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRootSlash, TRUE)) {
  478. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
  479. return STATUS_SUCCESS;
  480. }
  481. // prefix \\ or \\?\unc
  482. // must check the longer string first, or avoid the short circuit (| instead of ||)
  483. Unc1 = RtlPrefixUnicodeString(&RtlpWin32NtUncRootSlash, Path, TRUE);
  484. if (RTL_IS_PATH_SEPARATOR(Path->Buffer[1])) {
  485. Unc2 = TRUE;
  486. }
  487. else {
  488. Unc2 = FALSE;
  489. }
  490. Unc = Unc1 || Unc2;
  491. if (!Unc) {
  492. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  493. return STATUS_SUCCESS;
  494. }
  495. //
  496. // it's unc, see if it is only a machine (note that it'd be really nice if FindFirstFile(\\machine\*)
  497. // just worked and we didn't have to care..)
  498. //
  499. // point index at a slash that precedes the machine, anywhere in the run of slashes,
  500. // but after the \\? stuff
  501. if (Unc1) {
  502. Index = (RtlpWin32NtUncRootSlash.Length / sizeof(RtlpWin32NtUncRootSlash.Buffer[0])) - 1;
  503. } else {
  504. ASSERT(Unc2);
  505. Index = 1;
  506. }
  507. ASSERT(RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]));
  508. Length = Path->Length/ sizeof(Path->Buffer[0]);
  509. //
  510. // skip leading slashes
  511. //
  512. for ( ; Index < Length && RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
  513. PiecesSeen |= 1;
  514. }
  515. // skip the machine name
  516. for ( ; Index < Length && !RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
  517. PiecesSeen |= 2;
  518. }
  519. // skip the slashes between machine and share
  520. for ( ; Index < Length && RTL_IS_PATH_SEPARATOR(Path->Buffer[Index]) ; ++Index) {
  521. PiecesSeen |= 4;
  522. }
  523. //
  524. // Skip the share (make sure it's at least one char).
  525. //
  526. if (Index < Length && !RTL_IS_PATH_SEPARATOR(Path->Buffer[Index])) {
  527. PiecesSeen |= 8;
  528. }
  529. if (PiecesSeen != 0xF) {
  530. *RootType = RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE;
  531. }
  532. return STATUS_SUCCESS;
  533. }
  534. NTSTATUS
  535. NTAPI
  536. RtlpBadDosRootPathToEmptyString(
  537. IN ULONG Flags,
  538. IN OUT PUNICODE_STRING Path
  539. )
  540. /*++
  541. Routine Description:
  542. Arguments:
  543. Flags - room for future binary compatible expansion
  544. Path - the path to be checked and possibly emptied
  545. Return Value:
  546. STATUS_SUCCESS -
  547. STATUS_INVALID_PARAMETER -
  548. Path is NULL
  549. or Flags uses undefined values
  550. --*/
  551. {
  552. NTSTATUS Status;
  553. ULONG RootType = 0;
  554. UNREFERENCED_PARAMETER (Flags);
  555. Status = RtlpCheckForBadDosRootPath(0, Path, &RootType);
  556. if (!NT_SUCCESS(Status)) {
  557. return Status;
  558. }
  559. //
  560. // this is not invalid parameter, our contract is we
  561. // go \\machine\share to empty \\?\c: to empty, etc.
  562. //
  563. if (RootType != RTLP_GOOD_DOS_ROOT_PATH) {
  564. if (RootType == RTLP_BAD_DOS_ROOT_PATH_NT_PATH) {
  565. return STATUS_INVALID_PARAMETER;
  566. }
  567. Path->Length = 0;
  568. }
  569. return STATUS_SUCCESS;
  570. }
  571. NTSTATUS
  572. NTAPI
  573. RtlGetLengthWithoutLastFullDosPathElement(
  574. IN ULONG Flags,
  575. IN PCUNICODE_STRING Path,
  576. OUT ULONG* LengthOut
  577. )
  578. /*++
  579. Routine Description:
  580. Given a fulldospath, like c:\, \\machine\share, \\?\unc\machine\share, \\?\c:,
  581. return (in an out parameter) the length if the last element was cut off.
  582. Arguments:
  583. Flags - room for future binary compatible expansion
  584. Path - the path to be truncating
  585. LengthOut - the length if the last path element is removed
  586. Return Value:
  587. STATUS_SUCCESS -
  588. STATUS_INVALID_PARAMETER -
  589. Path is NULL
  590. or LengthOut is NULL
  591. or Flags uses undefined values
  592. --*/
  593. {
  594. NTSTATUS Status = STATUS_SUCCESS;
  595. UNICODE_STRING CheckRootString = { 0 };
  596. //
  597. // parameter validation is done in RtlpGetLengthWithoutLastPathElement
  598. //
  599. Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS, Path, LengthOut);
  600. if (!(NT_SUCCESS(Status))) {
  601. goto Exit;
  602. }
  603. CheckRootString.Buffer = Path->Buffer;
  604. CheckRootString.Length = (USHORT)(*LengthOut * sizeof(*Path->Buffer));
  605. CheckRootString.MaximumLength = CheckRootString.Length;
  606. if (!NT_SUCCESS(Status = RtlpBadDosRootPathToEmptyString(0, &CheckRootString))) {
  607. goto Exit;
  608. }
  609. *LengthOut = RTL_STRING_GET_LENGTH_CHARS(&CheckRootString);
  610. Status = STATUS_SUCCESS;
  611. Exit:
  612. #if DBG
  613. DbgPrintEx(
  614. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  615. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status);
  616. #endif
  617. return Status;
  618. }
  619. NTSTATUS
  620. NTAPI
  621. RtlAppendPathElement(
  622. IN ULONG Flags,
  623. IN OUT PRTL_UNICODE_STRING_BUFFER Path,
  624. PCUNICODE_STRING ConstElement
  625. )
  626. /*++
  627. Routine Description:
  628. This function appends a path element to a path.
  629. For now, like:
  630. typedef PRTL_UNICODE_STRING_BUFFER PRTL_MUTABLE_PATH;
  631. typedef PCUNICODE_STRING PCRTL_CONSTANT_PATH_ELEMENT;
  632. Maybe something higher level in the future.
  633. The result with regard to trailing slashes aims to be similar to the inputs.
  634. If either Path or ConstElement contains a trailing slash, the result has a trailing slash.
  635. The character used for the in between and trailing slash is picked among the existing
  636. slashes in the strings.
  637. Arguments:
  638. Flags - the ever popular "room for future binary compatible expansion"
  639. Path -
  640. a string representing a path using \\ or / as seperators
  641. ConstElement -
  642. a string representing a path element
  643. this can actually contain multiple \\ or / delimited path elements
  644. only the start and end of the string are examined for slashes
  645. Return Value:
  646. STATUS_SUCCESS -
  647. STATUS_INVALID_PARAMETER -
  648. Path is NULL
  649. or LengthOut is NULL
  650. STATUS_NO_MEMORY - RtlHeapAllocate failed
  651. STATUS_NAME_TOO_LONG - the resulting string does not fit in a UNICODE_STRING, due to its
  652. use of USHORT instead of ULONG or SIZE_T
  653. --*/
  654. {
  655. NTSTATUS Status = STATUS_SUCCESS;
  656. UNICODE_STRING InBetweenSlashString = RtlpEmptyString;
  657. UNICODE_STRING TrailingSlashString = RtlpEmptyString;
  658. WCHAR Slashes[] = {0,0,0,0};
  659. ULONG i;
  660. UNICODE_STRING PathsToAppend[3]; // possible slash, element, possible slash
  661. WCHAR PathSeperators[2] = { '/', '\\' };
  662. const ULONG ValidFlags =
  663. RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR
  664. | RTL_APPEND_PATH_ELEMENT_BUGFIX_CHECK_FIRST_THREE_CHARS_FOR_SLASH_TAKE_FOUND_SLASH_INSTEAD_OF_FIRST_CHAR
  665. ;
  666. const ULONG InvalidFlags = ~ValidFlags;
  667. #define LOCAL_IS_PATH_SEPARATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
  668. if ( !RTL_SOFT_VERIFY((Flags & InvalidFlags) == 0)
  669. || !RTL_SOFT_VERIFY(Path != NULL)
  670. || !RTL_SOFT_VERIFY(ConstElement != NULL)
  671. ) {
  672. Status = STATUS_INVALID_PARAMETER;
  673. goto Exit;
  674. }
  675. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) != 0) {
  676. PathSeperators[0] = '\\';
  677. }
  678. if (ConstElement->Length != 0) {
  679. UNICODE_STRING Element = *ConstElement;
  680. //
  681. // Note leading and trailing slashes on the inputs.
  682. // So that we know if an in-between slash is needed, and if a trailing slash is needed,
  683. // and to guide what sort of slash to place.
  684. //
  685. i = 0;
  686. if (Path->String.Length != 0) {
  687. ULONG j;
  688. ULONG Length = Path->String.Length / sizeof(WCHAR);
  689. //
  690. // for the sake for dos drive paths, check the first three chars for a slash
  691. //
  692. for (j = 0 ; j < 3 && j < Length ; ++j) {
  693. if (LOCAL_IS_PATH_SEPARATOR(Path->String.Buffer[j])) {
  694. if (Flags & RTL_APPEND_PATH_ELEMENT_BUGFIX_CHECK_FIRST_THREE_CHARS_FOR_SLASH_TAKE_FOUND_SLASH_INSTEAD_OF_FIRST_CHAR) {
  695. Slashes[i] = Path->String.Buffer[j];
  696. break;
  697. }
  698. Slashes[i] = Path->String.Buffer[0];
  699. break;
  700. }
  701. }
  702. i += 1;
  703. if (LOCAL_IS_PATH_SEPARATOR(Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1])) {
  704. Slashes[i] = Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1];
  705. }
  706. }
  707. i = 2;
  708. if (LOCAL_IS_PATH_SEPARATOR(Element.Buffer[0])) {
  709. Slashes[i] = Element.Buffer[0];
  710. }
  711. i += 1;
  712. if (LOCAL_IS_PATH_SEPARATOR(Element.Buffer[Element.Length/sizeof(WCHAR) - 1])) {
  713. Slashes[i] = Element.Buffer[Element.Length/sizeof(WCHAR) - 1];
  714. }
  715. if (!Slashes[1] && !Slashes[2]) {
  716. //
  717. // first string lacks trailing slash and second string lacks leading slash,
  718. // must insert one; we favor the types we have, otherwise use a default
  719. //
  720. InBetweenSlashString.Length = sizeof(WCHAR);
  721. InBetweenSlashString.Buffer = RtlPathSeperatorString.Buffer;
  722. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
  723. if (Slashes[3]) {
  724. InBetweenSlashString.Buffer = &Slashes[3];
  725. } else if (Slashes[0]) {
  726. InBetweenSlashString.Buffer = &Slashes[0];
  727. }
  728. }
  729. }
  730. if (Slashes[1] && !Slashes[3]) {
  731. //
  732. // first string has a trailing slash and second string does not,
  733. // must add one, the same type
  734. //
  735. TrailingSlashString.Length = sizeof(WCHAR);
  736. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
  737. TrailingSlashString.Buffer = &Slashes[1];
  738. } else {
  739. TrailingSlashString.Buffer = RtlPathSeperatorString.Buffer;
  740. }
  741. }
  742. if (Slashes[1] && Slashes[2]) {
  743. //
  744. // have both trailing and leading slash, remove leading
  745. //
  746. Element.Buffer += 1;
  747. Element.Length -= sizeof(WCHAR);
  748. Element.MaximumLength -= sizeof(WCHAR);
  749. }
  750. i = 0;
  751. PathsToAppend[i++] = InBetweenSlashString;
  752. PathsToAppend[i++] = Element;
  753. PathsToAppend[i++] = TrailingSlashString;
  754. Status = RtlMultiAppendUnicodeStringBuffer(Path, RTL_NUMBER_OF(PathsToAppend), PathsToAppend);
  755. if (!NT_SUCCESS(Status))
  756. goto Exit;
  757. }
  758. Status = STATUS_SUCCESS;
  759. Exit:
  760. #if DBG
  761. DbgPrintEx(
  762. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  763. "%s(%d):%s(%wZ, %wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path ? &Path->String : NULL, ConstElement, Status);
  764. #endif
  765. return Status;
  766. #undef LOCAL_IS_PATH_SEPARATOR
  767. }
  768. //
  769. // FUTURE-2002/02/20-ELi
  770. // Spelling mistake (Separators)
  771. // This function does not appear to be used and is exported
  772. // Figure out if it can be removed
  773. //
  774. NTSTATUS
  775. NTAPI
  776. RtlGetLengthWithoutTrailingPathSeperators(
  777. IN ULONG Flags,
  778. IN PCUNICODE_STRING Path,
  779. OUT ULONG* LengthOut
  780. )
  781. /*++
  782. Routine Description:
  783. This function computes the length of the string (in characters) if
  784. trailing path seperators (\\ and /) are removed.
  785. Arguments:
  786. Path -
  787. a string representing a path using \\ or / as seperators
  788. LengthOut -
  789. the length of String (in characters) having removed trailing characters
  790. Return Value:
  791. STATUS_SUCCESS -
  792. STATUS_INVALID_PARAMETER -
  793. Path is NULL
  794. or LengthOut is NULL
  795. --*/
  796. {
  797. NTSTATUS Status = STATUS_SUCCESS;
  798. ULONG Index = 0;
  799. ULONG Length = 0;
  800. if (LengthOut != NULL) {
  801. //
  802. // Arguably this should be Path->Length / sizeof(*Path->Buffer), but as long
  803. // as the callstack is all high quality code, it doesn't matter.
  804. //
  805. *LengthOut = 0;
  806. }
  807. if ( !RTL_SOFT_VERIFY(Path != NULL)
  808. || !RTL_SOFT_VERIFY(LengthOut != NULL)
  809. || !RTL_SOFT_VERIFY(Flags == 0)
  810. ) {
  811. Status = STATUS_INVALID_PARAMETER;
  812. goto Exit;
  813. }
  814. Length = Path->Length / sizeof(*Path->Buffer);
  815. for (Index = Length ; Index != 0 ; --Index) {
  816. if (!RTL_IS_PATH_SEPARATOR(Path->Buffer[Index - 1])) {
  817. break;
  818. }
  819. }
  820. //*LengthOut = (Length - Index);
  821. *LengthOut = Index;
  822. Status = STATUS_SUCCESS;
  823. Exit:
  824. #if DBG
  825. DbgPrintEx(
  826. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  827. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status);
  828. #endif
  829. return Status;
  830. }
  831. NTSTATUS
  832. NTAPI
  833. RtlpApplyLengthFunction(
  834. IN ULONG Flags,
  835. IN SIZE_T SizeOfStruct,
  836. IN OUT PVOID UnicodeStringOrUnicodeStringBuffer,
  837. NTSTATUS (NTAPI* LengthFunction)(ULONG, PCUNICODE_STRING, ULONG*)
  838. )
  839. /*++
  840. Routine Description:
  841. This function is common code for patterns like
  842. #define RtlRemoveTrailingPathSeperators(Path_) \
  843. (RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutTrailingPathSeperators))
  844. #define RtlRemoveLastPathElement(Path_) \
  845. (RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutLastPathElement))
  846. Note that shortening a UNICODE_STRING only changes the length, whereas
  847. shortening a RTL_UNICODE_STRING_BUFFER writes a terminal nul.
  848. I expect this pattern will be less error prone than having clients pass the UNICODE_STRING
  849. contained in the RTL_UNICODE_STRING_BUFFER followed by calling RTL_NUL_TERMINATE_STRING.
  850. And, that pattern cannot be inlined with a macro while also preserving that we
  851. return an NTSTATUS.
  852. Arguments:
  853. Flags - the ever popular "room for future binary compatible expansion"
  854. UnicodeStringOrUnicodeStringBuffer -
  855. a PUNICODE_STRING or PRTL_UNICODE_STRING_BUFFER, as indicated by
  856. SizeOfStruct
  857. SizeOfStruct -
  858. a rough type indicator of UnicodeStringOrUnicodeStringBuffer, to allow for overloading in C
  859. LengthFunction -
  860. computes a length for UnicodeStringOrUnicodeStringBuffer to be shortened too
  861. Return Value:
  862. STATUS_SUCCESS -
  863. STATUS_INVALID_PARAMETER -
  864. SizeOfStruct not one of the expected sizes
  865. or LengthFunction is NULL
  866. or UnicodeStringOrUnicodeStringBuffer is NULL
  867. --*/
  868. {
  869. PUNICODE_STRING UnicodeString = NULL;
  870. PRTL_UNICODE_STRING_BUFFER UnicodeStringBuffer = NULL;
  871. NTSTATUS Status = STATUS_SUCCESS;
  872. ULONG Length = 0;
  873. if (!RTL_SOFT_VERIFY(UnicodeStringOrUnicodeStringBuffer != NULL)) {
  874. Status = STATUS_INVALID_PARAMETER;
  875. goto Exit;
  876. }
  877. if (!RTL_SOFT_VERIFY(LengthFunction != NULL)) {
  878. Status = STATUS_INVALID_PARAMETER;
  879. goto Exit;
  880. }
  881. if (!RTL_SOFT_VERIFY(Flags == 0)) {
  882. Status = STATUS_INVALID_PARAMETER;
  883. goto Exit;
  884. }
  885. switch (SizeOfStruct)
  886. {
  887. default:
  888. Status = STATUS_INVALID_PARAMETER;
  889. goto Exit;
  890. case sizeof(*UnicodeString):
  891. UnicodeString = UnicodeStringOrUnicodeStringBuffer;
  892. break;
  893. case sizeof(*UnicodeStringBuffer):
  894. UnicodeStringBuffer = UnicodeStringOrUnicodeStringBuffer;
  895. UnicodeString = &UnicodeStringBuffer->String;
  896. break;
  897. }
  898. Status = (*LengthFunction)(Flags, UnicodeString, &Length);
  899. if (!NT_SUCCESS(Status)) {
  900. goto Exit;
  901. }
  902. if (Length > (UNICODE_STRING_MAX_BYTES / sizeof(UnicodeString->Buffer[0])) ) {
  903. Status = STATUS_NAME_TOO_LONG;
  904. goto Exit;
  905. }
  906. UnicodeString->Length = (USHORT)(Length * sizeof(UnicodeString->Buffer[0]));
  907. if (UnicodeStringBuffer != NULL) {
  908. RTL_NUL_TERMINATE_STRING(UnicodeString);
  909. }
  910. Status = STATUS_SUCCESS;
  911. Exit:
  912. #if DBG
  913. DbgPrintEx(
  914. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  915. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, UnicodeString, Status);
  916. #endif
  917. return Status;
  918. }