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.

5317 lines
169 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. curdir.c
  5. Abstract:
  6. Current directory support
  7. Author:
  8. Mark Lucovsky (markl) 10-Oct-1990
  9. Revision History:
  10. --*/
  11. #include "nt.h"
  12. #include "ntrtl.h"
  13. #include "nturtl.h"
  14. #include "string.h"
  15. #include "ctype.h"
  16. #include "sxstypes.h"
  17. #include "ntdllp.h"
  18. #define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
  19. #define IS_END_OF_COMPONENT_U(ch) (IS_PATH_SEPARATOR_U(ch) || (ch) == UNICODE_NULL)
  20. #define IS_DOT_U(s) ( (s)[0] == L'.' && IS_END_OF_COMPONENT_U( (s)[1] ))
  21. #define IS_DOT_DOT_U(s) ( (s)[0] == L'.' && IS_DOT_U( (s) + 1))
  22. #define IS_DRIVE_LETTER(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
  23. extern const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING( L"LPT" );
  24. extern const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING( L"COM" );
  25. extern const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING( L"PRN" );
  26. extern const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING( L"AUX" );
  27. extern const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING( L"NUL" );
  28. extern const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING( L"CON" );
  29. extern const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING( L"\\\\.\\CON" );
  30. extern const UNICODE_STRING RtlpSlashSlashDot = RTL_CONSTANT_STRING( L"\\\\.\\" );
  31. extern const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING( L"\\??\\" );
  32. extern const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING( L"\\??\\UNC\\" );
  33. #define RtlpLongestPrefix RtlpDosDevicesUncPrefix.Length
  34. const UNICODE_STRING RtlpEmptyString = RTL_CONSTANT_STRING(L"");
  35. //
  36. // \\? is referred to as the "Win32Nt" prefix or root.
  37. // Paths that start with \\? are referred to as "Win32Nt" paths.
  38. // Fudging the \\? to \?? converts the path to an Nt path.
  39. //
  40. extern const UNICODE_STRING RtlpWin32NtRoot = RTL_CONSTANT_STRING( L"\\\\?" );
  41. extern const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\" );
  42. extern const UNICODE_STRING RtlpWin32NtUncRoot = RTL_CONSTANT_STRING( L"\\\\?\\UNC" );
  43. extern const UNICODE_STRING RtlpWin32NtUncRootSlash = RTL_CONSTANT_STRING( L"\\\\?\\UNC\\" );
  44. #define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
  45. || (x) == STATUS_OBJECT_NAME_NOT_FOUND \
  46. ) \
  47. ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
  48. ULONG
  49. RtlpComputeBackupIndex(
  50. IN PCURDIR CurDir
  51. )
  52. {
  53. ULONG BackupIndex;
  54. PWSTR UncPathPointer;
  55. ULONG NumberOfPathSeparators;
  56. RTL_PATH_TYPE CurDirPathType;
  57. //
  58. // Get pathType of curdir
  59. //
  60. CurDirPathType = RtlDetermineDosPathNameType_U(CurDir->DosPath.Buffer);
  61. BackupIndex = 3;
  62. if ( CurDirPathType == RtlPathTypeUncAbsolute ) {
  63. //
  64. // We want to scan the supplied path to determine where
  65. // the "share" ends, and set BackupIndex to that point.
  66. //
  67. UncPathPointer = CurDir->DosPath.Buffer + 2;
  68. NumberOfPathSeparators = 0;
  69. while (*UncPathPointer) {
  70. if (IS_PATH_SEPARATOR_U(*UncPathPointer)) {
  71. NumberOfPathSeparators++;
  72. if (NumberOfPathSeparators == 2) {
  73. break;
  74. }
  75. }
  76. UncPathPointer++;
  77. }
  78. BackupIndex = (ULONG)(UncPathPointer - CurDir->DosPath.Buffer);
  79. }
  80. return BackupIndex;
  81. }
  82. ULONG
  83. RtlGetLongestNtPathLength( VOID )
  84. {
  85. return RtlpLongestPrefix + DOS_MAX_PATH_LENGTH + 1;
  86. }
  87. VOID
  88. RtlpResetDriveEnvironment(
  89. IN WCHAR DriveLetter
  90. )
  91. {
  92. WCHAR EnvVarNameBuffer[4];
  93. WCHAR EnvVarNameValue[4];
  94. UNICODE_STRING s1,s2;
  95. EnvVarNameBuffer[0] = L'=';
  96. EnvVarNameBuffer[1] = DriveLetter;
  97. EnvVarNameBuffer[2] = L':';
  98. EnvVarNameBuffer[3] = L'\0';
  99. RtlInitUnicodeString(&s1,EnvVarNameBuffer);
  100. EnvVarNameValue[0] = DriveLetter;
  101. EnvVarNameValue[1] = L':';
  102. EnvVarNameValue[2] = L'\\';
  103. EnvVarNameValue[3] = L'\0';
  104. RtlInitUnicodeString(&s2,EnvVarNameValue);
  105. RtlSetEnvironmentVariable(NULL,&s1,&s2);
  106. }
  107. ULONG
  108. RtlGetCurrentDirectory_U(
  109. ULONG nBufferLength,
  110. PWSTR lpBuffer
  111. )
  112. /*++
  113. Routine Description:
  114. The current directory for a process can be retreived using
  115. GetCurrentDirectory.
  116. Arguments:
  117. nBufferLength - Supplies the length in bytes of the buffer that is to
  118. receive the current directory string.
  119. lpBuffer - Returns the current directory string for the current
  120. process. The string is a null terminated string and specifies
  121. the absolute path to the current directory.
  122. Return Value:
  123. The return value is the length of the string copied to lpBuffer, not
  124. including the terminating null character. If the return value is
  125. greater than nBufferLength, the return value is the size of the buffer
  126. required to hold the pathname. The return value is zero if the
  127. function failed.
  128. --*/
  129. {
  130. PCURDIR CurDir;
  131. ULONG Length;
  132. PWSTR CurDirName;
  133. CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
  134. RtlAcquirePebLock();
  135. CurDirName = CurDir->DosPath.Buffer;
  136. //
  137. // Make sure user's buffer is big enough to hold the null
  138. // terminated current directory
  139. //
  140. Length = CurDir->DosPath.Length>>1;
  141. if (CurDirName[Length-2] != L':') {
  142. if ( nBufferLength < (Length)<<1 ) {
  143. RtlReleasePebLock();
  144. return (Length)<<1;
  145. }
  146. }
  147. else {
  148. if ( nBufferLength <= (Length<<1) ) {
  149. RtlReleasePebLock();
  150. return ((Length+1)<<1);
  151. }
  152. }
  153. try {
  154. RtlMoveMemory(lpBuffer,CurDirName,Length<<1);
  155. ASSERT(lpBuffer[Length-1] == L'\\');
  156. if (lpBuffer[Length-2] == L':') {
  157. lpBuffer[Length] = UNICODE_NULL;
  158. }
  159. else {
  160. lpBuffer[Length-1] = UNICODE_NULL;
  161. Length--;
  162. }
  163. }
  164. except (EXCEPTION_EXECUTE_HANDLER) {
  165. RtlReleasePebLock();
  166. return 0L;
  167. }
  168. RtlReleasePebLock();
  169. return Length<<1;
  170. }
  171. NTSTATUS
  172. RtlSetCurrentDirectory_U(
  173. PUNICODE_STRING PathName
  174. )
  175. /*++
  176. Routine Description:
  177. The current directory for a process is changed using
  178. SetCurrentDirectory.
  179. Each process has a single current directory. A current directory is
  180. made up of type parts.
  181. - A disk designator either which is either a drive letter followed
  182. by a colon, or a UNC servername/sharename "\\servername\sharename".
  183. - A directory on the disk designator.
  184. For APIs that manipulate files, the file names may be relative to
  185. the current directory. A filename is relative to the entire current
  186. directory if it does not begin with a disk designator or a path name
  187. SEPARATOR. If the file name begins with a path name SEPARATOR, then
  188. it is relative to the disk designator of the current directory. If
  189. a file name begins with a disk designator, than it is a fully
  190. qualified absolute path name.
  191. The value of lpPathName supplies the current directory. The value
  192. of lpPathName, may be a relative path name as described above, or a
  193. fully qualified absolute path name. In either case, the fully
  194. qualified absolute path name of the specified directory is
  195. calculated and is stored as the current directory.
  196. Arguments:
  197. lpPathName - Supplies the pathname of the directory that is to be
  198. made the current directory.
  199. Return Value:
  200. NT_SUCCESS - The operation was successful
  201. !NT_SUCCESS - The operation failed
  202. --*/
  203. {
  204. PCURDIR CurDir;
  205. NTSTATUS Status;
  206. BOOLEAN TranslationStatus;
  207. PVOID FreeBuffer;
  208. ULONG DosDirLength;
  209. ULONG IsDevice;
  210. ULONG DosDirCharCount;
  211. UNICODE_STRING DosDir;
  212. UNICODE_STRING NtFileName;
  213. HANDLE NewDirectoryHandle;
  214. OBJECT_ATTRIBUTES Obja;
  215. IO_STATUS_BLOCK IoStatusBlock;
  216. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  217. RTL_PATH_TYPE InputPathType;
  218. CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
  219. DosDir.Buffer = NULL;
  220. FreeBuffer = NULL;
  221. NewDirectoryHandle = NULL;
  222. RtlAcquirePebLock();
  223. NtCurrentPeb()->EnvironmentUpdateCount += 1;
  224. //
  225. // Set current directory is called first by the loader.
  226. // If current directory is not being inherited, then close
  227. // it !
  228. //
  229. if ( ((ULONG_PTR)CurDir->Handle & OBJ_HANDLE_TAGBITS) == RTL_USER_PROC_CURDIR_CLOSE ) {
  230. NtClose(CurDir->Handle);
  231. CurDir->Handle = NULL;
  232. }
  233. try {
  234. try {
  235. //
  236. // Compute the length of the Dos style fully qualified current
  237. // directory
  238. //
  239. DosDirLength = CurDir->DosPath.MaximumLength;
  240. DosDir.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0,DosDirLength);
  241. if ( !DosDir.Buffer ) {
  242. return STATUS_NO_MEMORY;
  243. }
  244. DosDir.Length = 0;
  245. DosDir.MaximumLength = (USHORT)DosDirLength;
  246. //
  247. // Now get the full pathname for the Dos style current
  248. // directory
  249. //
  250. IsDevice = RtlIsDosDeviceName_Ustr(PathName);
  251. if ( IsDevice ) {
  252. return STATUS_NOT_A_DIRECTORY;
  253. }
  254. DosDirLength = RtlGetFullPathName_Ustr(
  255. PathName,
  256. DosDirLength,
  257. DosDir.Buffer,
  258. NULL,
  259. NULL,
  260. &InputPathType
  261. );
  262. if ( !DosDirLength ) {
  263. return STATUS_OBJECT_NAME_INVALID;
  264. }
  265. DosDirCharCount = DosDirLength >> 1;
  266. //
  267. // Get the Nt filename of the new current directory
  268. //
  269. TranslationStatus = RtlDosPathNameToNtPathName_U(
  270. DosDir.Buffer,
  271. &NtFileName,
  272. NULL,
  273. NULL
  274. );
  275. if ( !TranslationStatus ) {
  276. return STATUS_OBJECT_NAME_INVALID;
  277. }
  278. FreeBuffer = NtFileName.Buffer;
  279. InitializeObjectAttributes(
  280. &Obja,
  281. &NtFileName,
  282. OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
  283. NULL,
  284. NULL
  285. );
  286. //
  287. // If we are inheriting current directory, then
  288. // avoid the open
  289. //
  290. if ( ((ULONG_PTR)CurDir->Handle & OBJ_HANDLE_TAGBITS) == RTL_USER_PROC_CURDIR_INHERIT ) {
  291. NewDirectoryHandle = (HANDLE)((ULONG_PTR)CurDir->Handle & ~OBJ_HANDLE_TAGBITS);
  292. CurDir->Handle = NULL;
  293. //
  294. // Test to see if this is removable media. If so
  295. // tag the handle this may fail if the process was
  296. // created with inherit handles set to false
  297. //
  298. Status = NtQueryVolumeInformationFile(
  299. NewDirectoryHandle,
  300. &IoStatusBlock,
  301. &DeviceInfo,
  302. sizeof(DeviceInfo),
  303. FileFsDeviceInformation
  304. );
  305. if ( !NT_SUCCESS(Status) ) {
  306. return RtlSetCurrentDirectory_U(PathName);
  307. }
  308. else {
  309. if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
  310. NewDirectoryHandle =(HANDLE)( (ULONG_PTR)NewDirectoryHandle | 1);
  311. }
  312. }
  313. }
  314. else {
  315. //
  316. // Open a handle to the current directory. Don't allow
  317. // deletes of the directory.
  318. //
  319. Status = NtOpenFile(
  320. &NewDirectoryHandle,
  321. FILE_TRAVERSE | SYNCHRONIZE,
  322. &Obja,
  323. &IoStatusBlock,
  324. FILE_SHARE_READ | FILE_SHARE_WRITE,
  325. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
  326. );
  327. if ( !NT_SUCCESS(Status) ) {
  328. return Status;
  329. }
  330. //
  331. // Test to see if this is removable media. If so
  332. // tag the handle
  333. //
  334. Status = NtQueryVolumeInformationFile(
  335. NewDirectoryHandle,
  336. &IoStatusBlock,
  337. &DeviceInfo,
  338. sizeof(DeviceInfo),
  339. FileFsDeviceInformation
  340. );
  341. if ( !NT_SUCCESS(Status) ) {
  342. return Status;
  343. }
  344. else {
  345. if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
  346. NewDirectoryHandle =(HANDLE)( (ULONG_PTR)NewDirectoryHandle | 1);
  347. }
  348. }
  349. }
  350. //
  351. // If there is no trailing '\', than place one
  352. //
  353. DosDir.Length = (USHORT)DosDirLength;
  354. if ( DosDir.Buffer[DosDirCharCount-1] != L'\\') {
  355. DosDir.Buffer[DosDirCharCount] = L'\\';
  356. DosDir.Buffer[DosDirCharCount+1] = UNICODE_NULL;
  357. DosDir.Length += sizeof( UNICODE_NULL );
  358. }
  359. //
  360. // Now we are set to change to the new directory.
  361. //
  362. RtlMoveMemory( CurDir->DosPath.Buffer, DosDir.Buffer, DosDir.Length+sizeof(UNICODE_NULL) );
  363. CurDir->DosPath.Length = DosDir.Length;
  364. if ( CurDir->Handle ) {
  365. NtClose(CurDir->Handle);
  366. }
  367. CurDir->Handle = NewDirectoryHandle;
  368. NewDirectoryHandle = NULL;
  369. }
  370. finally {
  371. if ( DosDir.Buffer ) {
  372. RtlFreeHeap(RtlProcessHeap(), 0, DosDir.Buffer);
  373. }
  374. if ( FreeBuffer ) {
  375. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  376. }
  377. if ( NewDirectoryHandle ) {
  378. NtClose(NewDirectoryHandle);
  379. }
  380. RtlReleasePebLock();
  381. }
  382. }
  383. except (EXCEPTION_EXECUTE_HANDLER) {
  384. return STATUS_ACCESS_VIOLATION;
  385. }
  386. return STATUS_SUCCESS;
  387. }
  388. RTL_PATH_TYPE
  389. RtlDetermineDosPathNameType_U(
  390. IN PCWSTR DosFileName
  391. )
  392. /*++
  393. Routine Description:
  394. This function examines the Dos format file name and determines the
  395. type of file name (i.e. UNC, DriveAbsolute, Current Directory
  396. rooted, or Relative.)
  397. Arguments:
  398. DosFileName - Supplies the Dos format file name whose type is to be
  399. determined.
  400. Return Value:
  401. RtlPathTypeUnknown - The path type can not be determined
  402. RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
  403. in the format \\server-name\sharename\rest-of-path
  404. RtlPathTypeLocalDevice - The path specifies a local device in the format
  405. \\.\rest-of-path or \\?\rest-of-path. This can be used for any device
  406. where the nt and Win32 names are the same. For example mailslots.
  407. RtlPathTypeRootLocalDevice - The path specifies the root of the local
  408. devices in the format \\. or \\?
  409. RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
  410. path in the form drive:\rest-of-path
  411. RtlPathTypeDriveRelative - The path specifies a drive letter relative
  412. path in the form drive:rest-of-path
  413. RtlPathTypeRooted - The path is rooted relative to the current disk
  414. designator (either Unc disk, or drive). The form is \rest-of-path.
  415. RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
  416. --*/
  417. {
  418. RTL_PATH_TYPE ReturnValue;
  419. ASSERT(DosFileName != NULL);
  420. if (IS_PATH_SEPARATOR_U(*DosFileName)) {
  421. if ( IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
  422. if ( DosFileName[2] == '.' || DosFileName[2] == '?') {
  423. if ( IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
  424. ReturnValue = RtlPathTypeLocalDevice;
  425. }
  426. else if ( (*(DosFileName+3)) == UNICODE_NULL ){
  427. ReturnValue = RtlPathTypeRootLocalDevice;
  428. }
  429. else {
  430. ReturnValue = RtlPathTypeUncAbsolute;
  431. }
  432. }
  433. else {
  434. ReturnValue = RtlPathTypeUncAbsolute;
  435. }
  436. }
  437. else {
  438. ReturnValue = RtlPathTypeRooted;
  439. }
  440. }
  441. else if ((*DosFileName) && (*(DosFileName+1)==L':')) {
  442. if (IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
  443. ReturnValue = RtlPathTypeDriveAbsolute;
  444. }
  445. else {
  446. ReturnValue = RtlPathTypeDriveRelative;
  447. }
  448. }
  449. else {
  450. ReturnValue = RtlPathTypeRelative;
  451. }
  452. return ReturnValue;
  453. }
  454. RTL_PATH_TYPE
  455. NTAPI
  456. RtlDetermineDosPathNameType_Ustr(
  457. IN PCUNICODE_STRING String
  458. )
  459. /*++
  460. Routine Description:
  461. This function examines the Dos format file name and determines the
  462. type of file name (i.e. UNC, DriveAbsolute, Current Directory
  463. rooted, or Relative.)
  464. Arguments:
  465. DosFileName - Supplies the Dos format file name whose type is to be
  466. determined.
  467. Return Value:
  468. RtlPathTypeUnknown - The path type can not be determined
  469. RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
  470. in the format \\server-name\sharename\rest-of-path
  471. RtlPathTypeLocalDevice - The path specifies a local device in the format
  472. \\.\rest-of-path or \\?\rest-of-path. This can be used for any device
  473. where the nt and Win32 names are the same. For example mailslots.
  474. RtlPathTypeRootLocalDevice - The path specifies the root of the local
  475. devices in the format \\. or \\?
  476. RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
  477. path in the form drive:\rest-of-path
  478. RtlPathTypeDriveRelative - The path specifies a drive letter relative
  479. path in the form drive:rest-of-path
  480. RtlPathTypeRooted - The path is rooted relative to the current disk
  481. designator (either Unc disk, or drive). The form is \rest-of-path.
  482. RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
  483. --*/
  484. {
  485. RTL_PATH_TYPE ReturnValue;
  486. const PCWSTR DosFileName = String->Buffer;
  487. #define ENOUGH_CHARS(_cch) (String->Length >= ((_cch) * sizeof(WCHAR)))
  488. if ( ENOUGH_CHARS(1) && IS_PATH_SEPARATOR_U(*DosFileName) ) {
  489. if ( ENOUGH_CHARS(2) && IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
  490. if ( ENOUGH_CHARS(3) && (DosFileName[2] == '.' ||
  491. DosFileName[2] == '?') ) {
  492. if ( ENOUGH_CHARS(4) && IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
  493. // "\\.\" or "\\?\"
  494. ReturnValue = RtlPathTypeLocalDevice;
  495. }
  496. //
  497. // Bogosity ahead, the code is confusing length and nuls,
  498. // because it was copy/pasted from the PCWSTR version.
  499. //
  500. else if ( ENOUGH_CHARS(4) && (*(DosFileName+3)) == UNICODE_NULL ){
  501. // "\\.\0" or \\?\0"
  502. ReturnValue = RtlPathTypeRootLocalDevice;
  503. }
  504. else {
  505. // "\\.x" or "\\." or "\\?x" or "\\?"
  506. ReturnValue = RtlPathTypeUncAbsolute;
  507. }
  508. }
  509. else {
  510. // "\\x"
  511. ReturnValue = RtlPathTypeUncAbsolute;
  512. }
  513. }
  514. else {
  515. // "\x"
  516. ReturnValue = RtlPathTypeRooted;
  517. }
  518. }
  519. //
  520. // the "*DosFileName" is left over from the PCWSTR version
  521. // Win32 and DOS don't allow embedded nuls and much code limits
  522. // drive letters to strictly 7bit a-zA-Z so it's ok.
  523. //
  524. else if (ENOUGH_CHARS(2) && *DosFileName && *(DosFileName+1)==L':') {
  525. if (ENOUGH_CHARS(3) && IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
  526. // "x:\"
  527. ReturnValue = RtlPathTypeDriveAbsolute;
  528. }
  529. else {
  530. // "c:x"
  531. ReturnValue = RtlPathTypeDriveRelative;
  532. }
  533. }
  534. else {
  535. // "x", first char is not a slash / second char is not colon
  536. ReturnValue = RtlPathTypeRelative;
  537. }
  538. return ReturnValue;
  539. #undef ENOUGH_CHARS
  540. }
  541. NTSTATUS
  542. NTAPI
  543. RtlpDetermineDosPathNameType4(
  544. IN ULONG InFlags,
  545. IN PCUNICODE_STRING DosPath,
  546. OUT RTL_PATH_TYPE* OutType,
  547. OUT ULONG* OutFlags
  548. )
  549. {
  550. NTSTATUS Status = STATUS_SUCCESS;
  551. RTL_PATH_TYPE PathType = 0;
  552. BOOLEAN Win32Nt = FALSE;
  553. BOOLEAN Win32NtUncAbsolute = FALSE;
  554. BOOLEAN Win32NtDriveAbsolute = FALSE;
  555. BOOLEAN IncompleteRoot = FALSE;
  556. RTL_PATH_TYPE PathTypeAfterWin32Nt = 0;
  557. if (OutType != NULL
  558. ) {
  559. *OutType = RtlPathTypeUnknown;
  560. }
  561. if (OutFlags != NULL
  562. ) {
  563. *OutFlags = 0;
  564. }
  565. if (
  566. !RTL_SOFT_VERIFY(DosPath != NULL)
  567. || !RTL_SOFT_VERIFY(OutType != NULL)
  568. || !RTL_SOFT_VERIFY(OutFlags != NULL)
  569. || !RTL_SOFT_VERIFY(
  570. (InFlags & ~(RTL_DETERMINE_DOS_PATH_NAME_TYPE_IN_FLAG_OLD | RTL_DETERMINE_DOS_PATH_NAME_TYPE_IN_FLAG_STRICT_WIN32NT))
  571. == 0)
  572. ) {
  573. Status = STATUS_INVALID_PARAMETER;
  574. goto Exit;
  575. }
  576. PathType = RtlDetermineDosPathNameType_Ustr(DosPath);
  577. *OutType = PathType;
  578. if (InFlags & RTL_DETERMINE_DOS_PATH_NAME_TYPE_IN_FLAG_OLD)
  579. goto Exit;
  580. if (DosPath->Length == sizeof(L"\\\\") - sizeof(DosPath->Buffer[0])
  581. ) {
  582. IncompleteRoot = TRUE;
  583. }
  584. else if (RtlEqualUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtRoot), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  585. ) {
  586. IncompleteRoot = TRUE;
  587. Win32Nt = TRUE;
  588. }
  589. else if (RtlEqualUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtRootSlash), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  590. ) {
  591. IncompleteRoot = TRUE;
  592. Win32Nt = TRUE;
  593. }
  594. else if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtRootSlash), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  595. ) {
  596. Win32Nt = TRUE;
  597. }
  598. if (Win32Nt) {
  599. if (RtlEqualUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtUncRoot), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  600. ) {
  601. IncompleteRoot = TRUE;
  602. Win32NtUncAbsolute = TRUE;
  603. }
  604. else if (RtlEqualUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtUncRootSlash), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  605. ) {
  606. IncompleteRoot = TRUE;
  607. Win32NtUncAbsolute = TRUE;
  608. }
  609. else if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtUncRootSlash), RTL_CONST_CAST(PUNICODE_STRING)(DosPath), TRUE)
  610. ) {
  611. Win32NtUncAbsolute = TRUE;
  612. }
  613. if (Win32NtUncAbsolute
  614. ) {
  615. Win32NtDriveAbsolute = FALSE;
  616. } else if (!IncompleteRoot) {
  617. const RTL_STRING_LENGTH_TYPE i = RtlpWin32NtRootSlash.Length;
  618. UNICODE_STRING PathAfterWin32Nt = *DosPath;
  619. PathAfterWin32Nt.Buffer += i / sizeof(PathAfterWin32Nt.Buffer[0]);
  620. PathAfterWin32Nt.Length -= i;
  621. PathAfterWin32Nt.MaximumLength -= i;
  622. PathTypeAfterWin32Nt = RtlDetermineDosPathNameType_Ustr(&PathAfterWin32Nt);
  623. Win32NtDriveAbsolute = (PathTypeAfterWin32Nt == RtlPathTypeDriveAbsolute);
  624. if (InFlags & RTL_DETERMINE_DOS_PATH_NAME_TYPE_IN_FLAG_STRICT_WIN32NT
  625. ) {
  626. if (!RTL_SOFT_VERIFY(Win32NtDriveAbsolute
  627. )) {
  628. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_INVALID;
  629. // we still succeed the function call
  630. }
  631. }
  632. }
  633. }
  634. ASSERT(RTLP_IMPLIES(Win32NtDriveAbsolute, Win32Nt));
  635. ASSERT(RTLP_IMPLIES(Win32NtUncAbsolute, Win32Nt));
  636. ASSERT(!(Win32NtUncAbsolute && Win32NtDriveAbsolute));
  637. if (IncompleteRoot)
  638. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_INCOMPLETE_ROOT;
  639. if (Win32Nt)
  640. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_WIN32NT;
  641. if (Win32NtUncAbsolute)
  642. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_WIN32NT_UNC_ABSOLUTE;
  643. if (Win32NtDriveAbsolute)
  644. *OutFlags |= RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_WIN32NT_DRIVE_ABSOLUTE;
  645. Status = STATUS_SUCCESS;
  646. Exit:
  647. return Status;
  648. }
  649. ULONG
  650. RtlIsDosDeviceName_Ustr(
  651. IN PUNICODE_STRING DosFileName
  652. )
  653. /*++
  654. Routine Description:
  655. This function examines the Dos format file name and determines if it
  656. is a Dos device name (e.g. LPT1, etc.). Valid Dos device names are:
  657. LPTn
  658. COMn
  659. PRN
  660. AUX
  661. NUL
  662. CON
  663. when n is a digit. Trailing colon is ignored if present.
  664. Arguments:
  665. DosFileName - Supplies the Dos format file name that is to be examined.
  666. Return Value:
  667. 0 - Specified Dos file name is not the name of a Dos device.
  668. > 0 - Specified Dos file name is the name of a Dos device and the
  669. return value is a ULONG where the high order 16 bits is the
  670. offset in the input buffer where the dos device name beings
  671. and the low order 16 bits is the length of the device name
  672. the length of the name (excluding any optional
  673. trailing colon).
  674. --*/
  675. {
  676. UNICODE_STRING UnicodeString;
  677. USHORT NumberOfCharacters;
  678. ULONG ReturnLength;
  679. ULONG ReturnOffset;
  680. LPWSTR p;
  681. USHORT ColonBias;
  682. RTL_PATH_TYPE PathType;
  683. WCHAR wch;
  684. ColonBias = 0;
  685. PathType = RtlDetermineDosPathNameType_U(DosFileName->Buffer);
  686. switch ( PathType ) {
  687. case RtlPathTypeLocalDevice:
  688. //
  689. // For Unc Absolute, Check for \\.\CON
  690. // since this really is not a device
  691. //
  692. if ( RtlEqualUnicodeString(DosFileName,&RtlpDosSlashCONDevice,TRUE) ) {
  693. return 0x00080006;
  694. }
  695. //
  696. // FALLTHRU
  697. //
  698. case RtlPathTypeUncAbsolute:
  699. case RtlPathTypeUnknown:
  700. return 0;
  701. }
  702. UnicodeString = *DosFileName;
  703. NumberOfCharacters = DosFileName->Length >> 1;
  704. if (NumberOfCharacters && DosFileName->Buffer[NumberOfCharacters-1] == L':') {
  705. UnicodeString.Length -= sizeof(WCHAR);
  706. NumberOfCharacters--;
  707. ColonBias = 1;
  708. }
  709. //
  710. // The above strip the trailing colon logic could have left us with 0
  711. // for NumberOfCharacters, so that needs to be tested
  712. //
  713. if ( NumberOfCharacters == 0 ) {
  714. return 0;
  715. }
  716. wch = UnicodeString.Buffer[NumberOfCharacters-1];
  717. while ( NumberOfCharacters && (wch == L'.' || wch == L' ') ) {
  718. UnicodeString.Length -= sizeof(WCHAR);
  719. NumberOfCharacters--;
  720. ColonBias++;
  721. wch = UnicodeString.Buffer[NumberOfCharacters-1];
  722. }
  723. ReturnLength = NumberOfCharacters << 1;
  724. //
  725. // Walk backwards through the string finding the
  726. // first slash or the beginning of the string. We also stop
  727. // at the drive: if it is present.
  728. //
  729. ReturnOffset = 0;
  730. if ( NumberOfCharacters ) {
  731. p = UnicodeString.Buffer + NumberOfCharacters-1;
  732. while ( p >= UnicodeString.Buffer ) {
  733. if ( *p == L'\\' || *p == L'/'
  734. || (*p == L':' && p == UnicodeString.Buffer + 1)) {
  735. p++;
  736. //
  737. // Get the first char of the file name and convert it to
  738. // lower case. This will be safe since we will be comparing
  739. // it to only lower-case ASCII.
  740. //
  741. wch = (*p) | 0x20;
  742. //
  743. // check to see if we possibly have a hit on
  744. // lpt, prn, con, com, aux, or nul
  745. //
  746. if ( !(wch == L'l' || wch == L'c' || wch == L'p' || wch == L'a'
  747. || wch == L'n')
  748. ) {
  749. return 0;
  750. }
  751. ReturnOffset = (ULONG)((PSZ)p - (PSZ)UnicodeString.Buffer);
  752. RtlInitUnicodeString(&UnicodeString,p);
  753. NumberOfCharacters = UnicodeString.Length >> 1;
  754. NumberOfCharacters -= ColonBias;
  755. ReturnLength = NumberOfCharacters << 1;
  756. UnicodeString.Length -= ColonBias*sizeof(WCHAR);
  757. break;
  758. }
  759. p--;
  760. }
  761. wch = UnicodeString.Buffer[0] | 0x20;
  762. //
  763. // check to see if we possibly have a hit on
  764. // lpt, prn, con, com, aux, or nul
  765. //
  766. if ( !( wch == L'l' || wch == L'c' || wch == L'p' || wch == L'a'
  767. || wch == L'n' ) ) {
  768. return 0;
  769. }
  770. }
  771. //
  772. // Now we need to see if we are dealing with a device name that has
  773. // an extension or a stream name. If so, we need to limit the search to the
  774. // file portion only
  775. //
  776. p = UnicodeString.Buffer;
  777. while (p < UnicodeString.Buffer + NumberOfCharacters && *p != L'.' && *p != L':') {
  778. p++;
  779. }
  780. //
  781. // p either points past end of string or to a dot or :. We back up over
  782. // trailing spaces
  783. //
  784. while (p > UnicodeString.Buffer && p[-1] == L' ') {
  785. p--;
  786. }
  787. //
  788. // p either points to the beginning of the string or p[-1] is
  789. // the first non-space char found above.
  790. //
  791. NumberOfCharacters = (USHORT)(p - UnicodeString.Buffer);
  792. UnicodeString.Length = NumberOfCharacters * sizeof( WCHAR );
  793. if ( NumberOfCharacters == 4 && iswdigit(UnicodeString.Buffer[3] ) ) {
  794. if ( (WCHAR)UnicodeString.Buffer[3] == L'0') {
  795. return 0;
  796. } else {
  797. UnicodeString.Length -= sizeof(WCHAR);
  798. if ( RtlEqualUnicodeString(&UnicodeString,&RtlpDosLPTDevice,TRUE) ||
  799. RtlEqualUnicodeString(&UnicodeString,&RtlpDosCOMDevice,TRUE) ) {
  800. ReturnLength = NumberOfCharacters << 1;
  801. } else {
  802. return 0;
  803. }
  804. }
  805. } else if ( NumberOfCharacters != 3 ) {
  806. return 0;
  807. } else if ( RtlEqualUnicodeString(&UnicodeString,&RtlpDosPRNDevice,TRUE) ) {
  808. ReturnLength = NumberOfCharacters << 1;
  809. } else if ( RtlEqualUnicodeString(&UnicodeString,&RtlpDosAUXDevice,TRUE) ) {
  810. ReturnLength = NumberOfCharacters << 1;
  811. } else if ( RtlEqualUnicodeString(&UnicodeString,&RtlpDosNULDevice,TRUE) ) {
  812. ReturnLength = NumberOfCharacters << 1;
  813. } else if ( RtlEqualUnicodeString(&UnicodeString,&RtlpDosCONDevice,TRUE) ) {
  814. ReturnLength = NumberOfCharacters << 1;
  815. } else {
  816. return 0;
  817. }
  818. return ReturnLength | (ReturnOffset << 16);
  819. }
  820. ULONG
  821. RtlIsDosDeviceName_U(
  822. IN PWSTR DosFileName
  823. )
  824. {
  825. UNICODE_STRING UnicodeString;
  826. RtlInitUnicodeString(&UnicodeString,DosFileName);
  827. return RtlIsDosDeviceName_Ustr(&UnicodeString);
  828. }
  829. NTSTATUS
  830. RtlpCheckDeviceName(
  831. PUNICODE_STRING DevName,
  832. ULONG DeviceNameOffset,
  833. BOOLEAN* NameInvalid
  834. )
  835. {
  836. PWSTR DevPath;
  837. NTSTATUS Status = STATUS_SUCCESS;
  838. DevPath = RtlAllocateHeap(RtlProcessHeap(), 0,DevName->Length);
  839. if (!DevPath) {
  840. *NameInvalid = FALSE;
  841. Status = STATUS_NO_MEMORY;
  842. goto Exit;
  843. }
  844. *NameInvalid = TRUE;
  845. try {
  846. RtlCopyMemory(DevPath,DevName->Buffer,DevName->Length);
  847. DevPath[DeviceNameOffset>>1]=L'.';
  848. DevPath[(DeviceNameOffset>>1)+1]=UNICODE_NULL;
  849. if (RtlDoesFileExists_U(DevPath) ) {
  850. *NameInvalid = FALSE;
  851. }
  852. else {
  853. *NameInvalid = TRUE;
  854. }
  855. }
  856. finally {
  857. RtlFreeHeap(RtlProcessHeap(), 0, DevPath);
  858. }
  859. Status = STATUS_SUCCESS;
  860. Exit:
  861. return Status;
  862. }
  863. //
  864. // We keep an open handle to the current directory on the current drive in order
  865. // to make relative opens faster.
  866. //
  867. // However, this current directory can become invalid under two circumstances:
  868. //
  869. // 1. The current drive is removable media. The user may arbitrarily switch
  870. // media without our knowledge. At this point, whatever information the
  871. // filesystem has about the media is now out of date.
  872. // 2. The volume is dismounted by explicit system/user action.
  873. //
  874. // We can ping (via FSCTL_IS_VOLUME_MOUNTED) the volume each time we want to
  875. // test to see if the current directory is still valid. While a "cheap" call
  876. // we will end up doing it a lot. We can reduce this frequency by only checking
  877. // when the media is known to be removable or when a dismount is known to have
  878. // occurred. The Io system exports in USER_SHARED_DATA a count of dismounts
  879. // since boot. We capture this and use it to decide if a dismount was performed.
  880. //
  881. ULONG RtlpSavedDismountCount = -1;
  882. VOID
  883. RtlpValidateCurrentDirectory(
  884. PCURDIR CurDir
  885. )
  886. /*++
  887. Routine Description:
  888. This function is used to validate the current directory for the process.
  889. The current directory can change in several ways, first, by replacing
  890. the media with one that has a different directory structure. Second
  891. by performing a force-dismount.
  892. Arguments:
  893. CurDir - Current directory structure for process
  894. Return Value:
  895. None.
  896. --*/
  897. {
  898. NTSTATUS FsCtlStatus;
  899. IO_STATUS_BLOCK IoStatusBlock;
  900. WCHAR TrimmedPath[4];
  901. UNICODE_STRING str;
  902. if (((ULONG_PTR)CurDir->Handle & 1) == 0
  903. && USER_SHARED_DATA->DismountCount == RtlpSavedDismountCount) {
  904. return;
  905. }
  906. //
  907. // Never been set yet.
  908. //
  909. if (CurDir->Handle == NULL) {
  910. return;
  911. }
  912. //
  913. // Call Nt to see if the volume that
  914. // contains the directory is still mounted.
  915. // If it is, then continue. Otherwise, trim
  916. // current directory to the root.
  917. //
  918. //
  919. // We're updated as far as possible with the current dismount count
  920. //
  921. RtlpSavedDismountCount = USER_SHARED_DATA->DismountCount;
  922. FsCtlStatus = NtFsControlFile(
  923. CurDir->Handle,
  924. NULL,
  925. NULL,
  926. NULL,
  927. &IoStatusBlock,
  928. FSCTL_IS_VOLUME_MOUNTED,
  929. NULL,
  930. 0,
  931. NULL,
  932. 0
  933. );
  934. if ( FsCtlStatus == STATUS_WRONG_VOLUME || FsCtlStatus == STATUS_VOLUME_DISMOUNTED) {
  935. //
  936. // Try to get back to where we were, failing that reset current directory
  937. // to the root of the current drive
  938. //
  939. NtClose( CurDir->Handle );
  940. CurDir->Handle = NULL;
  941. FsCtlStatus = RtlSetCurrentDirectory_U(&CurDir->DosPath);
  942. if ( !NT_SUCCESS(FsCtlStatus) ) {
  943. TrimmedPath[0] = CurDir->DosPath.Buffer[0];
  944. TrimmedPath[1] = CurDir->DosPath.Buffer[1];
  945. TrimmedPath[2] = CurDir->DosPath.Buffer[2];
  946. TrimmedPath[3] = UNICODE_NULL;
  947. RtlpResetDriveEnvironment( TrimmedPath[0] );
  948. RtlInitUnicodeString( &str, TrimmedPath );
  949. //
  950. // This can still fail if the volume was hard dismounted. We tried.
  951. // Ah well.
  952. //
  953. (VOID) RtlSetCurrentDirectory_U( &str );
  954. }
  955. }
  956. }
  957. ULONG
  958. RtlGetFullPathName_Ustr(
  959. PUNICODE_STRING FileName,
  960. ULONG nBufferLength,
  961. PWSTR lpBuffer,
  962. PWSTR *lpFilePart OPTIONAL,
  963. PBOOLEAN NameInvalid,
  964. RTL_PATH_TYPE *InputPathType
  965. )
  966. /*++
  967. Routine Description:
  968. This function is used to return a fully qualified pathname
  969. corresponding to the specified unicode filename. It does this by
  970. merging the current drive and directory together with the specified
  971. file name. In addition to this, it calculates the address of the
  972. file name portion of the fully qualified pathname.
  973. Arguments:
  974. lpFileName - Supplies the unicode file name of the file whose fully
  975. qualified pathname is to be returned.
  976. nBufferLength - Supplies the length in bytes of the buffer that is
  977. to receive the fully qualified path.
  978. lpBuffer - Returns the fully qualified pathname corresponding to the
  979. specified file.
  980. lpFilePart - Optional parameter that if specified, returns the
  981. address of the last component of the fully qualified pathname.
  982. Return Value:
  983. The return value is the length of the string copied to lpBuffer, not
  984. including the terminating unicode null character. If the return
  985. value is greater than nBufferLength, the return value is the size of
  986. the buffer required to hold the pathname. The return value is zero
  987. if the function failed.
  988. --*/
  989. {
  990. ULONG DeviceNameLength;
  991. ULONG DeviceNameOffset;
  992. ULONG PrefixSourceLength;
  993. LONG PathNameLength;
  994. UCHAR CurDrive, NewDrive;
  995. WCHAR EnvVarNameBuffer[4];
  996. UNICODE_STRING EnvVarName;
  997. PWSTR Source,Dest;
  998. UNICODE_STRING Prefix;
  999. PCURDIR CurDir;
  1000. ULONG MaximumLength;
  1001. UNICODE_STRING FullPath;
  1002. ULONG BackupIndex;
  1003. RTL_PATH_TYPE PathType;
  1004. NTSTATUS Status;
  1005. BOOLEAN StripTrailingSlash;
  1006. UNICODE_STRING UnicodeString;
  1007. ULONG NumberOfCharacters;
  1008. PWSTR lpFileName;
  1009. WCHAR wch;
  1010. PWCHAR pwch;
  1011. ULONG i,j;
  1012. if ( ARGUMENT_PRESENT(NameInvalid) ) {
  1013. *NameInvalid = FALSE;
  1014. }
  1015. if ( nBufferLength > MAXUSHORT ) {
  1016. nBufferLength = MAXUSHORT-2;
  1017. }
  1018. *InputPathType = RtlPathTypeUnknown;
  1019. UnicodeString = *FileName;
  1020. lpFileName = UnicodeString.Buffer;
  1021. NumberOfCharacters = UnicodeString.Length >> 1;
  1022. PathNameLength = UnicodeString.Length;
  1023. if ( PathNameLength == 0 || UnicodeString.Buffer[0] == UNICODE_NULL ) {
  1024. return 0;
  1025. }
  1026. else {
  1027. //
  1028. // trim trailing spaces to check for a null name
  1029. //
  1030. DeviceNameLength = PathNameLength;
  1031. wch = UnicodeString.Buffer[(DeviceNameLength>>1) - 1];
  1032. while ( DeviceNameLength && wch == L' ' ) {
  1033. DeviceNameLength -= sizeof(WCHAR);
  1034. if ( DeviceNameLength ) {
  1035. wch = UnicodeString.Buffer[(DeviceNameLength>>1) - 1];
  1036. }
  1037. }
  1038. if ( !DeviceNameLength ) {
  1039. return 0;
  1040. }
  1041. }
  1042. if ( lpFileName[NumberOfCharacters-1] == L'\\' || lpFileName[NumberOfCharacters-1] == L'/' ) {
  1043. StripTrailingSlash = FALSE;
  1044. }
  1045. else {
  1046. StripTrailingSlash = TRUE;
  1047. }
  1048. //
  1049. // If pass Dos file name is a Dos device name, then turn it into
  1050. // \\.\devicename and return its length.
  1051. //
  1052. DeviceNameLength = RtlIsDosDeviceName_Ustr(&UnicodeString);
  1053. if ( DeviceNameLength ) {
  1054. if ( ARGUMENT_PRESENT( lpFilePart ) ) {
  1055. *lpFilePart = NULL;
  1056. }
  1057. DeviceNameOffset = DeviceNameLength >> 16;
  1058. DeviceNameLength &= 0x0000ffff;
  1059. if ( ARGUMENT_PRESENT(NameInvalid) && DeviceNameOffset ) {
  1060. NTSTATUS Status;
  1061. Status = RtlpCheckDeviceName(&UnicodeString, DeviceNameOffset, NameInvalid);
  1062. if (*NameInvalid) {
  1063. return 0;
  1064. }
  1065. }
  1066. PathNameLength = DeviceNameLength + RtlpSlashSlashDot.Length;
  1067. if ( PathNameLength < (LONG)nBufferLength ) {
  1068. RtlMoveMemory(
  1069. lpBuffer,
  1070. RtlpSlashSlashDot.Buffer,
  1071. RtlpSlashSlashDot.Length
  1072. );
  1073. RtlMoveMemory(
  1074. (PVOID)((PUCHAR)lpBuffer+RtlpSlashSlashDot.Length),
  1075. (PSZ)lpFileName+DeviceNameOffset,
  1076. DeviceNameLength
  1077. );
  1078. RtlZeroMemory(
  1079. (PVOID)((PUCHAR)lpBuffer+RtlpSlashSlashDot.Length+DeviceNameLength),
  1080. sizeof(UNICODE_NULL)
  1081. );
  1082. return PathNameLength;
  1083. }
  1084. else {
  1085. return PathNameLength+sizeof(UNICODE_NULL);
  1086. }
  1087. }
  1088. //
  1089. // Setup output string that points to callers buffer.
  1090. //
  1091. FullPath.MaximumLength = (USHORT)nBufferLength;
  1092. FullPath.Length = 0;
  1093. FullPath.Buffer = lpBuffer;
  1094. RtlZeroMemory(lpBuffer,nBufferLength);
  1095. //
  1096. // Get a pointer to the current directory structure.
  1097. //
  1098. CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
  1099. //
  1100. // Determine the type of Dos Path Name specified.
  1101. //
  1102. *InputPathType = PathType = RtlDetermineDosPathNameType_U(lpFileName);
  1103. //
  1104. // Determine the prefix and backup index.
  1105. //
  1106. // Input Prefix Backup Index
  1107. //
  1108. // \\ -> \\, end of \\server\share
  1109. // \\.\ -> \\.\, 4
  1110. // \\. -> \\. 3 (\\.)
  1111. // \ -> Drive: from CurDir.DosPath 3 (Drive:\)
  1112. // d: -> Drive:\curdir from environment 3 (Drive:\)
  1113. // d:\ -> no prefix 3 (Drive:\)
  1114. // any -> CurDir.DosPath 3 (Drive:\)
  1115. //
  1116. RtlAcquirePebLock();
  1117. try {
  1118. //
  1119. // No prefixes yet.
  1120. //
  1121. Source = lpFileName;
  1122. PrefixSourceLength = 0;
  1123. Prefix.Length = 0;
  1124. Prefix.MaximumLength = 0;
  1125. Prefix.Buffer = NULL;
  1126. switch (PathType) {
  1127. case RtlPathTypeUncAbsolute :
  1128. {
  1129. PWSTR UncPathPointer;
  1130. ULONG NumberOfPathSeparators;
  1131. //
  1132. // We want to scan the supplied path to determine where
  1133. // the "share" ends, and set BackupIndex to that point.
  1134. //
  1135. UncPathPointer = lpFileName + 2;
  1136. NumberOfPathSeparators = 0;
  1137. while (*UncPathPointer) {
  1138. if (IS_PATH_SEPARATOR_U(*UncPathPointer)) {
  1139. NumberOfPathSeparators++;
  1140. if (NumberOfPathSeparators == 2) {
  1141. break;
  1142. }
  1143. }
  1144. UncPathPointer++;
  1145. }
  1146. BackupIndex = (ULONG)(UncPathPointer - lpFileName);
  1147. //
  1148. // Unc name. prefix = \\server\share
  1149. //
  1150. PrefixSourceLength = BackupIndex << 1;
  1151. Source += BackupIndex;
  1152. //
  1153. // There is no prefix to place into the buffer.
  1154. // The entire path is in Source
  1155. //
  1156. }
  1157. break;
  1158. case RtlPathTypeLocalDevice :
  1159. //
  1160. // Local device name. prefix = "\\.\"
  1161. //
  1162. PrefixSourceLength = RtlpSlashSlashDot.Length;
  1163. BackupIndex = 4;
  1164. Source += BackupIndex;
  1165. //
  1166. // There is no prefix to place into the buffer.
  1167. // The entire path is in Source
  1168. //
  1169. break;
  1170. case RtlPathTypeRootLocalDevice :
  1171. //
  1172. // Local Device root. prefix = "\\.\"
  1173. //
  1174. Prefix = RtlpSlashSlashDot;
  1175. Prefix.Length = (USHORT)(Prefix.Length - (USHORT)(2*sizeof(UNICODE_NULL)));
  1176. PrefixSourceLength = Prefix.Length + sizeof(UNICODE_NULL);
  1177. BackupIndex = 3;
  1178. Source += BackupIndex;
  1179. PathNameLength -= BackupIndex * sizeof( WCHAR );
  1180. break;
  1181. case RtlPathTypeDriveAbsolute :
  1182. CurDrive = (UCHAR)RtlUpcaseUnicodeChar( CurDir->DosPath.Buffer[0] );
  1183. NewDrive = (UCHAR)RtlUpcaseUnicodeChar( lpFileName[0] );
  1184. if ( CurDrive == NewDrive ) {
  1185. RtlpValidateCurrentDirectory( CurDir );
  1186. }
  1187. //
  1188. // Dos drive absolute name
  1189. //
  1190. BackupIndex = 3;
  1191. break;
  1192. case RtlPathTypeDriveRelative :
  1193. //
  1194. // Dos drive relative name
  1195. //
  1196. CurDrive = (UCHAR)RtlUpcaseUnicodeChar( CurDir->DosPath.Buffer[0] );
  1197. NewDrive = (UCHAR)RtlUpcaseUnicodeChar( lpFileName[0] );
  1198. if ( CurDrive == NewDrive ) {
  1199. RtlpValidateCurrentDirectory( CurDir );
  1200. Prefix = *(PUNICODE_STRING)&CurDir->DosPath;
  1201. }
  1202. else {
  1203. RtlpCheckRelativeDrive((WCHAR)NewDrive);
  1204. EnvVarNameBuffer[0] = L'=';
  1205. EnvVarNameBuffer[1] = (WCHAR)NewDrive;
  1206. EnvVarNameBuffer[2] = L':';
  1207. EnvVarNameBuffer[3] = UNICODE_NULL;
  1208. RtlInitUnicodeString(&EnvVarName,EnvVarNameBuffer);
  1209. Prefix = FullPath;
  1210. Status = RtlQueryEnvironmentVariable_U( NULL,
  1211. &EnvVarName,
  1212. &Prefix
  1213. );
  1214. if ( !NT_SUCCESS( Status ) ) {
  1215. if (Status == STATUS_BUFFER_TOO_SMALL) {
  1216. return (ULONG)(Prefix.Length) + PathNameLength + 2;
  1217. }
  1218. else {
  1219. //
  1220. // Otherwise default to root directory of drive
  1221. //
  1222. Status = STATUS_SUCCESS;
  1223. EnvVarNameBuffer[0] = (WCHAR)NewDrive;
  1224. EnvVarNameBuffer[1] = L':';
  1225. EnvVarNameBuffer[2] = L'\\';
  1226. EnvVarNameBuffer[3] = UNICODE_NULL;
  1227. RtlInitUnicodeString(&Prefix,EnvVarNameBuffer);
  1228. }
  1229. }
  1230. else {
  1231. {
  1232. ULONG LastChar;
  1233. //
  1234. // Determine
  1235. // if a backslash needs to be added
  1236. //
  1237. LastChar = Prefix.Length >> 1;
  1238. if (LastChar > 3) {
  1239. Prefix.Buffer[ LastChar ] = L'\\';
  1240. Prefix.Length += sizeof(UNICODE_NULL);
  1241. }
  1242. }
  1243. }
  1244. }
  1245. BackupIndex = 3;
  1246. Source += 2;
  1247. PathNameLength -= 2 * sizeof( WCHAR );
  1248. break;
  1249. case RtlPathTypeRooted :
  1250. BackupIndex = RtlpComputeBackupIndex(CurDir);
  1251. if ( BackupIndex != 3 ) {
  1252. Prefix = CurDir->DosPath;
  1253. Prefix.Length = (USHORT)(BackupIndex << 1);
  1254. }
  1255. else {
  1256. //
  1257. // Rooted name. Prefix is drive portion of current directory
  1258. //
  1259. Prefix = CurDir->DosPath;
  1260. Prefix.Length = 2*sizeof(UNICODE_NULL);
  1261. }
  1262. break;
  1263. case RtlPathTypeRelative :
  1264. RtlpValidateCurrentDirectory( CurDir );
  1265. //
  1266. // Current drive:directory relative name
  1267. //
  1268. Prefix = CurDir->DosPath;
  1269. BackupIndex = RtlpComputeBackupIndex(CurDir);
  1270. break;
  1271. default:
  1272. return 0;
  1273. }
  1274. //
  1275. // Maximum length required is the length of the prefix plus
  1276. // the length of the specified pathname. If the callers buffer
  1277. // is not at least this large, then return an error.
  1278. //
  1279. MaximumLength = PathNameLength + Prefix.Length;
  1280. if ( MaximumLength >= nBufferLength ) {
  1281. if ( (NumberOfCharacters > 1) ||
  1282. (*lpFileName != L'.') ) {
  1283. return MaximumLength+sizeof(UNICODE_NULL);
  1284. }
  1285. else {
  1286. //
  1287. // If we are expanding curdir, then remember the trailing '\'
  1288. //
  1289. if ( NumberOfCharacters == 1 && *lpFileName == L'.' ) {
  1290. //
  1291. // We are expanding .
  1292. //
  1293. if ( Prefix.Length == 6 ) {
  1294. if ( nBufferLength <= Prefix.Length ) {
  1295. return (ULONG)(Prefix.Length+(USHORT)sizeof(UNICODE_NULL));
  1296. }
  1297. }
  1298. else {
  1299. if ( nBufferLength < Prefix.Length ) {
  1300. return (ULONG)Prefix.Length;
  1301. }
  1302. else {
  1303. for(i=0,j=0;i<Prefix.Length;i+=sizeof(WCHAR),j++){
  1304. if ( Prefix.Buffer[j] == L'\\' ||
  1305. Prefix.Buffer[j] == L'/' ) {
  1306. FullPath.Buffer[j] = L'\\';
  1307. }
  1308. else {
  1309. FullPath.Buffer[j] = Prefix.Buffer[j];
  1310. }
  1311. }
  1312. FullPath.Length = Prefix.Length-(USHORT)sizeof(L'\\');
  1313. goto skipit;
  1314. }
  1315. }
  1316. }
  1317. else {
  1318. return MaximumLength;
  1319. }
  1320. }
  1321. }
  1322. if (PrefixSourceLength || Prefix.Buffer != FullPath.Buffer) {
  1323. //
  1324. // Copy the prefix from the source string.
  1325. //
  1326. //RtlMoveMemory(FullPath.Buffer,lpFileName,PrefixSourceLength);
  1327. for(i=0,j=0;i<PrefixSourceLength;i+=sizeof(WCHAR),j++){
  1328. if ( lpFileName[j] == L'\\' ||
  1329. lpFileName[j] == L'/' ) {
  1330. FullPath.Buffer[j] = L'\\';
  1331. }
  1332. else {
  1333. FullPath.Buffer[j] = lpFileName[j];
  1334. }
  1335. }
  1336. FullPath.Length = (USHORT)PrefixSourceLength;
  1337. //
  1338. // Append any additional prefix
  1339. //
  1340. for(i=0,j=0;i<Prefix.Length;i+=sizeof(WCHAR),j++){
  1341. if ( Prefix.Buffer[j] == L'\\' ||
  1342. Prefix.Buffer[j] == L'/' ) {
  1343. FullPath.Buffer[j+(FullPath.Length>>1)] = L'\\';
  1344. }
  1345. else {
  1346. FullPath.Buffer[j+(FullPath.Length>>1)] = Prefix.Buffer[j];
  1347. }
  1348. }
  1349. FullPath.Length += Prefix.Length;
  1350. }
  1351. else {
  1352. FullPath.Length = Prefix.Length;
  1353. }
  1354. skipit:
  1355. Dest = (PWSTR)((PUCHAR)FullPath.Buffer + FullPath.Length);
  1356. *Dest = UNICODE_NULL;
  1357. while ( *Source ) {
  1358. switch ( *Source ) {
  1359. case L'\\' :
  1360. case L'/' :
  1361. //
  1362. // collapse multiple "\" characters. If the previous character was
  1363. // a path character, skip it.
  1364. //
  1365. if ( *(Dest-1) != L'\\' ) {
  1366. *Dest++ = L'\\';
  1367. }
  1368. Source++;
  1369. break;
  1370. case '.' :
  1371. //
  1372. // Ignoring dot in a leading //./ has already been taken
  1373. // care of by advancing Source above.
  1374. //
  1375. // Eat single dots as in /./ by simply skipping them
  1376. //
  1377. // Double dots back up one level as in /../
  1378. //
  1379. // Any other . is just a filename character
  1380. //
  1381. if ( IS_DOT_U(Source) ) {
  1382. Source++;
  1383. if (IS_PATH_SEPARATOR_U(*Source)) {
  1384. Source++;
  1385. }
  1386. break;
  1387. }
  1388. else if ( IS_DOT_DOT_U(Source) ) {
  1389. //
  1390. // backup destination string looking for a '\'
  1391. //
  1392. while (*Dest != L'\\') {
  1393. *Dest = UNICODE_NULL;
  1394. Dest--;
  1395. }
  1396. //
  1397. // backup to previous component..
  1398. // \a\b\c\.. to \a\b
  1399. //
  1400. do {
  1401. //
  1402. // If we bump into root prefix, then
  1403. // stay at root
  1404. //
  1405. if ( Dest == FullPath.Buffer + (BackupIndex-1) ) {
  1406. break;
  1407. }
  1408. *Dest = UNICODE_NULL;
  1409. Dest--;
  1410. } while (*Dest != L'\\');
  1411. if ( Dest == FullPath.Buffer + (BackupIndex-1) ) {
  1412. Dest++;
  1413. }
  1414. //
  1415. // Advance source past ..
  1416. //
  1417. Source += 2;
  1418. break;
  1419. }
  1420. //
  1421. // Not a single dot or double-dot. The dot we found begins
  1422. // a file name so we treat this as a normal file name
  1423. //
  1424. // FALLTHRU
  1425. //
  1426. default:
  1427. //
  1428. // Copy the filename. Note that
  1429. // null and /,\ will stop the copy. If any
  1430. // charcter other than null or /,\ is encountered,
  1431. // then the pathname is invalid.
  1432. //
  1433. //
  1434. // Copy up until NULL or path separator
  1435. //
  1436. while ( !IS_END_OF_COMPONENT_U(*Source) ) {
  1437. *Dest++ = *Source++;
  1438. }
  1439. //
  1440. // Once copied, we should do some processing for compatibility with Win9x.
  1441. // Win9x strips all trailing spaces and dots from the name.
  1442. // Nt4/Win2K strips only the last dot if its followed by a path separator.
  1443. //
  1444. // Ideally, we'd do something reasonable like stripping all trailing spaces
  1445. // and dots (like Win9X). However, IIS has a security model that's based
  1446. // on NAMES and not on objects. This means that IIS needs to process names
  1447. // in the same way that we do. They should use GetFullPathName and be done
  1448. // with it. In any event, DON'T CHANGE THE CANONICALIZATION OF THE NAME
  1449. // HERE WITHOUTH FIRST GOING THROUGH IIS.
  1450. //
  1451. // We do EXACTLY what Nt4 did: if there's a trailing dot AND we're at a path
  1452. // seperator, remove the trailing dot.
  1453. //
  1454. if (IS_PATH_SEPARATOR_U( *Source ) && Dest[-1] == L'.') {
  1455. Dest--;
  1456. }
  1457. }
  1458. }
  1459. *Dest = UNICODE_NULL;
  1460. if ( StripTrailingSlash ) {
  1461. if ( Dest > (FullPath.Buffer + BackupIndex ) && *(Dest-1) == L'\\' ) {
  1462. Dest--;
  1463. *Dest = UNICODE_NULL;
  1464. }
  1465. }
  1466. FullPath.Length = (USHORT)(PtrToUlong(Dest) - PtrToUlong(FullPath.Buffer));
  1467. //
  1468. // Strip trailing spaces and dots.
  1469. //
  1470. while (Dest > FullPath.Buffer && (Dest[-1] == L' ' || Dest[-1] == L'.')) {
  1471. *--Dest = UNICODE_NULL;
  1472. FullPath.Length -= sizeof( WCHAR );
  1473. }
  1474. if ( ARGUMENT_PRESENT( lpFilePart ) ) {
  1475. //
  1476. // Locate the file part...
  1477. //
  1478. Source = Dest-1;
  1479. Dest = NULL;
  1480. while(Source > FullPath.Buffer ) {
  1481. if ( *Source == L'\\' ) {
  1482. Dest = Source + 1;
  1483. break;
  1484. }
  1485. Source--;
  1486. }
  1487. if ( Dest && *Dest ) {
  1488. //
  1489. // If this is a UNC name, make sure filepart is past the backup index
  1490. //
  1491. if ( PathType == RtlPathTypeUncAbsolute ) {
  1492. if ( Dest < (FullPath.Buffer + BackupIndex ) ) {
  1493. *lpFilePart = NULL;
  1494. leave;
  1495. }
  1496. }
  1497. *lpFilePart = Dest;
  1498. }
  1499. else {
  1500. *lpFilePart = NULL;
  1501. }
  1502. }
  1503. }
  1504. finally {
  1505. RtlReleasePebLock();
  1506. }
  1507. return (ULONG)FullPath.Length;
  1508. }
  1509. NTSTATUS
  1510. RtlGetFullPathName_UstrEx(
  1511. PUNICODE_STRING FileName,
  1512. PUNICODE_STRING StaticString,
  1513. PUNICODE_STRING DynamicString,
  1514. PUNICODE_STRING *StringUsed,
  1515. SIZE_T *FilePartPrefixCch OPTIONAL,
  1516. PBOOLEAN NameInvalid,
  1517. RTL_PATH_TYPE *InputPathType,
  1518. SIZE_T *BytesRequired OPTIONAL
  1519. )
  1520. /*++
  1521. Routine Description:
  1522. See a description of RtlGetFullPathName_Ustr() for a functional
  1523. description.
  1524. This function provides the same basic behavior as RtlGetFullPathName_Ustr(),
  1525. but with easier support for dynamically allocated arbitrary path name
  1526. buffers.
  1527. One would think that this is the core implementation and the non-Ex()
  1528. version would call the ...Ex() version, but that seems risky, and
  1529. would only really help the performance of the case where a dynamic allocation
  1530. is done.
  1531. --*/
  1532. {
  1533. NTSTATUS Status;
  1534. ULONG Length;
  1535. PWSTR Buffer;
  1536. ULONG BufferLength;
  1537. PWSTR FilePart = NULL;
  1538. UNICODE_STRING TempDynamicString;
  1539. USHORT StaticBufferSize;
  1540. if (StringUsed != NULL)
  1541. *StringUsed = NULL;
  1542. if (BytesRequired != NULL)
  1543. *BytesRequired = 0;
  1544. if (FilePartPrefixCch != NULL)
  1545. *FilePartPrefixCch = 0;
  1546. TempDynamicString.Buffer = NULL;
  1547. if ((StaticString != NULL) && (DynamicString != NULL) && (StringUsed == NULL)) {
  1548. Status = STATUS_INVALID_PARAMETER;
  1549. goto Exit;
  1550. }
  1551. StaticBufferSize = 0;
  1552. if (StaticString != NULL) {
  1553. StaticBufferSize = StaticString->MaximumLength;
  1554. }
  1555. // First try getting into the static string.
  1556. Length = RtlGetFullPathName_Ustr(
  1557. FileName,
  1558. StaticBufferSize,
  1559. StaticString->Buffer,
  1560. &FilePart,
  1561. NameInvalid,
  1562. InputPathType);
  1563. if (Length == 0) {
  1564. #if DBG
  1565. DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__);
  1566. #endif // DBG
  1567. Status = STATUS_OBJECT_NAME_INVALID;
  1568. goto Exit;
  1569. }
  1570. if ((StaticString != NULL) && (Length < StaticBufferSize)) {
  1571. // woohoo it worked.
  1572. StaticString->Length = (USHORT) Length;
  1573. if (FilePartPrefixCch != NULL) {
  1574. *FilePartPrefixCch = (FilePart != NULL) ? (FilePart - StaticString->Buffer) : 0;
  1575. }
  1576. if (StringUsed != NULL)
  1577. *StringUsed = StaticString;
  1578. Status = STATUS_SUCCESS;
  1579. } else if (DynamicString == NULL) {
  1580. // The static buffer wasn't big enough and the caller doesn't want us to do
  1581. // a dynamic allocation; the best we can hope for is to give them back a
  1582. // reasonable size.
  1583. if (BytesRequired != NULL) {
  1584. *BytesRequired = Length;
  1585. }
  1586. Status = STATUS_BUFFER_TOO_SMALL;
  1587. goto Exit;
  1588. } else {
  1589. // Not big enough... allocate some memory into the dynamic buffer.
  1590. // But wait; we need to lock the Peb lock so that someone doesn't
  1591. // change the process's current directory out from under us!
  1592. RtlAcquirePebLock();
  1593. __try {
  1594. // Do it again with the peb lock taken so that we can get an accurate stable
  1595. // length.
  1596. Length = RtlGetFullPathName_Ustr(
  1597. FileName,
  1598. StaticBufferSize,
  1599. StaticString->Buffer,
  1600. &FilePart,
  1601. NameInvalid,
  1602. InputPathType);
  1603. if (Length == 0) {
  1604. #if DBG
  1605. DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__);
  1606. #endif // DBG
  1607. Status = STATUS_OBJECT_NAME_INVALID;
  1608. goto Exit;
  1609. }
  1610. if ((StaticString != NULL) && (Length < StaticString->MaximumLength)) {
  1611. // woohoo it worked; some voodoo is going on where the current directory
  1612. // or something just changed prior to us acquiring the Peb lock.
  1613. StaticString->Length = (USHORT) Length;
  1614. if (FilePartPrefixCch != NULL) {
  1615. *FilePartPrefixCch = (FilePart != NULL) ? (FilePart - StaticString->Buffer) : 0;
  1616. }
  1617. if (StringUsed != NULL)
  1618. *StringUsed = StaticString;
  1619. } else {
  1620. // If it doesn't fit into a UNICODE string, we're in big trouble.
  1621. if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) {
  1622. Status = STATUS_NAME_TOO_LONG;
  1623. goto Exit;
  1624. }
  1625. TempDynamicString.MaximumLength = (USHORT) (Length + sizeof(WCHAR));
  1626. TempDynamicString.Buffer = (RtlAllocateStringRoutine)(TempDynamicString.MaximumLength);
  1627. if (TempDynamicString.Buffer == NULL) {
  1628. Status = STATUS_NO_MEMORY;
  1629. goto Exit;
  1630. }
  1631. Length = RtlGetFullPathName_Ustr(
  1632. FileName,
  1633. TempDynamicString.MaximumLength - sizeof(WCHAR),
  1634. TempDynamicString.Buffer,
  1635. &FilePart,
  1636. NameInvalid,
  1637. InputPathType);
  1638. if (Length == 0) {
  1639. #if DBG
  1640. DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__);
  1641. #endif // DBG
  1642. Status = STATUS_OBJECT_NAME_INVALID;
  1643. goto Exit;
  1644. }
  1645. // If this assert fires, it means that someone changed something that
  1646. // RtlGetFullPathName_Ustr() uses to resolve the filename, even while
  1647. // we're holding the Peb lock. This is really bad and whoever is
  1648. // trashing the PEB is resposible, not this code.
  1649. ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR)));
  1650. if (Length > (TempDynamicString.MaximumLength - sizeof(WCHAR))) {
  1651. Status = STATUS_INTERNAL_ERROR;
  1652. goto Exit;
  1653. }
  1654. if (FilePartPrefixCch != NULL) {
  1655. *FilePartPrefixCch = (FilePart != NULL) ? (FilePart - TempDynamicString.Buffer) : 0;
  1656. }
  1657. TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
  1658. DynamicString->Buffer = TempDynamicString.Buffer;
  1659. DynamicString->Length = (USHORT) Length;
  1660. DynamicString->MaximumLength = TempDynamicString.MaximumLength;
  1661. if (StringUsed != NULL)
  1662. *StringUsed = DynamicString;
  1663. TempDynamicString.Buffer = NULL;
  1664. }
  1665. Status = STATUS_SUCCESS;
  1666. } __finally {
  1667. RtlReleasePebLock();
  1668. }
  1669. }
  1670. Exit:
  1671. if (TempDynamicString.Buffer != NULL) {
  1672. (RtlFreeStringRoutine)(TempDynamicString.Buffer);
  1673. }
  1674. #if DBG
  1675. // This happens a lot for STATUS_NO_SUCH_FILE and STATUS_BUFFER_TOO_SMALL; we'll report any others
  1676. if (NT_ERROR(Status) && (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) {
  1677. DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n", __FUNCTION__, FileName, Status);
  1678. }
  1679. #endif // DBG
  1680. return Status;
  1681. }
  1682. ULONG
  1683. RtlGetFullPathName_U(
  1684. PCWSTR lpFileName,
  1685. ULONG nBufferLength,
  1686. PWSTR lpBuffer,
  1687. PWSTR *lpFilePart OPTIONAL
  1688. )
  1689. {
  1690. UNICODE_STRING UnicodeString;
  1691. RTL_PATH_TYPE PathType;
  1692. RtlInitUnicodeString(&UnicodeString,lpFileName);
  1693. return RtlGetFullPathName_Ustr(&UnicodeString,nBufferLength,lpBuffer,lpFilePart,NULL,&PathType);
  1694. }
  1695. NTSTATUS
  1696. RtlpWin32NTNameToNtPathName_U(
  1697. IN PUNICODE_STRING DosFileName,
  1698. OUT PUNICODE_STRING NtFileName,
  1699. OUT PWSTR *FilePart OPTIONAL,
  1700. OUT PRTL_RELATIVE_NAME RelativeName OPTIONAL
  1701. )
  1702. {
  1703. PWSTR FullNtPathName = NULL;
  1704. PWSTR Source,Dest;
  1705. NTSTATUS Status = STATUS_SUCCESS;
  1706. FullNtPathName = RtlAllocateHeap(
  1707. RtlProcessHeap(),
  1708. 0,
  1709. DosFileName->Length-8+sizeof(UNICODE_NULL)+RtlpDosDevicesPrefix.Length
  1710. );
  1711. if ( !FullNtPathName ) {
  1712. Status = STATUS_NO_MEMORY;
  1713. goto Exit;
  1714. }
  1715. //
  1716. // Copy the full Win32/NT path next to the name prefix, skipping over
  1717. // the \\?\ at the front of the path.
  1718. //
  1719. RtlMoveMemory(FullNtPathName,RtlpDosDevicesPrefix.Buffer,RtlpDosDevicesPrefix.Length);
  1720. RtlMoveMemory((PUCHAR)FullNtPathName+RtlpDosDevicesPrefix.Length,
  1721. DosFileName->Buffer + 4,
  1722. DosFileName->Length - 8);
  1723. //
  1724. // Null terminate the path name to make strlen below happy.
  1725. //
  1726. NtFileName->Buffer = FullNtPathName;
  1727. NtFileName->Length = (USHORT)(DosFileName->Length-8+RtlpDosDevicesPrefix.Length);
  1728. NtFileName->MaximumLength = NtFileName->Length + sizeof(UNICODE_NULL);
  1729. FullNtPathName[ NtFileName->Length >> 1 ] = UNICODE_NULL;
  1730. //
  1731. // Now we have the passed in path with \DosDevices\ prepended. Blow out the
  1732. // relative name structure (if provided), and possibly compute filepart
  1733. //
  1734. if ( ARGUMENT_PRESENT(RelativeName) ) {
  1735. //
  1736. // If the current directory is a sub-string of the
  1737. // Nt file name, and if a handle exists for the current
  1738. // directory, then return the directory handle and name
  1739. // relative to the directory.
  1740. //
  1741. RelativeName->RelativeName.Length = 0;
  1742. RelativeName->RelativeName.MaximumLength = 0;
  1743. RelativeName->RelativeName.Buffer = 0;
  1744. RelativeName->ContainingDirectory = NULL;
  1745. }
  1746. if ( ARGUMENT_PRESENT( FilePart ) ) {
  1747. //
  1748. // Locate the file part...
  1749. //
  1750. Source = &FullNtPathName[ (NtFileName->Length-1) >> 1 ];
  1751. Dest = NULL;
  1752. while(Source > FullNtPathName ) {
  1753. if ( *Source == L'\\' ) {
  1754. Dest = Source + 1;
  1755. break;
  1756. }
  1757. Source--;
  1758. }
  1759. if ( Dest && *Dest ) {
  1760. *FilePart = Dest;
  1761. }
  1762. else {
  1763. *FilePart = NULL;
  1764. }
  1765. }
  1766. Status = STATUS_SUCCESS;
  1767. Exit:
  1768. return Status;
  1769. }
  1770. BOOLEAN
  1771. RtlDosPathNameToNtPathName_Ustr(
  1772. IN PCUNICODE_STRING DosFileNameString,
  1773. OUT PUNICODE_STRING NtFileName,
  1774. OUT PWSTR *FilePart OPTIONAL,
  1775. OUT PRTL_RELATIVE_NAME RelativeName OPTIONAL
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. A Dos pathname can be translated into an Nt style pathname
  1780. using this function.
  1781. This function is used only within the Base dll to translate Dos
  1782. pathnames to Nt pathnames. Upon successful translation, the
  1783. pointer (NtFileName->Buffer) points to memory from RtlProcessHeap()
  1784. that contains the Nt version of the input dos file name.
  1785. Arguments:
  1786. DosFileName - Supplies the unicode Dos style file name that is to be
  1787. translated into an equivalent unicode Nt file name.
  1788. NtFileName - Returns the address of memory in the RtlProcessHeap() that
  1789. contains an NT filename that refers to the specified Dos file
  1790. name.
  1791. FilePart - Optional parameter that if specified, returns the
  1792. trailing file portion of the file name. A path of \foo\bar\x.x
  1793. returns the address of x.x as the file part.
  1794. RelativeName - An optional parameter, that if specified, returns
  1795. a pathname relative to the current directory of the file. The
  1796. length field of RelativeName->RelativeName is 0 if the relative
  1797. name can not be used.
  1798. Return Value:
  1799. TRUE - The path name translation was successful. Once the caller is
  1800. done with the translated name, the memory pointed to by
  1801. NtFileName.Buffer should be returned to the RtlProcessHeap().
  1802. FALSE - The operation failed.
  1803. Note:
  1804. The buffers pointed to by RelativeName, FilePart, and NtFileName must ALL
  1805. point within the same memory address. If they don't, code that calls
  1806. this routine will fail.
  1807. --*/
  1808. {
  1809. ULONG BufferLength;
  1810. ULONG DosPathLength;
  1811. PWSTR FullNtPathName = NULL;
  1812. PWSTR FullDosPathName = NULL;
  1813. UNICODE_STRING Prefix;
  1814. UNICODE_STRING UnicodeFilePart;
  1815. UNICODE_STRING FullDosPathString;
  1816. PCURDIR CurDir;
  1817. RTL_PATH_TYPE DosPathType;
  1818. RTL_PATH_TYPE InputDosPathType;
  1819. ULONG DosPathNameOffset;
  1820. ULONG FullDosPathNameLength;
  1821. ULONG LastCharacter;
  1822. UNICODE_STRING UnicodeString;
  1823. BOOLEAN NameInvalid;
  1824. WCHAR StaticDosBuffer[DOS_MAX_PATH_LENGTH + 1];
  1825. BOOLEAN UseWin32Name, fRC;
  1826. //
  1827. // Calculate the size needed for the full pathname. Add in
  1828. // space for the longest Nt prefix
  1829. //
  1830. BufferLength = sizeof(StaticDosBuffer);
  1831. DosPathLength = (DOS_MAX_PATH_LENGTH << 1 );
  1832. UnicodeString = *DosFileNameString;
  1833. //
  1834. // see if this is \\?\ form of name
  1835. //
  1836. if ( UnicodeString.Length > 8 && UnicodeString.Buffer[0] == '\\' &&
  1837. UnicodeString.Buffer[1] == '\\' && UnicodeString.Buffer[2] == '?' &&
  1838. UnicodeString.Buffer[3] == '\\' ) {
  1839. UseWin32Name = TRUE;
  1840. }
  1841. else {
  1842. UseWin32Name = FALSE;
  1843. //
  1844. // The dos name starts just after the longest Nt prefix
  1845. //
  1846. FullDosPathName = &StaticDosBuffer[0];
  1847. BufferLength += RtlpLongestPrefix;
  1848. //
  1849. // Allocate space for the full Nt Name (including DOS name portion)
  1850. //
  1851. FullNtPathName = RtlAllocateHeap(RtlProcessHeap(), 0, BufferLength);
  1852. if ( !FullNtPathName ) {
  1853. return FALSE;
  1854. }
  1855. }
  1856. RtlAcquirePebLock();
  1857. fRC = TRUE;
  1858. __try {
  1859. __try {
  1860. if ( UseWin32Name ) {
  1861. NTSTATUS Status;
  1862. Status = RtlpWin32NTNameToNtPathName_U(&UnicodeString,NtFileName,FilePart,RelativeName);
  1863. fRC = NT_SUCCESS(Status);
  1864. __leave;
  1865. }
  1866. FullDosPathNameLength = RtlGetFullPathName_Ustr(
  1867. &UnicodeString,
  1868. DosPathLength,
  1869. FullDosPathName,
  1870. FilePart,
  1871. &NameInvalid,
  1872. &InputDosPathType
  1873. );
  1874. if ( NameInvalid || !FullDosPathNameLength ||
  1875. FullDosPathNameLength > DosPathLength ) {
  1876. fRC = FALSE;
  1877. __leave;
  1878. }
  1879. //
  1880. // Determine how to format prefix of FullNtPathName base on the
  1881. // the type of Dos path name. All Nt names begin in the \DosDevices
  1882. // directory.
  1883. //
  1884. Prefix = RtlpDosDevicesPrefix;
  1885. DosPathType = RtlDetermineDosPathNameType_U(FullDosPathName);
  1886. switch (DosPathType) {
  1887. case RtlPathTypeUncAbsolute :
  1888. //
  1889. // Unc name, use \DosDevices\UNC symbolic link to find
  1890. // redirector. Skip of \\ in source Dos path.
  1891. //
  1892. Prefix = RtlpDosDevicesUncPrefix;
  1893. DosPathNameOffset = 2;
  1894. break;
  1895. case RtlPathTypeLocalDevice :
  1896. //
  1897. // Local device name, so just use \DosDevices prefix and
  1898. // skip \\.\ in source Dos path.
  1899. //
  1900. DosPathNameOffset = 4;
  1901. break;
  1902. case RtlPathTypeRootLocalDevice :
  1903. ASSERT( FALSE );
  1904. break;
  1905. case RtlPathTypeDriveAbsolute :
  1906. case RtlPathTypeDriveRelative :
  1907. case RtlPathTypeRooted :
  1908. case RtlPathTypeRelative :
  1909. //
  1910. // All drive references just use \DosDevices prefix and
  1911. // do not skip any of the characters in the source Dos path.
  1912. //
  1913. DosPathNameOffset = 0;
  1914. break;
  1915. default:
  1916. ASSERT( FALSE );
  1917. }
  1918. //
  1919. // Copy the full DOS path next to the name prefix, skipping over
  1920. // the "\\" at the front of the UNC path or the "\\.\" at the front
  1921. // of a device name.
  1922. //
  1923. RtlMoveMemory(FullNtPathName,Prefix.Buffer,Prefix.Length);
  1924. RtlMoveMemory((PUCHAR)FullNtPathName+Prefix.Length,
  1925. FullDosPathName + DosPathNameOffset,
  1926. FullDosPathNameLength - (DosPathNameOffset<<1));
  1927. //
  1928. // Null terminate the path name to make strlen below happy.
  1929. //
  1930. NtFileName->Buffer = FullNtPathName;
  1931. NtFileName->Length = (USHORT)(FullDosPathNameLength-(DosPathNameOffset<<1))+Prefix.Length;
  1932. NtFileName->MaximumLength = (USHORT)BufferLength;
  1933. LastCharacter = NtFileName->Length >> 1;
  1934. FullNtPathName[ LastCharacter ] = UNICODE_NULL;
  1935. //
  1936. // Readjust the file part to point to the appropriate position within
  1937. // the FullNtPathName buffer instead of inside the FullDosPathName
  1938. // buffer
  1939. //
  1940. if ( ARGUMENT_PRESENT(FilePart) ) {
  1941. if (*FilePart) {
  1942. RtlInitUnicodeString(&UnicodeFilePart,*FilePart);
  1943. *FilePart = &FullNtPathName[ LastCharacter ] - (UnicodeFilePart.Length >> 1);
  1944. }
  1945. }
  1946. if ( ARGUMENT_PRESENT(RelativeName) ) {
  1947. //
  1948. // If the current directory is a sub-string of the
  1949. // Nt file name, and if a handle exists for the current
  1950. // directory, then return the directory handle and name
  1951. // relative to the directory.
  1952. //
  1953. RelativeName->RelativeName.Length = 0;
  1954. RelativeName->RelativeName.MaximumLength = 0;
  1955. RelativeName->RelativeName.Buffer = 0;
  1956. RelativeName->ContainingDirectory = NULL;
  1957. if ( InputDosPathType == RtlPathTypeRelative ) {
  1958. CurDir = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
  1959. if ( CurDir->Handle ) {
  1960. //
  1961. // Now compare curdir to full dos path. If curdir length is
  1962. // greater than full path. It is not a match. Otherwise,
  1963. // trim full path length to cur dir length and compare.
  1964. //
  1965. RtlInitUnicodeString(&FullDosPathString,FullDosPathName);
  1966. if ( CurDir->DosPath.Length <= FullDosPathString.Length ) {
  1967. FullDosPathString.Length = CurDir->DosPath.Length;
  1968. if ( RtlEqualUnicodeString(
  1969. (PUNICODE_STRING)&CurDir->DosPath,
  1970. &FullDosPathString,
  1971. TRUE
  1972. ) ) {
  1973. //
  1974. // The full dos pathname is a substring of the
  1975. // current directory. Compute the start of the
  1976. // relativename.
  1977. //
  1978. RelativeName->RelativeName.Buffer = ((PUCHAR)FullNtPathName + Prefix.Length - (DosPathNameOffset<<1) + (CurDir->DosPath.Length));
  1979. RelativeName->RelativeName.Length = (USHORT)FullDosPathNameLength - (CurDir->DosPath.Length);
  1980. if ( *(PWSTR)(RelativeName->RelativeName.Buffer) == L'\\' ) {
  1981. (PWSTR)(RelativeName->RelativeName.Buffer)++;
  1982. RelativeName->RelativeName.Length -= 2;
  1983. }
  1984. RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
  1985. RelativeName->ContainingDirectory = CurDir->Handle;
  1986. }
  1987. }
  1988. }
  1989. }
  1990. }
  1991. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1992. fRC = FALSE;
  1993. }
  1994. }
  1995. finally {
  1996. if (fRC == FALSE && FullNtPathName != NULL) {
  1997. RtlFreeHeap(RtlProcessHeap(), 0, FullNtPathName);
  1998. }
  1999. RtlReleasePebLock();
  2000. }
  2001. return fRC;
  2002. }
  2003. BOOLEAN
  2004. RtlDosPathNameToNtPathName_U(
  2005. IN PCWSTR DosFileName,
  2006. OUT PUNICODE_STRING NtFileName,
  2007. OUT PWSTR *FilePart OPTIONAL,
  2008. OUT PRTL_RELATIVE_NAME RelativeName OPTIONAL
  2009. )
  2010. {
  2011. UNICODE_STRING DosFileNameString;
  2012. ULONG Length = 0;
  2013. if (DosFileName != NULL) {
  2014. Length = wcslen( DosFileName ) * sizeof( WCHAR );
  2015. if (Length + sizeof( UNICODE_NULL ) >= UNICODE_STRING_MAX_BYTES) {
  2016. return FALSE;
  2017. }
  2018. DosFileNameString.MaximumLength = (USHORT)(Length + sizeof( UNICODE_NULL ));
  2019. } else {
  2020. DosFileNameString.MaximumLength = 0;
  2021. }
  2022. DosFileNameString.Buffer = (PWSTR) DosFileName;
  2023. DosFileNameString.Length = (USHORT)Length;
  2024. return RtlDosPathNameToNtPathName_Ustr( &DosFileNameString, NtFileName, FilePart, RelativeName );
  2025. }
  2026. NTSTATUS
  2027. RtlDoesFileExist3(
  2028. OUT PHANDLE FileHandle,
  2029. IN PCUNICODE_STRING FileNameString,
  2030. IN ACCESS_MASK DesiredAccess,
  2031. IN ULONG ShareAccess,
  2032. IN ULONG OpenOptions
  2033. )
  2034. /*++
  2035. RtlDoesFileExist is "RtlDoesFileExist1"
  2036. RtlDoesFileExistEx is "RtlDoesFileExist2"
  2037. Supposedly NtOpenFile on unc paths is not reliable (per comments in RtlDoesFileExists_UstrEx).
  2038. Supposedly NtQueryAttributesFile on unc paths is not reliable (per that RtlDoesFileExists_UstrEx has to
  2039. decide how to treat STATUS_ACCESS_DENIED and STATUS_SHARING_VIOLATION and per comments
  2040. from Mike Grier. STATUS_SHARING_VIOLATION does imply exists, but it's up to the caller
  2041. to decide what they really want to know..)
  2042. NtOpenFile to NT4 unc and random downlevel 3rd party filesystems / redirectors is also not reliable,
  2043. it needs the workarounds that kernel32.dll CreateFile supplies. This is a general
  2044. bug throughout ntdll.dll.
  2045. --*/
  2046. {
  2047. NTSTATUS Status;
  2048. OBJECT_ATTRIBUTES Obja;
  2049. UNICODE_STRING NtFileName;
  2050. PVOID FreeBuffer = NULL;
  2051. FILE_ALL_INFORMATION FileInfo; // it feels safest to get all
  2052. // information, like GetFileInformationByHandle does
  2053. BOOLEAN DosPathToNtPathReturnValue;
  2054. RTL_RELATIVE_NAME RelativeName;
  2055. IO_STATUS_BLOCK IoStatusBlock;
  2056. if (FileHandle != NULL) {
  2057. *FileHandle = NULL;
  2058. }
  2059. if (FileHandle == NULL) {
  2060. Status = STATUS_INVALID_PARAMETER_1;
  2061. goto Exit;
  2062. }
  2063. if (FileNameString == NULL) {
  2064. Status = STATUS_INVALID_PARAMETER_2;
  2065. goto Exit;
  2066. }
  2067. DosPathToNtPathReturnValue = RtlDosPathNameToNtPathName_Ustr(
  2068. FileNameString,
  2069. &NtFileName,
  2070. NULL,
  2071. &RelativeName
  2072. );
  2073. if ( !DosPathToNtPathReturnValue ) {
  2074. Status = STATUS_UNSUCCESSFUL; // Blackcomb..
  2075. goto Exit;
  2076. }
  2077. FreeBuffer = NtFileName.Buffer;
  2078. if ( RelativeName.RelativeName.Length ) {
  2079. NtFileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  2080. }
  2081. else {
  2082. RelativeName.ContainingDirectory = NULL;
  2083. }
  2084. InitializeObjectAttributes(
  2085. &Obja,
  2086. &NtFileName,
  2087. OBJ_CASE_INSENSITIVE,
  2088. RelativeName.ContainingDirectory,
  2089. NULL
  2090. );
  2091. //
  2092. // do we worry about synchronize / pending?
  2093. //
  2094. Status = NtOpenFile(FileHandle, DesiredAccess | FILE_READ_ATTRIBUTES, &Obja, &IoStatusBlock, ShareAccess, OpenOptions);
  2095. if (!NT_SUCCESS(Status))
  2096. goto Exit;
  2097. Status = NtQueryInformationFile(*FileHandle, &IoStatusBlock, &FileInfo, sizeof(FileInfo), FileAllInformation);
  2098. if (!NT_SUCCESS(Status)) {
  2099. NtClose(*FileHandle);
  2100. *FileHandle = NULL;
  2101. goto Exit;
  2102. }
  2103. Status = STATUS_SUCCESS;
  2104. Exit:
  2105. RtlFreeHeap(RtlProcessHeap(),0,FreeBuffer);
  2106. return Status;
  2107. }
  2108. BOOLEAN
  2109. RtlDoesFileExists_UstrEx(
  2110. IN PCUNICODE_STRING FileNameString,
  2111. IN BOOLEAN TreatDeniedOrSharingAsHit
  2112. )
  2113. /*++
  2114. Routine Description:
  2115. This function checks to see if the specified unicode filename exists.
  2116. Arguments:
  2117. FileName - Supplies the file name of the file to find.
  2118. Return Value:
  2119. TRUE - The file was found.
  2120. FALSE - The file was not found.
  2121. --*/
  2122. {
  2123. NTSTATUS Status;
  2124. OBJECT_ATTRIBUTES Obja;
  2125. UNICODE_STRING NtFileName;
  2126. BOOLEAN ReturnValue;
  2127. RTL_RELATIVE_NAME RelativeName;
  2128. PVOID FreeBuffer;
  2129. FILE_BASIC_INFORMATION BasicInfo;
  2130. ReturnValue = RtlDosPathNameToNtPathName_Ustr(
  2131. FileNameString,
  2132. &NtFileName,
  2133. NULL,
  2134. &RelativeName
  2135. );
  2136. if ( !ReturnValue ) {
  2137. return FALSE;
  2138. }
  2139. FreeBuffer = NtFileName.Buffer;
  2140. if ( RelativeName.RelativeName.Length ) {
  2141. NtFileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  2142. }
  2143. else {
  2144. RelativeName.ContainingDirectory = NULL;
  2145. }
  2146. InitializeObjectAttributes(
  2147. &Obja,
  2148. &NtFileName,
  2149. OBJ_CASE_INSENSITIVE,
  2150. RelativeName.ContainingDirectory,
  2151. NULL
  2152. );
  2153. //
  2154. // Query the file's attributes. Note that the file cannot simply be opened
  2155. // to determine whether or not it exists, as the NT LanMan redirector lies
  2156. // on NtOpenFile to a Lan Manager server because it does not actually open
  2157. // the file until an operation is performed on it.
  2158. //
  2159. Status = NtQueryAttributesFile(
  2160. &Obja,
  2161. &BasicInfo
  2162. );
  2163. RtlFreeHeap(RtlProcessHeap(),0,FreeBuffer);
  2164. if ( !NT_SUCCESS(Status) ) {
  2165. if ( Status == STATUS_SHARING_VIOLATION ||
  2166. Status == STATUS_ACCESS_DENIED ) {
  2167. if ( TreatDeniedOrSharingAsHit ) {
  2168. ReturnValue = TRUE;
  2169. }
  2170. else {
  2171. ReturnValue = FALSE;
  2172. }
  2173. }
  2174. else {
  2175. ReturnValue = FALSE;
  2176. }
  2177. }
  2178. else {
  2179. ReturnValue = TRUE;
  2180. }
  2181. return ReturnValue;
  2182. }
  2183. BOOLEAN
  2184. RtlDoesFileExists_UEx(
  2185. IN PCWSTR FileName,
  2186. IN BOOLEAN TreatDeniedOrSharingAsHit
  2187. )
  2188. {
  2189. UNICODE_STRING FileNameString;
  2190. RtlInitUnicodeString(&FileNameString, FileName);
  2191. return RtlDoesFileExists_UstrEx(&FileNameString, TreatDeniedOrSharingAsHit);
  2192. }
  2193. BOOLEAN
  2194. RtlDoesFileExists_U(
  2195. IN PCWSTR FileName
  2196. )
  2197. /*++
  2198. Routine Description:
  2199. This function checks to see if the specified unicode filename exists.
  2200. Arguments:
  2201. FileName - Supplies the file name of the file to find.
  2202. Return Value:
  2203. TRUE - The file was found.
  2204. FALSE - The file was not found.
  2205. --*/
  2206. {
  2207. return RtlDoesFileExists_UEx(FileName,TRUE);
  2208. }
  2209. BOOLEAN
  2210. RtlDoesFileExists_UStr(
  2211. IN PCUNICODE_STRING FileName
  2212. )
  2213. /*
  2214. RtlDoesFileExists_UStr same as RtlDoesFileExists_U but takes a PCUNICODE_STRING instead of a PCWSTR,
  2215. saving a call to wcslen
  2216. */
  2217. {
  2218. return RtlDoesFileExists_UstrEx(FileName,TRUE);
  2219. }
  2220. ULONG
  2221. RtlDosSearchPath_U(
  2222. IN PCWSTR lpPath,
  2223. IN PCWSTR lpFileName,
  2224. IN PCWSTR lpExtension OPTIONAL,
  2225. IN ULONG nBufferLength,
  2226. OUT PWSTR lpBuffer,
  2227. OUT PWSTR *lpFilePart
  2228. )
  2229. /*++
  2230. Routine Description:
  2231. This function is used to search for a file specifying a search path
  2232. and a filename. It returns with a fully qualified pathname of the
  2233. found file.
  2234. This function is used to locate a file using the specified path. If
  2235. the file is found, its fully qualified pathname is returned. In
  2236. addition to this, it calculates the address of the file name portion
  2237. of the fully qualified pathname.
  2238. Arguments:
  2239. lpPath - Supplies the search path to be used when locating the file.
  2240. lpFileName - Supplies the file name of the file to search for.
  2241. lpExtension - An optional parameter, that if specified, supplies an
  2242. extension to be added to the filename when doing the search.
  2243. The extension is only added if the specified filename does not
  2244. end with an extension.
  2245. nBufferLength - Supplies the length in bytes of the buffer that is
  2246. to receive the fully qualified path.
  2247. lpBuffer - Returns the fully qualified pathname corresponding to the
  2248. file that was found.
  2249. lpFilePart - Optional parameter that if specified, returns the
  2250. address of the last component of the fully qualified pathname.
  2251. Return Value:
  2252. The return value is the length of the string copied to lpBuffer, not
  2253. including the terminating null character. If the return value is
  2254. greater than nBufferLength, the return value is the size of the buffer
  2255. required to hold the pathname. The return value is zero if the
  2256. function failed.
  2257. --*/
  2258. {
  2259. PWSTR ComputedFileName;
  2260. ULONG ExtensionLength;
  2261. ULONG PathLength;
  2262. ULONG FileLength;
  2263. UNICODE_STRING Scratch;
  2264. PCWSTR p;
  2265. //
  2266. // if the file name is not a relative name, then
  2267. // return an if the file does not exist.
  2268. //
  2269. // If a fully qualified pathname is used in the search, then
  2270. // allow access_denied or sharing violations to terminate the
  2271. // search. This was the nt 3.1-4.0 behavior, and was changed for the
  2272. // loader to handle cases where when walking through a search, we didn't
  2273. // terminate the search early because of an inaccessible UNC path component
  2274. // be restoring the old behavior in this case, we give the correct (access_denied)
  2275. // error codes on fully qualified module lookups, but keep going when bumping
  2276. // through search path components
  2277. //
  2278. if ( RtlDetermineDosPathNameType_U(lpFileName) != RtlPathTypeRelative ) {
  2279. if (RtlDoesFileExists_UEx(lpFileName,TRUE) ) {
  2280. PathLength = RtlGetFullPathName_U(
  2281. lpFileName,
  2282. nBufferLength,
  2283. lpBuffer,
  2284. lpFilePart
  2285. );
  2286. return PathLength;
  2287. }
  2288. else {
  2289. return 0;
  2290. }
  2291. }
  2292. //
  2293. // Determine if the file name contains an extension
  2294. //
  2295. ExtensionLength = 1;
  2296. p = lpFileName;
  2297. while (*p) {
  2298. if ( *p == L'.' ) {
  2299. ExtensionLength = 0;
  2300. break;
  2301. }
  2302. p++;
  2303. }
  2304. //
  2305. // If no extension was found, then determine the extension length
  2306. // that should be used to search for the file
  2307. //
  2308. if ( ExtensionLength ) {
  2309. if ( ARGUMENT_PRESENT(lpExtension) ) {
  2310. RtlInitUnicodeString(&Scratch,lpExtension);
  2311. ExtensionLength = Scratch.Length;
  2312. }
  2313. else {
  2314. ExtensionLength = 0;
  2315. }
  2316. }
  2317. //
  2318. // Compute the file name length and the path length;
  2319. //
  2320. RtlInitUnicodeString(&Scratch,lpPath);
  2321. PathLength = Scratch.Length;
  2322. RtlInitUnicodeString(&Scratch,lpFileName);
  2323. FileLength = Scratch.Length;
  2324. ComputedFileName = RtlAllocateHeap(
  2325. RtlProcessHeap(), 0,
  2326. PathLength + FileLength + ExtensionLength + 3*sizeof(UNICODE_NULL)
  2327. );
  2328. if ( !ComputedFileName ) {
  2329. KdPrint(("%s: Failing due to out of memory (RtlAllocateHeap failure)\n", __FUNCTION__));
  2330. return 0;
  2331. }
  2332. //
  2333. // find ; 's in path and copy path component to computed file name
  2334. //
  2335. do {
  2336. PWSTR Cursor;
  2337. Cursor = ComputedFileName;
  2338. while (*lpPath) {
  2339. if (*lpPath == L';') {
  2340. lpPath++;
  2341. break;
  2342. }
  2343. *Cursor++ = *lpPath++;
  2344. }
  2345. if (Cursor != ComputedFileName &&
  2346. Cursor [ -1 ] != L'\\' ) {
  2347. *Cursor++ = L'\\';
  2348. }
  2349. if (*lpPath == UNICODE_NULL) {
  2350. lpPath = NULL;
  2351. }
  2352. RtlMoveMemory(Cursor,lpFileName,FileLength);
  2353. if ( ExtensionLength ) {
  2354. RtlMoveMemory((PUCHAR)Cursor+FileLength,lpExtension,ExtensionLength+sizeof(UNICODE_NULL));
  2355. }
  2356. else {
  2357. *(PWSTR)((PUCHAR)Cursor+FileLength) = UNICODE_NULL;
  2358. }
  2359. if (RtlDoesFileExists_UEx(ComputedFileName,FALSE) ) {
  2360. PathLength = RtlGetFullPathName_U(
  2361. ComputedFileName,
  2362. nBufferLength,
  2363. lpBuffer,
  2364. lpFilePart
  2365. );
  2366. RtlFreeHeap(RtlProcessHeap(), 0, ComputedFileName);
  2367. return PathLength;
  2368. }
  2369. }
  2370. while ( lpPath );
  2371. RtlFreeHeap(RtlProcessHeap(), 0, ComputedFileName);
  2372. return 0;
  2373. }
  2374. NTSTATUS
  2375. RtlDosSearchPath_Ustr(
  2376. IN ULONG Flags,
  2377. IN PCUNICODE_STRING Path,
  2378. IN PCUNICODE_STRING FileName,
  2379. IN PCUNICODE_STRING DefaultExtension OPTIONAL,
  2380. OUT PUNICODE_STRING StaticString OPTIONAL,
  2381. OUT PUNICODE_STRING DynamicString OPTIONAL,
  2382. OUT PCUNICODE_STRING *FullFileNameOut OPTIONAL,
  2383. OUT SIZE_T *FilePartPrefixCch OPTIONAL,
  2384. OUT SIZE_T *BytesRequired OPTIONAL // includes space for trailing NULL
  2385. )
  2386. /*++
  2387. Routine Description:
  2388. This function is used to search for a file specifying a search path
  2389. and a filename. It returns with a fully qualified pathname of the
  2390. found file.
  2391. This function is used to locate a file using the specified path. If
  2392. the file is found, its fully qualified pathname is returned. In
  2393. addition to this, it calculates the address of the file name portion
  2394. of the fully qualified pathname.
  2395. Arguments:
  2396. Flags - Optional flags to affect the behavior of the path search.
  2397. Use the logical or operator (|) to combine flags.
  2398. Defined flags include:
  2399. RTL_DOS_SEARCH_PATH_FLAG_APPLY_ISOLATION_REDIRECTION
  2400. If the FileName passed in is a relative path, isolation
  2401. redirection of the file path is applied prior to searching
  2402. for a matching path.
  2403. Path - search path to use when locating the file
  2404. FileName - file name to search for
  2405. DefaultExtension - Optional extension to apply to the file name
  2406. if the file name does not include an extension.
  2407. StaticString - Optional UNICODE_STRING which references an already
  2408. allocated buffer which is used to construct the actual path
  2409. of the file.
  2410. DynamicString - Optional UNICODE_STRING which will be filled in with
  2411. a dynamically allocated UNICODE_STRING if either StaticBuffer is
  2412. not provided, or is not long enough to hold the resolved name.
  2413. The dynamic buffer's size is reflected in the MaximumLength field
  2414. of the UNICODE_STRING. It will always exceed the Length of the
  2415. string by at least two bytes, but may be even larger.
  2416. FullFileNameOut - Optional pointer to UNICODE_STRING which points to
  2417. the complete resolved file name. This UNICODE_STRING is not
  2418. allocated; it is either set equal to FileName, StaticBuffer or
  2419. DynamicBuffer as appropriate.
  2420. Return Value:
  2421. NTSTATUS indicating the disposition of the function. If the file
  2422. is redirected via activation context data, STATUS_SUCCESS is returned
  2423. regardless of whether the file exists or not. If the file does not
  2424. exist in any of the directories referenced by the Path parameter,
  2425. STATUS_NO_SUCH_FILE is returned.
  2426. --*/
  2427. {
  2428. NTSTATUS Status;
  2429. PWSTR Cursor;
  2430. PWSTR EndMarker;
  2431. SIZE_T MaximumPathSegmentLength = 0;
  2432. SIZE_T BiggestPossibleFileName;
  2433. USHORT DefaultExtensionLength = 0;
  2434. RTL_PATH_TYPE PathType; // not used; required argument to RtlGetFullPathName_Ustr().
  2435. UNICODE_STRING CandidateString;
  2436. WCHAR StaticCandidateBuffer[DOS_MAX_PATH_LENGTH];
  2437. CandidateString.Length = 0;
  2438. CandidateString.MaximumLength = sizeof(StaticCandidateBuffer);
  2439. CandidateString.Buffer = StaticCandidateBuffer;
  2440. if (FullFileNameOut != NULL) {
  2441. *FullFileNameOut = NULL;
  2442. }
  2443. if (BytesRequired != NULL) {
  2444. *BytesRequired = 0;
  2445. }
  2446. if (FilePartPrefixCch != NULL) {
  2447. *FilePartPrefixCch = 0;
  2448. }
  2449. if (DynamicString != NULL) {
  2450. DynamicString->Length = 0;
  2451. DynamicString->MaximumLength = 0;
  2452. DynamicString->Buffer = NULL;
  2453. }
  2454. if (((Flags & ~(
  2455. RTL_DOS_SEARCH_PATH_FLAG_APPLY_ISOLATION_REDIRECTION |
  2456. RTL_DOS_SEARCH_PATH_FLAG_DISALLOW_DOT_RELATIVE_PATH_SEARCH |
  2457. RTL_DOS_SEARCH_PATH_FLAG_APPLY_DEFAULT_EXTENSION_WHEN_NOT_RELATIVE_PATH_EVEN_IF_FILE_HAS_EXTENSION)) != 0) ||
  2458. (Path == NULL) ||
  2459. (FileName == NULL) ||
  2460. ((StaticString != NULL) && (DynamicString != NULL) && (FullFileNameOut == NULL))) {
  2461. #if DBG
  2462. DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__);
  2463. #endif // DBG
  2464. Status = STATUS_INVALID_PARAMETER;
  2465. goto Exit;
  2466. }
  2467. PathType = RtlDetermineDosPathNameType_Ustr(FileName);
  2468. // If the caller wants to disallow .\ and ..\ relative path searches, stop them!
  2469. if ((Flags & RTL_DOS_SEARCH_PATH_FLAG_DISALLOW_DOT_RELATIVE_PATH_SEARCH) && (PathType == RtlPathTypeRelative)) {
  2470. if (FileName->Length >= (2 * sizeof(WCHAR))) {
  2471. if (FileName->Buffer[0] == L'.') {
  2472. if (IS_PATH_SEPARATOR_U(FileName->Buffer[1])) {
  2473. PathType = RtlPathTypeUnknown;
  2474. } else if ((FileName->Buffer[1] == L'.') &&
  2475. (FileName->Length >= (3 * sizeof(WCHAR))) &&
  2476. IS_PATH_SEPARATOR_U(FileName->Buffer[2])) {
  2477. PathType = RtlPathTypeUnknown;
  2478. }
  2479. }
  2480. }
  2481. }
  2482. //
  2483. // if the file name is not a relative name, then
  2484. // return an if the file does not exist.
  2485. //
  2486. // If a fully qualified pathname is used in the search, then
  2487. // allow access_denied or sharing violations to terminate the
  2488. // search. This was the nt 3.1-4.0 behavior, and was changed for the
  2489. // loader to handle cases where when walking through a search, we didn't
  2490. // terminate the search early because of an inaccessible UNC path component
  2491. // be restoring the old behavior in this case, we give the correct (access_denied)
  2492. // error codes on fully qualified module lookups, but keep going when bumping
  2493. // through search path components
  2494. //
  2495. if (PathType != RtlPathTypeRelative ) {
  2496. if (RtlDoesFileExists_UstrEx(FileName, TRUE)) {
  2497. Status = RtlGetFullPathName_UstrEx(
  2498. RTL_CONST_CAST(PUNICODE_STRING)(FileName),
  2499. StaticString,
  2500. DynamicString,
  2501. (PUNICODE_STRING *) FullFileNameOut,
  2502. FilePartPrefixCch,
  2503. NULL,
  2504. &PathType,
  2505. BytesRequired);
  2506. if (!NT_SUCCESS(Status)) {
  2507. #if DBG
  2508. if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) {
  2509. DbgPrint("%s: Failing because RtlGetFullPathName_UstrEx() on %wZ failed with %08lx\n", __FUNCTION__, FileName, Status);
  2510. }
  2511. #endif // DBG
  2512. goto Exit;
  2513. }
  2514. } else {
  2515. //
  2516. // The file wasn't there; let's try adding the default extension if we need to.
  2517. //
  2518. if ((DefaultExtension == NULL) || (DefaultExtension->Length == 0)) {
  2519. #if DBG
  2520. // DbgPrint("%s: Failing because RtlDoesFileExists_UstrEx() on %wZ says it does not exist and there is no default extension to apply\n", __FUNCTION__, FileName);
  2521. #endif // DBG
  2522. Status = STATUS_NO_SUCH_FILE;
  2523. goto Exit;
  2524. }
  2525. DefaultExtensionLength = DefaultExtension->Length;
  2526. // If they've asked for SearchPathW() bug compatibility mode, always apply the default
  2527. // extension if the file isn't found, even if the file name has an extension.
  2528. if (!(Flags & RTL_DOS_SEARCH_PATH_FLAG_APPLY_DEFAULT_EXTENSION_WHEN_NOT_RELATIVE_PATH_EVEN_IF_FILE_HAS_EXTENSION)) {
  2529. if (FileName->Length != 0) {
  2530. Cursor = FileName->Buffer + (FileName->Length / sizeof(WCHAR));
  2531. while (Cursor != FileName->Buffer) {
  2532. const WCHAR wch = *--Cursor;
  2533. if (IS_PATH_SEPARATOR_U(wch)) {
  2534. // it's a slash; we have a filename without an extension...
  2535. break;
  2536. }
  2537. if (wch == L'.') {
  2538. // There was an extension. We're just out of luck.
  2539. Status = STATUS_NO_SUCH_FILE;
  2540. goto Exit;
  2541. }
  2542. }
  2543. }
  2544. }
  2545. // We need to move the filename into a different buffer.
  2546. BiggestPossibleFileName = (FileName->Length + DefaultExtensionLength + sizeof(WCHAR));
  2547. if (BiggestPossibleFileName > UNICODE_STRING_MAX_BYTES) {
  2548. #if DBG
  2549. DbgPrint("%s: Failing because the filename plus extension (%Iu bytes) is too big\n", __FUNCTION__, BiggestPossibleFileName);
  2550. #endif // DBG
  2551. Status = STATUS_NAME_TOO_LONG;
  2552. goto Exit;
  2553. }
  2554. // If the buffer on the stack isn't big enough, allocate one from the heap
  2555. if (BiggestPossibleFileName > CandidateString.MaximumLength) {
  2556. CandidateString.MaximumLength = (USHORT) BiggestPossibleFileName;
  2557. CandidateString.Buffer = (RtlAllocateStringRoutine)(CandidateString.MaximumLength);
  2558. if (CandidateString.Buffer == NULL) {
  2559. #if DBG
  2560. DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n", __FUNCTION__);
  2561. #endif // DBG
  2562. Status = STATUS_NO_MEMORY;
  2563. goto Exit;
  2564. }
  2565. }
  2566. RtlCopyMemory(CandidateString.Buffer, FileName->Buffer, FileName->Length);
  2567. RtlCopyMemory(CandidateString.Buffer + (FileName->Length / sizeof(WCHAR)), DefaultExtension->Buffer, DefaultExtension->Length);
  2568. CandidateString.Buffer[(FileName->Length + DefaultExtension->Length) / sizeof(WCHAR)] = UNICODE_NULL;
  2569. CandidateString.Length = FileName->Length + DefaultExtension->Length;
  2570. if (!RtlDoesFileExists_UstrEx(&CandidateString, TRUE)) {
  2571. Status = STATUS_NO_SUCH_FILE;
  2572. goto Exit;
  2573. }
  2574. Status = RtlGetFullPathName_UstrEx(
  2575. &CandidateString,
  2576. StaticString,
  2577. DynamicString,
  2578. (PUNICODE_STRING *) FullFileNameOut,
  2579. FilePartPrefixCch,
  2580. NULL,
  2581. &PathType,
  2582. BytesRequired);
  2583. if (!NT_SUCCESS(Status)) {
  2584. #if DBG
  2585. if (Status != STATUS_NO_SUCH_FILE) {
  2586. DbgPrint("%s: Failing on \"%wZ\" because RtlGetFullPathName_UstrEx() failed with status %08lx\n", __FUNCTION__, &CandidateString, Status);
  2587. }
  2588. #endif // DBG
  2589. goto Exit;
  2590. }
  2591. }
  2592. Status = STATUS_SUCCESS;
  2593. goto Exit;
  2594. }
  2595. // We know it's a relative path at this point. Do we want to try side-by-side
  2596. // isolation of the file?
  2597. if (Flags & RTL_DOS_SEARCH_PATH_FLAG_APPLY_ISOLATION_REDIRECTION) {
  2598. PUNICODE_STRING FullPathStringFound = NULL;
  2599. Status = RtlDosApplyFileIsolationRedirection_Ustr(
  2600. RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, FileName,
  2601. DefaultExtension, StaticString, DynamicString, &FullPathStringFound,
  2602. NULL, FilePartPrefixCch, BytesRequired);
  2603. if (NT_SUCCESS(Status)) {
  2604. if (FullFileNameOut != NULL) {
  2605. *FullFileNameOut = FullPathStringFound;
  2606. }
  2607. Status = STATUS_SUCCESS;
  2608. goto Exit;
  2609. }
  2610. if (Status != STATUS_SXS_KEY_NOT_FOUND) {
  2611. #if DBG
  2612. DbgPrint("%s: Failing because call to RtlDosApplyFileIsolationRedirection_Ustr() failed with status %wZ\n", __FUNCTION__, FileName);
  2613. #endif // DBG
  2614. goto Exit;
  2615. }
  2616. }
  2617. //
  2618. // If a default extension was provided, see if we need to account for it
  2619. //
  2620. if (DefaultExtension != NULL) {
  2621. DefaultExtensionLength = DefaultExtension->Length;
  2622. if (FileName->Length != 0) {
  2623. Cursor = FileName->Buffer + (FileName->Length / sizeof(WCHAR));
  2624. while (Cursor != FileName->Buffer) {
  2625. const WCHAR wch = *--Cursor;
  2626. if (IS_PATH_SEPARATOR_U(wch)) {
  2627. // it's a slash; we have a filename without an extension...
  2628. break;
  2629. }
  2630. if (wch == L'.') {
  2631. // There's an extension; ignore the defualt.
  2632. DefaultExtension = NULL;
  2633. DefaultExtensionLength = 0;
  2634. break;
  2635. }
  2636. }
  2637. }
  2638. }
  2639. if (Path->Length != 0) {
  2640. USHORT CchThisSegment;
  2641. PCWSTR LastCursor;
  2642. Cursor = Path->Buffer + (Path->Length / sizeof(WCHAR));
  2643. LastCursor = Cursor;
  2644. while (Cursor != Path->Buffer) {
  2645. if (*--Cursor == L';') {
  2646. CchThisSegment = (USHORT) ((LastCursor - Cursor) - 1);
  2647. if (CchThisSegment != 0) {
  2648. // If there is not a trailing slash, add one character
  2649. if (!IS_PATH_SEPARATOR_U(LastCursor[-1])) {
  2650. CchThisSegment++;
  2651. }
  2652. }
  2653. if (CchThisSegment > MaximumPathSegmentLength) {
  2654. MaximumPathSegmentLength = CchThisSegment;
  2655. }
  2656. // LastCursor now points to the semicolon...
  2657. LastCursor = Cursor;
  2658. }
  2659. }
  2660. CchThisSegment = (USHORT) (LastCursor - Cursor);
  2661. if (CchThisSegment != 0) {
  2662. if (!IS_PATH_SEPARATOR_U(LastCursor[-1])) {
  2663. CchThisSegment++;
  2664. }
  2665. }
  2666. if (CchThisSegment > MaximumPathSegmentLength) {
  2667. MaximumPathSegmentLength = CchThisSegment;
  2668. }
  2669. // Convert from WCHARs to bytes
  2670. MaximumPathSegmentLength *= sizeof(WCHAR);
  2671. }
  2672. BiggestPossibleFileName =
  2673. MaximumPathSegmentLength +
  2674. FileName->Length +
  2675. DefaultExtensionLength +
  2676. sizeof(WCHAR); // don't forget space for a trailing NULL...
  2677. // It's all got to fit into a UNICODE_STRING at some point, so check that that's possible
  2678. if (BiggestPossibleFileName > UNICODE_STRING_MAX_BYTES) {
  2679. #if DBG
  2680. DbgPrint("%s: returning STATUS_NAME_TOO_LONG because the computed worst case file name length is %Iu bytes\n", __FUNCTION__, BiggestPossibleFileName);
  2681. #endif // DBG
  2682. Status = STATUS_NAME_TOO_LONG;
  2683. goto Exit;
  2684. }
  2685. // It's tempting to allocate the dynamic buffer here, but if it turns out that
  2686. // the file is quickly found in one of the first segments that fits in the
  2687. // static buffer, we'll have wasted a heap alloc.
  2688. Cursor = Path->Buffer;
  2689. EndMarker = Cursor + (Path->Length / sizeof(WCHAR));
  2690. while (Cursor != EndMarker) {
  2691. PWSTR BufferToFill = NULL;
  2692. PWSTR BufferToFillCursor;
  2693. PWSTR SegmentEnd = Cursor;
  2694. USHORT SegmentSize;
  2695. USHORT BytesToCopy;
  2696. UNICODE_STRING DebugString;
  2697. // Scan ahead for the end of the path buffer or the next semicolon
  2698. while ((SegmentEnd != EndMarker) && (*SegmentEnd != L';'))
  2699. SegmentEnd++;
  2700. SegmentSize = (USHORT) ((SegmentEnd - Cursor) * sizeof(WCHAR));
  2701. DebugString.Buffer = Cursor;
  2702. DebugString.Length = SegmentSize;
  2703. DebugString.MaximumLength = SegmentSize;
  2704. BytesToCopy = SegmentSize;
  2705. // Add space for a trailing slash if there isn't one.
  2706. if ((SegmentSize != 0) && !IS_PATH_SEPARATOR_U(SegmentEnd[-1])) {
  2707. SegmentSize += sizeof(WCHAR);
  2708. }
  2709. // If the string we're using for the candidates isn't big enough, allocate one that is.
  2710. if (CandidateString.MaximumLength < (SegmentSize + FileName->Length + DefaultExtensionLength + sizeof(WCHAR))) {
  2711. // If CandidateString is already a dynamic buffer, something's hosed because we should have allocated
  2712. // the largest one needed the first time we outgrew the static one.
  2713. ASSERT(CandidateString.Buffer == StaticCandidateBuffer);
  2714. if (CandidateString.Buffer != StaticCandidateBuffer) {
  2715. #if DBG
  2716. DbgPrint("%s: internal error #1; CandidateString.Buffer = %p; StaticCandidateBuffer = %p\n", __FUNCTION__, CandidateString.Buffer, StaticCandidateBuffer);
  2717. #endif // DBG
  2718. Status = STATUS_INTERNAL_ERROR;
  2719. goto Exit;
  2720. }
  2721. // If this assert fires, there's either a code bug above where we computed the maximum
  2722. // segment length, or someone's changing either the filename, default extension or
  2723. // path around on us in another thread. Performing a capture on the buffers seems like
  2724. // massive overkill, so we'll just not overrun our buffers here.
  2725. ASSERT((SegmentSize + FileName->Length + DefaultExtensionLength) < UNICODE_STRING_MAX_BYTES);
  2726. if ((SegmentSize + FileName->Length + DefaultExtensionLength) > UNICODE_STRING_MAX_BYTES) {
  2727. #if DBG
  2728. DbgPrint("%s: internal error #2; SegmentSize = %u, FileName->Length = %u, DefaultExtensionLength = %u\n", __FUNCTION__,
  2729. SegmentSize, FileName->Length, DefaultExtensionLength);
  2730. #endif // DBG
  2731. Status = STATUS_INTERNAL_ERROR;
  2732. goto Exit;
  2733. }
  2734. CandidateString.MaximumLength = (USHORT) BiggestPossibleFileName;
  2735. CandidateString.Buffer = (RtlAllocateStringRoutine)(CandidateString.MaximumLength);
  2736. if (CandidateString.Buffer == NULL) {
  2737. #if DBG
  2738. DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n", __FUNCTION__, CandidateString.MaximumLength);
  2739. #endif // DBG
  2740. Status = STATUS_NO_MEMORY;
  2741. goto Exit;
  2742. }
  2743. }
  2744. RtlCopyMemory(
  2745. CandidateString.Buffer,
  2746. Cursor,
  2747. BytesToCopy);
  2748. BufferToFillCursor = CandidateString.Buffer + (BytesToCopy / sizeof(WCHAR));
  2749. // Add a trailing slash if it was omitted. It's safe to index [-1] since
  2750. // we know that SegmentSize != 0.
  2751. if ((SegmentSize != 0) && (BytesToCopy != SegmentSize))
  2752. *BufferToFillCursor++ = L'\\';
  2753. RtlCopyMemory(
  2754. BufferToFillCursor,
  2755. FileName->Buffer,
  2756. FileName->Length);
  2757. BufferToFillCursor += (FileName->Length / sizeof(WCHAR));
  2758. if (DefaultExtension != NULL) {
  2759. RtlCopyMemory(
  2760. BufferToFillCursor,
  2761. DefaultExtension->Buffer,
  2762. DefaultExtension->Length);
  2763. BufferToFillCursor += (DefaultExtension->Length / sizeof(WCHAR));
  2764. }
  2765. // And top it off with a unicode null...
  2766. *BufferToFillCursor = UNICODE_NULL;
  2767. CandidateString.Length = (USHORT) ((BufferToFillCursor - CandidateString.Buffer) * sizeof(WCHAR));
  2768. if (RtlDoesFileExists_UEx(CandidateString.Buffer, FALSE)) {
  2769. // Run it through the path canonicalizer...
  2770. Status = RtlGetFullPathName_UstrEx(
  2771. &CandidateString,
  2772. StaticString,
  2773. DynamicString,
  2774. (PUNICODE_STRING *) FullFileNameOut,
  2775. FilePartPrefixCch,
  2776. NULL,
  2777. &PathType,
  2778. BytesRequired);
  2779. if (NT_SUCCESS(Status))
  2780. Status = STATUS_SUCCESS;
  2781. else {
  2782. #if DBG
  2783. if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) {
  2784. DbgPrint("%s: Failing because we thought we found %wZ on the search path, but RtlGetFullPathName_UstrEx() returned %08lx\n", __FUNCTION__, FileName, Status);
  2785. }
  2786. #endif // DBG
  2787. }
  2788. goto Exit;
  2789. }
  2790. if (SegmentEnd != EndMarker)
  2791. Cursor = SegmentEnd + 1;
  2792. else
  2793. Cursor = SegmentEnd;
  2794. }
  2795. Status = STATUS_NO_SUCH_FILE;
  2796. Exit:
  2797. if ((CandidateString.Buffer != NULL) &&
  2798. (CandidateString.Buffer != StaticCandidateBuffer)) {
  2799. RtlFreeUnicodeString(&CandidateString);
  2800. }
  2801. return Status;
  2802. }
  2803. VOID
  2804. RtlpCheckRelativeDrive(
  2805. WCHAR NewDrive
  2806. )
  2807. /*++
  2808. Routine Description:
  2809. This function is called whenever we are asked to expand a non
  2810. current directory drive relative name ( f:this\is\my\file ). In
  2811. this case, we validate the environment variable string to make sure
  2812. the current directory at that drive is valid. If not, we trim back to
  2813. the root.
  2814. Arguments:
  2815. NewDrive - Supplies the drive to check
  2816. Return Value:
  2817. None.
  2818. --*/
  2819. {
  2820. WCHAR EnvVarValueBuffer[DOS_MAX_PATH_LENGTH+12]; // + sizeof (\DosDevices\)
  2821. WCHAR EnvVarNameBuffer[4];
  2822. UNICODE_STRING EnvVarName;
  2823. UNICODE_STRING EnvValue;
  2824. NTSTATUS Status;
  2825. OBJECT_ATTRIBUTES Obja;
  2826. IO_STATUS_BLOCK IoStatusBlock;
  2827. HANDLE DirHandle;
  2828. ULONG HardErrorValue;
  2829. PTEB Teb;
  2830. EnvVarNameBuffer[0] = L'=';
  2831. EnvVarNameBuffer[1] = (WCHAR)NewDrive;
  2832. EnvVarNameBuffer[2] = L':';
  2833. EnvVarNameBuffer[3] = UNICODE_NULL;
  2834. RtlInitUnicodeString(&EnvVarName,EnvVarNameBuffer);
  2835. //
  2836. // capture the value in a buffer that has space at the front for the dos devices
  2837. // prefix
  2838. //
  2839. EnvValue.Length = 0;
  2840. EnvValue.MaximumLength = DOS_MAX_PATH_LENGTH<<1;
  2841. EnvValue.Buffer = &EnvVarValueBuffer[RtlpDosDevicesPrefix.Length>>1];
  2842. Status = RtlQueryEnvironmentVariable_U( NULL,
  2843. &EnvVarName,
  2844. &EnvValue
  2845. );
  2846. if ( !NT_SUCCESS( Status ) ) {
  2847. //
  2848. // Otherwise default to root directory of drive
  2849. //
  2850. EnvValue.Buffer[0] = (WCHAR)NewDrive;
  2851. EnvValue.Buffer[1] = L':';
  2852. EnvValue.Buffer[2] = L'\\';
  2853. EnvValue.Buffer[3] = UNICODE_NULL;
  2854. EnvValue.Length = 6;
  2855. }
  2856. //
  2857. // Form the NT name for this directory
  2858. //
  2859. EnvValue.Length = EnvValue.Length + RtlpDosDevicesPrefix.Length;
  2860. EnvValue.MaximumLength = sizeof(EnvValue);
  2861. EnvValue.Buffer = EnvVarValueBuffer;
  2862. RtlCopyMemory(EnvVarValueBuffer,RtlpDosDevicesPrefix.Buffer,RtlpDosDevicesPrefix.Length);
  2863. InitializeObjectAttributes(
  2864. &Obja,
  2865. &EnvValue,
  2866. OBJ_CASE_INSENSITIVE,
  2867. NULL,
  2868. NULL
  2869. );
  2870. Teb = NtCurrentTeb();
  2871. HardErrorValue = Teb->HardErrorsAreDisabled;
  2872. Teb->HardErrorsAreDisabled = 1;
  2873. Status = NtOpenFile(
  2874. &DirHandle,
  2875. SYNCHRONIZE,
  2876. &Obja,
  2877. &IoStatusBlock,
  2878. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2879. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
  2880. );
  2881. Teb->HardErrorsAreDisabled = HardErrorValue;
  2882. //
  2883. // If the open succeeds, then the directory is valid... No need to do anything
  2884. // further. If the open fails, then trim back the environment to the root.
  2885. //
  2886. if ( NT_SUCCESS(Status) ) {
  2887. NtClose(DirHandle);
  2888. return;
  2889. }
  2890. RtlpResetDriveEnvironment(NewDrive);
  2891. }
  2892. //
  2893. // The expected calling sequence for RtlDosApplyFileIsolationRedirection_Ustr() is something
  2894. // like this:
  2895. //
  2896. // {
  2897. // WCHAR Buffer[MAX_PATH];
  2898. // UNICODE_STRING PreAllocatedString;
  2899. // UNICODE_STRING DynamicallyAllocatedString;
  2900. // PUNICODE_STRING FullPath;
  2901. //
  2902. // PreAllocatedString.Length = 0;
  2903. // PreAllocatedString.MaximumLength = sizeof(Buffer);
  2904. // PreAllocatedString.Buffer = Buffer;
  2905. //
  2906. // Status = RtlDosApplyFileIsolationRedirection_Ustr(
  2907. // Flags,
  2908. // FileToCheck,
  2909. // DefaultExtensionToApply, // for example ".DLL"
  2910. // &PreAllocatedString,
  2911. // &DynamicallyAllocatedString,
  2912. // &FullPath);
  2913. // if (NT_ERROR(Status)) return Status;
  2914. // // now code uses FullPath as the complete path name...
  2915. //
  2916. // // In exit paths, always free the dynamic string:
  2917. // RtlFreeUnicodeString(&DynamicallyAllocatedString);
  2918. // }
  2919. //
  2920. NTSTATUS
  2921. RtlDosApplyFileIsolationRedirection_Ustr(
  2922. ULONG Flags,
  2923. PCUNICODE_STRING FileNameIn,
  2924. PCUNICODE_STRING DefaultExtension,
  2925. PUNICODE_STRING PreAllocatedString,
  2926. PUNICODE_STRING DynamicallyAllocatedString,
  2927. PUNICODE_STRING *FullPath,
  2928. ULONG *OutFlags,
  2929. SIZE_T *FilePartPrefixCch,
  2930. SIZE_T *BytesRequired
  2931. )
  2932. {
  2933. NTSTATUS Status;
  2934. ACTIVATION_CONTEXT_SECTION_KEYED_DATA askd = {sizeof(askd)};
  2935. PUNICODE_STRING TempString = NULL;
  2936. UNICODE_STRING TempDynamicString;
  2937. ULONG BytesLeft;
  2938. ULONG RealTotalPathLength;
  2939. ULONG i;
  2940. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION UNALIGNED * DllRedirData;
  2941. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT UNALIGNED * PathSegmentArray;
  2942. PCUNICODE_STRING FileName = NULL;
  2943. UNICODE_STRING FileNameWithExtension;
  2944. WCHAR FileNameWithExtensionBuffer[DOS_MAX_PATH_LENGTH/4];
  2945. PWSTR Cursor;
  2946. PACTIVATION_CONTEXT ActivationContext = NULL;
  2947. PCUNICODE_STRING AssemblyStorageRoot = NULL;
  2948. WCHAR ExpandedTemporaryBuffer[DOS_MAX_PATH_LENGTH];
  2949. UNICODE_STRING ExpandedTemporaryString;
  2950. UNICODE_STRING DllNameUnderLocalDirString = { 0, 0, NULL };
  2951. RTL_PATH_TYPE PathType = 0;
  2952. PCUNICODE_STRING LocalDllNameString = NULL;
  2953. #if DBG_SXS
  2954. DbgPrintEx(
  2955. DPFLTR_SXS_ID,
  2956. DPFLTR_TRACE_LEVEL,
  2957. "SXS: Entered %s\n", __FUNCTION__);
  2958. #endif // DBG_SXS
  2959. if (DynamicallyAllocatedString != NULL) {
  2960. DynamicallyAllocatedString->Length = 0;
  2961. DynamicallyAllocatedString->MaximumLength = 0;
  2962. DynamicallyAllocatedString->Buffer = NULL;
  2963. }
  2964. if (OutFlags != NULL)
  2965. *OutFlags = 0;
  2966. if (FilePartPrefixCch != NULL)
  2967. *FilePartPrefixCch = 0;
  2968. if (BytesRequired != NULL)
  2969. *BytesRequired = 0;
  2970. TempDynamicString.Length = 0;
  2971. TempDynamicString.MaximumLength = 0;
  2972. TempDynamicString.Buffer = NULL;
  2973. ExpandedTemporaryString.Length = 0;
  2974. ExpandedTemporaryString.MaximumLength = sizeof(ExpandedTemporaryBuffer);
  2975. ExpandedTemporaryString.Buffer = ExpandedTemporaryBuffer;
  2976. FileNameWithExtension.Buffer = NULL;
  2977. // Valid input conditions:
  2978. // 1. You have to have a filename
  2979. // 2. If you specify both the preallocated buffer and the dynamically
  2980. // allocated buffer, you need the FullPath parameter to detect
  2981. // which was actually populated.
  2982. // 3. If you ask for the file part prefix cch, you need an output
  2983. // buffer; otherwise you can't know what the cch is relative to.
  2984. if (((Flags & ~(RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL)) != 0) ||
  2985. (FileNameIn == NULL) ||
  2986. ((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL) && (FilePartPrefixCch != NULL)) ||
  2987. ((PreAllocatedString != NULL) &&
  2988. (DynamicallyAllocatedString != NULL) &&
  2989. (FullPath == NULL))) {
  2990. #if DBG
  2991. DbgPrintEx(
  2992. DPFLTR_SXS_ID,
  2993. DPFLTR_ERROR_LEVEL,
  2994. "%s - invalid parameter(s)\n"
  2995. " FileNameIn = %p\n"
  2996. " PreAllocatedString = %p\n"
  2997. " DynamicallyAllocatedString = %p\n"
  2998. " FullPath = %p\n"
  2999. " FilePartPrefixCch = %p\n",
  3000. __FUNCTION__,
  3001. FileNameIn,
  3002. PreAllocatedString,
  3003. DynamicallyAllocatedString,
  3004. FullPath,
  3005. FilePartPrefixCch);
  3006. #endif // DBG
  3007. Status = STATUS_INVALID_PARAMETER;
  3008. goto Exit;
  3009. }
  3010. PathType = RtlDetermineDosPathNameType_Ustr(FileNameIn);
  3011. if (PathType!= RtlPathTypeRelative) {
  3012. //
  3013. // We only redirect relative paths for actctx; bypass all extra work for non-relative paths
  3014. // but for local file, we need verify its existence of those files.
  3015. //
  3016. if ((PathType == RtlPathTypeDriveAbsolute) || (PathType == RtlPathTypeUncAbsolute))
  3017. {
  3018. // check the existence of the local
  3019. PPEB pPeb = NtCurrentPeb();
  3020. if ((pPeb != NULL) && (pPeb->ProcessParameters != NULL) && (pPeb->ProcessParameters->Flags & RTL_USER_PROC_DLL_REDIRECTION_LOCAL)) // there is .local
  3021. {
  3022. // check whether this file is under the app directory
  3023. const USHORT cbFullImageNameLength = pPeb->ProcessParameters->ImagePathName.Length;
  3024. const LPWSTR pFullImageName = (PWSTR)pPeb->ProcessParameters->ImagePathName.Buffer;
  3025. LPWSTR p, p1, t, t1;
  3026. USHORT cbPathLength, cbDllPathLength, cbApplicationPathLength;
  3027. if (pFullImageName == NULL)
  3028. goto SxsKeyNotFound;
  3029. p = pFullImageName + cbFullImageNameLength/sizeof(WCHAR) - 1; // point to last character of this name
  3030. p1 = NULL;
  3031. while (p != pFullImageName) {
  3032. if (*p == (WCHAR)'\\') {
  3033. p1 = p + 1;
  3034. break;
  3035. }
  3036. p-- ;
  3037. }
  3038. ASSERT(p1 != NULL);
  3039. cbApplicationPathLength = (USHORT)(p1 - pFullImageName) * sizeof(WCHAR);
  3040. t = FileNameIn->Buffer + FileNameIn->Length / sizeof(WCHAR) -1; // point to last char of this full-qualified filename
  3041. t1 = NULL;
  3042. while (t != FileNameIn->Buffer ) {
  3043. if (*t == (WCHAR)'\\') {
  3044. t1 = t + 1;
  3045. break;
  3046. }
  3047. t-- ;
  3048. }
  3049. ASSERT(t1 != NULL);
  3050. cbDllPathLength = (USHORT)(t1 - FileNameIn->Buffer) * sizeof(WCHAR);
  3051. // comparison the path
  3052. if (cbDllPathLength == cbApplicationPathLength)
  3053. {
  3054. UNICODE_STRING ustrAppPath, ustrDllPath;
  3055. ustrAppPath.Buffer = pFullImageName;
  3056. ustrAppPath.Length = cbApplicationPathLength;
  3057. ustrAppPath.MaximumLength = cbApplicationPathLength;
  3058. ustrDllPath.Buffer = FileNameIn->Buffer;
  3059. ustrDllPath.Length = cbDllPathLength;
  3060. ustrDllPath.MaximumLength = cbDllPathLength;
  3061. if (RtlCompareUnicodeString(&ustrAppPath, &ustrDllPath, TRUE) == 0 )
  3062. {
  3063. // .local and local binary
  3064. LocalDllNameString = FileNameIn;
  3065. goto CopyIntoOutBuffer;
  3066. }
  3067. }
  3068. // for file name with a fullly qualified path, which is not the app dir,
  3069. // we need check the appdir and appdir\app.exe.local\ whether a file with the same name exists,
  3070. // If so, it is redired. Just like a relative-path filename
  3071. goto DoAllWork; // only for .local case
  3072. }
  3073. }
  3074. SxsKeyNotFound:
  3075. Status = STATUS_SXS_KEY_NOT_FOUND;
  3076. goto Exit;
  3077. }
  3078. DoAllWork:
  3079. // See if we need to default the extension...
  3080. if ((DefaultExtension != NULL) && (DefaultExtension->Length != 0)) {
  3081. SIZE_T BytesRequired;
  3082. if (FileNameIn->Length >= sizeof(WCHAR)) {
  3083. Cursor = FileNameIn->Buffer + ((FileNameIn->Length / sizeof(WCHAR)) - 1);
  3084. while (Cursor >= FileNameIn->Buffer) {
  3085. const WCHAR wch = *Cursor--;
  3086. if (IS_PATH_SEPARATOR_U(wch)) {
  3087. // we've come to the end of the last path segment; break out.
  3088. break;
  3089. }
  3090. if (wch == L'.') {
  3091. // There's an extension; don't default anything.
  3092. DefaultExtension = NULL;
  3093. break;
  3094. }
  3095. }
  3096. }
  3097. // If it's still not null, we have some work to do.
  3098. if (DefaultExtension != NULL) {
  3099. BytesRequired = FileNameIn->Length + DefaultExtension->Length + sizeof(WCHAR);
  3100. if (BytesRequired > UNICODE_STRING_MAX_BYTES) {
  3101. Status = STATUS_NAME_TOO_LONG;
  3102. goto Exit;
  3103. }
  3104. if (BytesRequired > sizeof(FileNameWithExtensionBuffer)) {
  3105. FileNameWithExtension.Buffer = (RtlAllocateStringRoutine)(BytesRequired);
  3106. if (FileNameWithExtension.Buffer == NULL) {
  3107. Status = STATUS_NO_MEMORY;
  3108. goto Exit;
  3109. }
  3110. FileNameWithExtension.MaximumLength = (USHORT) BytesRequired;
  3111. } else {
  3112. FileNameWithExtension.Buffer = FileNameWithExtensionBuffer;
  3113. FileNameWithExtension.MaximumLength = sizeof(FileNameWithExtensionBuffer);
  3114. }
  3115. FileNameWithExtension.Length = ((USHORT) BytesRequired - sizeof(WCHAR));
  3116. RtlCopyMemory(
  3117. FileNameWithExtension.Buffer,
  3118. FileNameIn->Buffer,
  3119. FileNameIn->Length);
  3120. RtlCopyMemory(
  3121. FileNameWithExtension.Buffer + (FileNameIn->Length / sizeof(WCHAR)),
  3122. DefaultExtension->Buffer,
  3123. DefaultExtension->Length);
  3124. FileNameWithExtension.Buffer[(FileNameIn->Length + DefaultExtension->Length) / sizeof(WCHAR)] = UNICODE_NULL;
  3125. FileName = &FileNameWithExtension;
  3126. }
  3127. }
  3128. // If we didn't munge it, use the pointer to the filename passed in
  3129. if (FileName == NULL) {
  3130. FileName = FileNameIn;
  3131. }
  3132. //
  3133. // Before the actctx is searched, check whether .local is respected.
  3134. // 1) if .local is respected
  3135. // 2) get the .local filename
  3136. // 3) if the .local file exist,
  3137. // 4) return the local filename
  3138. // 5) endif
  3139. // 6) endif
  3140. if ((Flags & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL) &&
  3141. (NtCurrentPeb()->ProcessParameters != NULL) &&
  3142. (NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROC_DLL_REDIRECTION_LOCAL))
  3143. {
  3144. BOOLEAN fLocalExist = FALSE;
  3145. //
  3146. // \x\foo.exe
  3147. // ExpandedTemporaryString is \x\bar.dll
  3148. // DllNameUnderLocalDirString is \x\foo.exe.local\bar.dll
  3149. //
  3150. Status = RtlComputePrivatizedDllName_U(FileName, &ExpandedTemporaryString, &DllNameUnderLocalDirString);
  3151. if(!NT_SUCCESS(Status))
  3152. goto Exit;
  3153. #if DBG && 0
  3154. {
  3155. BOOLEAN Boolean;
  3156. NTSTATUS Status;
  3157. HANDLE Handle = NULL;
  3158. const PCUNICODE_STRING Files[] = { &DllNameUnderLocalDirString, &ExpandedTemporaryString };
  3159. ULONG i;
  3160. for (i = 0 ; i != RTL_NUMBER_OF(Files) ; ++i) {
  3161. const PCUNICODE_STRING File = Files[i];
  3162. Boolean = RtlDoesFileExists_UStr(File);
  3163. Status = RtlDoesFileExist3(&Handle, File, FILE_GENERIC_READ, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE);
  3164. DbgPrint("RtlDoesFileExists_UStr(%wZ):%s\n", File, Boolean ? "true" : "false");
  3165. DbgPrint("RtlDoesFileExist3(%wZ):%x\n", File, Status);
  3166. if (Handle != NULL) {
  3167. NtClose(Handle);
  3168. Handle = NULL;
  3169. }
  3170. }
  3171. }
  3172. #endif
  3173. if (RtlDoesFileExists_UStr(&DllNameUnderLocalDirString))// there is a local dll, use it
  3174. {
  3175. LocalDllNameString = &DllNameUnderLocalDirString;
  3176. }
  3177. else if (RtlDoesFileExists_UStr(&ExpandedTemporaryString))// there is a local dll, use it
  3178. {
  3179. LocalDllNameString = &ExpandedTemporaryString;
  3180. }
  3181. CopyIntoOutBuffer:
  3182. //
  3183. // we have found the .local files
  3184. //
  3185. if (LocalDllNameString != NULL)
  3186. {
  3187. if ((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL))
  3188. {
  3189. Status = STATUS_SUCCESS;
  3190. // This was just an existence test. return the successful status.
  3191. goto Exit;
  3192. }
  3193. if ((DynamicallyAllocatedString == NULL) &&
  3194. (PreAllocatedString->MaximumLength < (LocalDllNameString->Length + sizeof(WCHAR)))) {
  3195. if (BytesRequired != NULL)
  3196. *BytesRequired = (LocalDllNameString->Length + sizeof(WCHAR));
  3197. Status = STATUS_BUFFER_TOO_SMALL;
  3198. goto Exit;
  3199. }
  3200. if ((PreAllocatedString == NULL) ||
  3201. (PreAllocatedString->MaximumLength < (LocalDllNameString->Length + sizeof(WCHAR))))
  3202. {
  3203. TempDynamicString.Buffer = (RtlAllocateStringRoutine)(LocalDllNameString->Length + sizeof(WCHAR));
  3204. if (TempDynamicString.Buffer == NULL)
  3205. {
  3206. Status = STATUS_NO_MEMORY;
  3207. goto Exit;
  3208. }
  3209. TempDynamicString.MaximumLength = (USHORT) (LocalDllNameString->Length + sizeof(WCHAR));
  3210. TempString = &TempDynamicString;
  3211. } else
  3212. {
  3213. TempString = PreAllocatedString;
  3214. }
  3215. RtlCopyMemory(TempString->Buffer, LocalDllNameString->Buffer, LocalDllNameString->Length);
  3216. TempString->Length = (USHORT)LocalDllNameString->Length;
  3217. TempString->Buffer[TempString->Length / sizeof(WCHAR)] = 0;
  3218. if (OutFlags)
  3219. *OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT;
  3220. goto Done;
  3221. }
  3222. }
  3223. Status = RtlFindActivationContextSectionString(
  3224. FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_ACTIVATION_CONTEXT
  3225. | FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_FLAGS,
  3226. NULL,
  3227. ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
  3228. FileName,
  3229. &askd);
  3230. if (!NT_SUCCESS(Status)) {
  3231. // Map section not found back to key not found so that callers don't
  3232. // have to worry about sections being present vs. the lookup key
  3233. // being present.
  3234. if (Status == STATUS_SXS_SECTION_NOT_FOUND)
  3235. Status = STATUS_SXS_KEY_NOT_FOUND;
  3236. if (Status != STATUS_SXS_KEY_NOT_FOUND) {
  3237. #if DBG
  3238. DbgPrintEx(
  3239. DPFLTR_SXS_ID,
  3240. DPFLTR_ERROR_LEVEL,
  3241. "SXS: %s - RtlFindActivationContextSectionString() returned ntstatus 0x%08lx\n",
  3242. __FUNCTION__,
  3243. Status);
  3244. #endif // DBG
  3245. }
  3246. goto Exit;
  3247. }
  3248. ActivationContext = askd.ActivationContext;
  3249. if ((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL)) {
  3250. Status = STATUS_SUCCESS;
  3251. // This was just an existence test. return the successful status.
  3252. goto Exit;
  3253. }
  3254. if ((askd.Length < sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION)) ||
  3255. (askd.DataFormatVersion != ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_FORMAT_WHISTLER)) {
  3256. #if DBG
  3257. DbgPrintEx(
  3258. DPFLTR_SXS_ID,
  3259. DPFLTR_ERROR_LEVEL,
  3260. "SXS: %s - Length or data format version of dll redirection data invalid.\n",
  3261. __FUNCTION__);
  3262. #endif // DBG
  3263. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3264. goto Exit;
  3265. }
  3266. DllRedirData = (const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION UNALIGNED *) askd.Data;
  3267. // If the entry requires path root resolution, do so!
  3268. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT) {
  3269. NTSTATUS InnerStatus = STATUS_SUCCESS;
  3270. // There's no need to support both a dynamic root and environment variable
  3271. // expansion.
  3272. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
  3273. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3274. goto Exit;
  3275. }
  3276. Status = RtlGetAssemblyStorageRoot(
  3277. ((askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_PROCESS_DEFAULT)
  3278. ? RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_PROCESS_DEFAULT
  3279. : 0)
  3280. | ((askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_SYSTEM_DEFAULT)
  3281. ? RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_SYSTEM_DEFAULT
  3282. : 0),
  3283. ActivationContext,
  3284. askd.AssemblyRosterIndex,
  3285. &AssemblyStorageRoot,
  3286. &RtlpAssemblyStorageMapResolutionDefaultCallback,
  3287. (PVOID) &InnerStatus);
  3288. if (NT_ERROR(Status)) {
  3289. if (Status == STATUS_CANCELLED) {
  3290. if (NT_ERROR(InnerStatus)) {
  3291. Status = InnerStatus;
  3292. }
  3293. }
  3294. goto Exit;
  3295. }
  3296. }
  3297. RealTotalPathLength = DllRedirData->TotalPathLength;
  3298. if (!(DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_INCLUDES_BASE_NAME))
  3299. RealTotalPathLength += FileName->Length;
  3300. if (AssemblyStorageRoot != NULL)
  3301. RealTotalPathLength += AssemblyStorageRoot->Length;
  3302. // If the path doesn't fit into the statically allocated buffer and there isn't a dynamic one,
  3303. // we're in trouble.
  3304. if ((DynamicallyAllocatedString == NULL) &&
  3305. (PreAllocatedString->MaximumLength < (RealTotalPathLength + sizeof(WCHAR)))) {
  3306. if (BytesRequired != NULL)
  3307. *BytesRequired = (RealTotalPathLength + sizeof(WCHAR));
  3308. Status = STATUS_BUFFER_TOO_SMALL;
  3309. goto Exit;
  3310. }
  3311. // Allocate the dynamic string if we need to...
  3312. if ((PreAllocatedString == NULL) ||
  3313. (PreAllocatedString->MaximumLength < (RealTotalPathLength + sizeof(WCHAR)))) {
  3314. TempDynamicString.Buffer = (RtlAllocateStringRoutine)(RealTotalPathLength + sizeof(WCHAR));
  3315. if (TempDynamicString.Buffer == NULL) {
  3316. Status = STATUS_NO_MEMORY;
  3317. goto Exit;
  3318. }
  3319. TempDynamicString.MaximumLength = (USHORT) (RealTotalPathLength + sizeof(WCHAR));
  3320. TempString = &TempDynamicString;
  3321. } else {
  3322. TempString = PreAllocatedString;
  3323. }
  3324. Cursor = TempString->Buffer;
  3325. // Account for the trailing null character in BytesLeft up front...
  3326. BytesLeft = TempString->MaximumLength - sizeof(WCHAR);
  3327. // If the path segment list extends beyond the section, we're outta here
  3328. if ((((ULONG) DllRedirData->PathSegmentOffset) > askd.SectionTotalLength) ||
  3329. (((ULONG) (DllRedirData->PathSegmentOffset + (DllRedirData->PathSegmentCount * sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT)))) > askd.SectionTotalLength)) {
  3330. #if DBG
  3331. DbgPrintEx(
  3332. DPFLTR_SXS_ID,
  3333. DPFLTR_ERROR_LEVEL,
  3334. "SXS: %s - Path segment array extends beyond section limits\n",
  3335. __FUNCTION__);
  3336. #endif // DBG
  3337. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3338. goto Exit;
  3339. }
  3340. // If we have a dynamically obtained storage root, put it in the buffer first.
  3341. if (AssemblyStorageRoot != NULL) {
  3342. USHORT Length = AssemblyStorageRoot->Length; // capture the value so that we don't possibly overwrite the buffer
  3343. if (BytesLeft < Length) {
  3344. Status = STATUS_INTERNAL_ERROR; // someone's playing tricks on us with the unicode string
  3345. goto Exit;
  3346. }
  3347. RtlCopyMemory(Cursor, AssemblyStorageRoot->Buffer, Length);
  3348. Cursor = (PWSTR) (((ULONG_PTR) Cursor) + Length);
  3349. BytesLeft -= Length;
  3350. }
  3351. PathSegmentArray = (PCACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT) (((ULONG_PTR) askd.SectionBase) + DllRedirData->PathSegmentOffset);
  3352. for (i=0; i<DllRedirData->PathSegmentCount; i++)
  3353. {
  3354. // It would seem that we could just report insufficient buffer here, but the total
  3355. // size checked earlier indicated that the FullPath variable was big enough;
  3356. // the data structure is hosed.
  3357. if (BytesLeft < PathSegmentArray[i].Length) {
  3358. #if DBG
  3359. DbgPrintEx(
  3360. DPFLTR_SXS_ID,
  3361. DPFLTR_ERROR_LEVEL,
  3362. "SXS: %s - path segment %lu overflowed buffer\n",
  3363. __FUNCTION__, i);
  3364. #endif // DBG
  3365. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3366. goto Exit;
  3367. }
  3368. // If the character array is outside the bounds of the section, something's hosed.
  3369. if ((((ULONG) PathSegmentArray[i].Offset) > askd.SectionTotalLength) ||
  3370. (((ULONG) (PathSegmentArray[i].Offset + PathSegmentArray[i].Length)) > askd.SectionTotalLength)) {
  3371. #if DBG
  3372. DbgPrintEx(
  3373. DPFLTR_SXS_ID,
  3374. DPFLTR_ERROR_LEVEL,
  3375. "SXS: %s - path segment %lu at %p (offset: %ld, length: %lu, bounds: %lu) buffer is outside section bounds\n",
  3376. __FUNCTION__,
  3377. i,
  3378. &PathSegmentArray[i].Offset,
  3379. PathSegmentArray[i].Offset,
  3380. PathSegmentArray[i].Length,
  3381. askd.SectionTotalLength);
  3382. #endif // DBG
  3383. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3384. goto Exit;
  3385. }
  3386. RtlCopyMemory(
  3387. Cursor,
  3388. (PVOID) (((ULONG_PTR) askd.SectionBase) + PathSegmentArray[i].Offset),
  3389. PathSegmentArray[i].Length);
  3390. Cursor += (PathSegmentArray[i].Length / sizeof(WCHAR));
  3391. BytesLeft -= PathSegmentArray[i].Length;
  3392. }
  3393. if (!(DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_INCLUDES_BASE_NAME)) {
  3394. if (BytesLeft < FileName->Length) {
  3395. #if DBG
  3396. DbgPrintEx(
  3397. DPFLTR_SXS_ID,
  3398. DPFLTR_ERROR_LEVEL,
  3399. "SXS: %s - appending base file name overflowed buffer\n",
  3400. __FUNCTION__);
  3401. #endif // DBG
  3402. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  3403. goto Exit;
  3404. }
  3405. RtlCopyMemory(
  3406. Cursor,
  3407. FileName->Buffer,
  3408. FileName->Length);
  3409. Cursor += (FileName->Length / sizeof(WCHAR));
  3410. BytesLeft -= FileName->Length;
  3411. }
  3412. *Cursor = L'\0';
  3413. TempString->Length = (USHORT) (((ULONG_PTR) Cursor) - ((ULONG_PTR) TempString->Buffer));
  3414. // Apply any environment strings as necessary...
  3415. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
  3416. ULONG ExpandedStringLength;
  3417. ExpandedTemporaryString.Length = 0;
  3418. ExpandedTemporaryString.MaximumLength = sizeof(ExpandedTemporaryBuffer) - sizeof(WCHAR);
  3419. ExpandedTemporaryString.Buffer = ExpandedTemporaryBuffer;
  3420. Status = RtlExpandEnvironmentStrings_U(NULL, TempString, &ExpandedTemporaryString, &ExpandedStringLength);
  3421. if (NT_ERROR(Status)) {
  3422. if (Status != STATUS_BUFFER_TOO_SMALL)
  3423. goto Exit;
  3424. // If it expands to something larger than UNICODE_MAX_BYTES, there's no hope.
  3425. if (ExpandedStringLength > UNICODE_STRING_MAX_BYTES) {
  3426. Status = STATUS_NAME_TOO_LONG;
  3427. goto Exit;
  3428. }
  3429. ExpandedTemporaryString.Buffer = (RtlAllocateStringRoutine)(ExpandedStringLength);
  3430. if (ExpandedTemporaryString.Buffer == NULL) {
  3431. Status = STATUS_NO_MEMORY;
  3432. goto Exit;
  3433. }
  3434. // This cast is OK because we already range checked the value.
  3435. ExpandedTemporaryString.MaximumLength = (USHORT) ExpandedStringLength;
  3436. ExpandedTemporaryString.Length = 0;
  3437. Status = RtlExpandEnvironmentStrings_U(NULL, TempString, &ExpandedTemporaryString, &ExpandedStringLength);
  3438. if (NT_ERROR(Status))
  3439. goto Exit;
  3440. ExpandedTemporaryString.Buffer[ExpandedTemporaryString.Length / sizeof(WCHAR)] = L'\0';
  3441. // Now move this thing into the caller's buffer
  3442. if ((PreAllocatedString != NULL) && (PreAllocatedString->MaximumLength >= ExpandedTemporaryString.MaximumLength)) {
  3443. // It does. Just copy the bits and let's go.
  3444. RtlCopyMemory(
  3445. PreAllocatedString->Buffer,
  3446. ExpandedTemporaryString.Buffer,
  3447. ExpandedTemporaryString.MaximumLength); // MaximumLength because it includes the trailing null character
  3448. PreAllocatedString->Length = ExpandedTemporaryString.Length;
  3449. TempString = PreAllocatedString;
  3450. } else if (DynamicallyAllocatedString != NULL) {
  3451. // If they can take a dynamic string, we'll use this one.
  3452. TempString = &ExpandedTemporaryString;
  3453. } else {
  3454. // Just plain no room.
  3455. if (BytesRequired != NULL)
  3456. *BytesRequired = ExpandedTemporaryString.MaximumLength;
  3457. Status = STATUS_BUFFER_TOO_SMALL;
  3458. goto Exit;
  3459. }
  3460. } else {
  3461. ExpandedTemporaryString.Buffer[ExpandedTemporaryString.Length / sizeof(WCHAR)] = L'\0';
  3462. // Hey, it succeeded! Let's see if we can fit this into the output static string.
  3463. if ((PreAllocatedString != NULL) && ((ExpandedTemporaryString.Length + sizeof(WCHAR)) < PreAllocatedString->MaximumLength)) {
  3464. // It fits!
  3465. RtlCopyMemory(
  3466. PreAllocatedString->Buffer,
  3467. ExpandedTemporaryBuffer,
  3468. ExpandedTemporaryString.Length + sizeof(WCHAR));
  3469. PreAllocatedString->Length = ExpandedTemporaryString.Length;
  3470. } else {
  3471. // Let's make a dynamic string. It's possible that we already have a dynamic string
  3472. // but the chance is pretty darned remote so I won't optimize for it. -mgrier
  3473. if ((ExpandedTemporaryString.Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) {
  3474. Status = STATUS_NAME_TOO_LONG;
  3475. goto Exit;
  3476. }
  3477. ExpandedTemporaryString.MaximumLength = (USHORT) (ExpandedTemporaryString.Length + sizeof(WCHAR));
  3478. ExpandedTemporaryString.Buffer = (RtlAllocateStringRoutine)(ExpandedTemporaryString.MaximumLength);
  3479. if (ExpandedTemporaryString.Buffer == NULL) {
  3480. Status = STATUS_NO_MEMORY;
  3481. goto Exit;
  3482. }
  3483. RtlCopyMemory(
  3484. ExpandedTemporaryString.Buffer,
  3485. ExpandedTemporaryBuffer,
  3486. ExpandedTemporaryString.MaximumLength);
  3487. TempString = &ExpandedTemporaryString;
  3488. }
  3489. }
  3490. }
  3491. if (OutFlags)
  3492. *OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_ACTCTX_REDIRECT;
  3493. Done:
  3494. if (FilePartPrefixCch != NULL) {
  3495. //
  3496. // set Cursor at the end of the unicode string
  3497. //
  3498. Cursor = TempString->Buffer + TempString->Length;
  3499. while (Cursor != TempString->Buffer) {
  3500. if (IS_PATH_SEPARATOR_U(*Cursor)) {
  3501. break;
  3502. }
  3503. Cursor--;
  3504. }
  3505. *FilePartPrefixCch = (Cursor - TempString->Buffer) + 1;
  3506. }
  3507. if (TempString != PreAllocatedString)
  3508. {
  3509. *DynamicallyAllocatedString = *TempString;
  3510. //
  3511. // TempString->Buffer is dynamically allocated, and this memory would be
  3512. // passed to caller, so set it to be NULL to avoid to be freed;
  3513. //
  3514. TempDynamicString.Buffer = NULL;
  3515. if (FullPath != NULL)
  3516. *FullPath = DynamicallyAllocatedString;
  3517. }
  3518. else
  3519. if (FullPath != NULL)
  3520. *FullPath = PreAllocatedString;
  3521. Status = STATUS_SUCCESS;
  3522. Exit:
  3523. if (DllNameUnderLocalDirString.Buffer != NULL)
  3524. (RtlFreeStringRoutine)(DllNameUnderLocalDirString.Buffer);
  3525. if (TempDynamicString.Buffer != NULL) {
  3526. (RtlFreeStringRoutine)(TempDynamicString.Buffer);
  3527. }
  3528. if ((FileNameWithExtension.Buffer != NULL) && (FileNameWithExtension.Buffer != FileNameWithExtensionBuffer)) {
  3529. (RtlFreeStringRoutine)(FileNameWithExtension.Buffer);
  3530. }
  3531. if ((ExpandedTemporaryString.Buffer != NULL) && (ExpandedTemporaryString.Buffer != ExpandedTemporaryBuffer)) {
  3532. (RtlFreeStringRoutine)(ExpandedTemporaryString.Buffer);
  3533. }
  3534. if (ActivationContext != NULL)
  3535. RtlReleaseActivationContext(ActivationContext);
  3536. return Status;
  3537. }
  3538. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT (0x00000001)
  3539. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS (0x00000002)
  3540. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT (0x00000003)
  3541. #define RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS (0x00000004)
  3542. NTSTATUS
  3543. NTAPI
  3544. RtlpGetLengthWithoutLastPathElement(
  3545. IN ULONG Flags,
  3546. IN ULONG PathType,
  3547. IN PCUNICODE_STRING Path,
  3548. OUT ULONG* LengthOut
  3549. )
  3550. /*++
  3551. Routine Description:
  3552. Report how long Path would be if you remove its last element.
  3553. This is much simpler than RtlRemoveLastDosPathElement.
  3554. It is used to implement the other RtlRemoveLast*PathElement.
  3555. Arguments:
  3556. Flags - room for future expansion
  3557. Path - the path is is an NT path or a full DOS path; the various relative DOS
  3558. path types do not work, see RtlRemoveLastDosPathElement for them.
  3559. Return Value:
  3560. STATUS_SUCCESS - the usual hunky-dory
  3561. STATUS_NO_MEMORY - the usual stress
  3562. STATUS_INVALID_PARAMETER - the usual bug
  3563. --*/
  3564. {
  3565. ULONG Index = 0;
  3566. ULONG Length = 0;
  3567. NTSTATUS Status = STATUS_SUCCESS;
  3568. RTL_PATH_TYPE DosPathType = RtlPathTypeUnknown;
  3569. ULONG DosPathFlags = 0;
  3570. const RTL_PATH_TYPE* AllowedPathTypes;
  3571. ULONG AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted)
  3572. | (1UL << RtlPathTypeUncAbsolute)
  3573. | (1UL << RtlPathTypeDriveAbsolute)
  3574. | (1UL << RtlPathTypeLocalDevice) // "\\?\"
  3575. | (1UL << RtlPathTypeRootLocalDevice) // "\\?"
  3576. ;
  3577. WCHAR PathSeperators[2] = { '/', '\\' };
  3578. #define LOCAL_IS_PATH_SEPERATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
  3579. if (LengthOut != NULL) {
  3580. *LengthOut = 0;
  3581. }
  3582. if ( !RTL_SOFT_VERIFY(Path != NULL)
  3583. || !RTL_SOFT_VERIFY(Flags == 0)
  3584. || !RTL_SOFT_VERIFY(LengthOut != NULL)
  3585. ) {
  3586. Status = STATUS_INVALID_PARAMETER;
  3587. goto Exit;
  3588. }
  3589. Length = RTL_STRING_GET_LENGTH_CHARS(Path);
  3590. switch (PathType)
  3591. {
  3592. default:
  3593. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_DOS:
  3594. Status = STATUS_INVALID_PARAMETER;
  3595. goto Exit;
  3596. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT:
  3597. //
  3598. // RtlpDetermineDosPathNameType4 calls it "rooted"
  3599. // only backslashes are seperators
  3600. // path must start with backslash
  3601. // second char must not be backslash
  3602. //
  3603. AllowedDosPathTypeBits = (1UL << RtlPathTypeRooted);
  3604. PathSeperators[0] = '\\';
  3605. if (Length > 0 && Path->Buffer[0] != '\\'
  3606. ) {
  3607. Status = STATUS_INVALID_PARAMETER;
  3608. goto Exit;
  3609. }
  3610. if (Length > 1 && Path->Buffer[1] == '\\'
  3611. ) {
  3612. Status = STATUS_INVALID_PARAMETER;
  3613. goto Exit;
  3614. }
  3615. break;
  3616. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS:
  3617. AllowedDosPathTypeBits &= ~(1UL << RtlPathTypeRooted);
  3618. break;
  3619. case RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT:
  3620. break;
  3621. }
  3622. if (Length == 0) {
  3623. goto Exit;
  3624. }
  3625. if (!RTL_SOFT_VERIFY(NT_SUCCESS(Status = RtlpDetermineDosPathNameType4(RTL_DETERMINE_DOS_PATH_NAME_TYPE_IN_FLAG_STRICT_WIN32NT, Path, &DosPathType, &DosPathFlags)))) {
  3626. goto Exit;
  3627. }
  3628. if (!RTL_SOFT_VERIFY((1UL << DosPathType) & AllowedDosPathTypeBits)
  3629. ) {
  3630. //KdPrintEx();
  3631. Status = STATUS_INVALID_PARAMETER;
  3632. goto Exit;
  3633. }
  3634. if (!RTL_SOFT_VERIFY(
  3635. (PathType & RTLP_DETERMINE_DOS_PATH_NAME_TYPE_OUT_FLAG_INVALID) == 0
  3636. )) {
  3637. Status = STATUS_INVALID_PARAMETER;
  3638. goto Exit;
  3639. }
  3640. // skip one or more trailing path seperators
  3641. for ( ; Length != 0 && LOCAL_IS_PATH_SEPERATOR(Path->Buffer[Length - 1]) ; --Length) {
  3642. // nothing
  3643. }
  3644. // skip trailing path element
  3645. for ( ; Length != 0 && !LOCAL_IS_PATH_SEPERATOR(Path->Buffer[Length - 1]) ; --Length) {
  3646. // nothing
  3647. }
  3648. // skip one or more in between path seperators
  3649. for ( ; Length != 0 && LOCAL_IS_PATH_SEPERATOR(Path->Buffer[Length - 1]) ; --Length) {
  3650. // nothing
  3651. }
  3652. // put back a trailing path seperator, for the sake of c:\ vs. c:
  3653. if (Length != 0) {
  3654. ++Length;
  3655. }
  3656. //
  3657. // Should optionally check for "bad dos roots" here.
  3658. //
  3659. *LengthOut = Length;
  3660. Status = STATUS_SUCCESS;
  3661. Exit:
  3662. return Status;
  3663. #undef LOCAL_IS_PATH_SEPERATOR
  3664. }
  3665. NTSTATUS
  3666. NTAPI
  3667. RtlGetLengthWithoutLastNtPathElement(
  3668. IN ULONG Flags,
  3669. IN PCUNICODE_STRING Path,
  3670. OUT ULONG* LengthOut
  3671. )
  3672. /*++
  3673. Routine Description:
  3674. Report how long Path would be if you remove its last element.
  3675. Arguments:
  3676. Flags - room for future expansion
  3677. Path - the path is is an NT path; the various DOS path types
  3678. do not work, see RtlRemoveLastDosPathElement for them.
  3679. Return Value:
  3680. STATUS_SUCCESS - the usual hunky-dory
  3681. STATUS_NO_MEMORY - the usual stress
  3682. STATUS_INVALID_PARAMETER - the usual bug
  3683. --*/
  3684. {
  3685. NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_NT, Path, LengthOut);
  3686. return Status;
  3687. }
  3688. NTSTATUS
  3689. NTAPI
  3690. RtlGetLengthWithoutLastFullDosOrNtPathElement(
  3691. IN ULONG Flags,
  3692. IN PCUNICODE_STRING Path,
  3693. OUT ULONG* LengthOut
  3694. )
  3695. /*++
  3696. Routine Description:
  3697. Report how long Path would be if you remove its last element.
  3698. Arguments:
  3699. Flags - room for future expansion
  3700. Path - the path is is an NT path; the various DOS path types
  3701. do not work, see RtlRemoveLastDosPathElement for them.
  3702. Return Value:
  3703. STATUS_SUCCESS - the usual hunky-dory
  3704. STATUS_NO_MEMORY - the usual stress
  3705. STATUS_INVALID_PARAMETER - the usual bug
  3706. --*/
  3707. {
  3708. NTSTATUS Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS_OR_NT, Path, LengthOut);
  3709. return Status;
  3710. }
  3711. CONST CHAR*
  3712. RtlpDbgBadDosRootPathTypeToString(
  3713. IN ULONG Flags,
  3714. IN ULONG RootType
  3715. )
  3716. /*++
  3717. Routine Description:
  3718. An aid to writing DbgPrint code.
  3719. Arguments:
  3720. Flags - room for future binary compatible expansion
  3721. RootType - fairly specifically what the string is
  3722. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX - \\? or \\?\
  3723. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX - \\?\unc or \\?\unc\
  3724. RTLP_BAD_DOS_ROOT_PATH_NT_PATH - \??\ but this i only a rough check
  3725. RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE - \\machine or \\?\unc\machine
  3726. RTLP_GOOD_DOS_ROOT_PATH - none of the above, seems ok
  3727. Return Value:
  3728. strings like those that describe RootType or "unknown" or empty in free builds
  3729. --*/
  3730. {
  3731. CONST CHAR* s = "";
  3732. #if DBG
  3733. if (Flags != 0) {
  3734. DbgPrint("Invalid parameter to %s ignored\n", __FUNCTION__);
  3735. }
  3736. switch (RootType
  3737. ) {
  3738. case RTLP_GOOD_DOS_ROOT_PATH : s = "good"; break;
  3739. case RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX : s = "\\\\?\\"; break;
  3740. case RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX: s = "\\\\?\\unc"; break;
  3741. case RTLP_BAD_DOS_ROOT_PATH_NT_PATH : s = "\\??\\"; break;
  3742. case RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE : s = "\\\\machine or \\\\?\\unc\\machine"; break;
  3743. default:
  3744. s = "unknown";
  3745. DbgPrint("Invalid parameter %0x08Ix to %s ignored\n", RootType, __FUNCTION__);
  3746. break;
  3747. }
  3748. #endif
  3749. return s;
  3750. }
  3751. NTSTATUS
  3752. RtlpCheckForBadDosRootPath(
  3753. IN ULONG Flags,
  3754. IN PCUNICODE_STRING Path,
  3755. OUT ULONG* RootType
  3756. )
  3757. /*++
  3758. Routine Description:
  3759. Arguments:
  3760. Flags - room for future binary compatible expansion
  3761. Path - the path to be checked
  3762. RootType - fairly specifically what the string is
  3763. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX - \\? or \\?\
  3764. RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX - \\?\unc or \\?\unc\
  3765. RTLP_BAD_DOS_ROOT_PATH_NT_PATH - \??\ but this i only a rough check
  3766. RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE - \\machine or \\?\unc\machine
  3767. RTLP_GOOD_DOS_ROOT_PATH - none of the above, seems ok
  3768. Return Value:
  3769. STATUS_SUCCESS -
  3770. STATUS_INVALID_PARAMETER -
  3771. Path is NULL
  3772. or Flags uses undefined values
  3773. --*/
  3774. {
  3775. ULONG Length = 0;
  3776. ULONG Index = 0;
  3777. NTSTATUS Status = STATUS_SUCCESS;
  3778. BOOLEAN Unc = FALSE;
  3779. BOOLEAN Unc1 = FALSE;
  3780. BOOLEAN Unc2 = FALSE;
  3781. ULONG PiecesSeen = 0;
  3782. if (RootType != NULL) {
  3783. *RootType = 0;
  3784. }
  3785. if ( !RTL_SOFT_VERIFY(Path != NULL)
  3786. || !RTL_SOFT_VERIFY(RootType != NULL)
  3787. || !RTL_SOFT_VERIFY(Flags == 0)
  3788. ) {
  3789. Status = STATUS_INVALID_PARAMETER;
  3790. goto Exit;
  3791. }
  3792. Length = Path->Length / sizeof(Path->Buffer[0]);
  3793. if (Length < 3 || !RTL_IS_PATH_SEPERATOR(Path->Buffer[0])) {
  3794. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  3795. goto Exit;
  3796. }
  3797. // prefix \??\ (heuristic, doesn't catch many NT paths)
  3798. if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), RTL_CONST_CAST(PUNICODE_STRING)(Path), TRUE)) {
  3799. *RootType = RTLP_BAD_DOS_ROOT_PATH_NT_PATH;
  3800. goto Exit;
  3801. }
  3802. if (!RTL_IS_PATH_SEPERATOR(Path->Buffer[1])) {
  3803. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  3804. goto Exit;
  3805. }
  3806. // == \\?
  3807. if (RtlEqualUnicodeString(Path, &RtlpWin32NtRoot, TRUE)) {
  3808. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
  3809. goto Exit;
  3810. }
  3811. if (RtlEqualUnicodeString(Path, &RtlpWin32NtRootSlash, TRUE)) {
  3812. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_PREFIX;
  3813. goto Exit;
  3814. }
  3815. // == \\?\unc
  3816. if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRoot, TRUE)) {
  3817. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
  3818. goto Exit;
  3819. }
  3820. if (RtlEqualUnicodeString(Path, &RtlpWin32NtUncRootSlash, TRUE)) {
  3821. *RootType = RTLP_BAD_DOS_ROOT_PATH_WIN32NT_UNC_PREFIX;
  3822. goto Exit;
  3823. }
  3824. // prefix \\ or \\?\unc
  3825. // must check the longer string first, or avoid the short circuit (| instead of ||)
  3826. Unc = (Unc1 = RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpWin32NtUncRootSlash), RTL_CONST_CAST(PUNICODE_STRING)(Path), TRUE))
  3827. || (Unc2 = RTL_IS_PATH_SEPERATOR(Path->Buffer[1]));
  3828. if (!Unc) {
  3829. *RootType = RTLP_GOOD_DOS_ROOT_PATH;
  3830. goto Exit;
  3831. }
  3832. //
  3833. // it's unc, see if it is only a machine (note that it'd be really nice if FindFirstFile(\\machine\*)
  3834. // just worked and we didn't have to care..)
  3835. //
  3836. // point index at a slash that precedes the machine, anywhere in the run of slashes,
  3837. // but after the \\? stuff
  3838. if (Unc1) {
  3839. Index = (RtlpWin32NtUncRootSlash.Length / sizeof(RtlpWin32NtUncRootSlash.Buffer[0])) - 1;
  3840. } else {
  3841. ASSERT(Unc2);
  3842. Index = 1;
  3843. }
  3844. ASSERT(RTL_IS_PATH_SEPERATOR(Path->Buffer[Index]));
  3845. Length = Path->Length/ sizeof(Path->Buffer[0]);
  3846. //
  3847. // skip leading slashes
  3848. //
  3849. for ( ; Index < Length && RTL_IS_PATH_SEPERATOR(Path->Buffer[Index]) ; ++Index) {
  3850. PiecesSeen |= 1;
  3851. }
  3852. // skip the machine name
  3853. for ( ; Index < Length && !RTL_IS_PATH_SEPERATOR(Path->Buffer[Index]) ; ++Index) {
  3854. PiecesSeen |= 2;
  3855. }
  3856. // skip the slashes between machine and share
  3857. for ( ; Index < Length && RTL_IS_PATH_SEPERATOR(Path->Buffer[Index]) ; ++Index) {
  3858. PiecesSeen |= 4;
  3859. }
  3860. // skip the share (make sure it's at least one char)
  3861. for ( ; Index < Length && !RTL_IS_PATH_SEPERATOR(Path->Buffer[Index]) ; ++Index) {
  3862. PiecesSeen |= 8;
  3863. break;
  3864. }
  3865. if (PiecesSeen != 0xF) {
  3866. *RootType = RTLP_BAD_DOS_ROOT_PATH_MACHINE_NO_SHARE;
  3867. goto Exit;
  3868. }
  3869. Status = STATUS_SUCCESS;
  3870. Exit:
  3871. return Status;
  3872. }
  3873. NTSTATUS
  3874. NTAPI
  3875. RtlpBadDosRootPathToEmptyString(
  3876. IN ULONG Flags,
  3877. IN OUT PUNICODE_STRING Path
  3878. )
  3879. /*++
  3880. Routine Description:
  3881. Arguments:
  3882. Flags - room for future binary compatible expansion
  3883. Path - the path to be checked and possibly emptied
  3884. Return Value:
  3885. STATUS_SUCCESS -
  3886. STATUS_INVALID_PARAMETER -
  3887. Path is NULL
  3888. or Flags uses undefined values
  3889. --*/
  3890. {
  3891. NTSTATUS Status = STATUS_SUCCESS;
  3892. ULONG RootType = 0;
  3893. if (!NT_SUCCESS(Status = RtlpCheckForBadDosRootPath(0, Path, &RootType))) {
  3894. goto Exit;
  3895. }
  3896. //
  3897. // this is not invalid parameter, our contract is we go \\machine\share to empty
  3898. // \\?\c: to empty, etc.
  3899. //
  3900. if (RootType != RTLP_GOOD_DOS_ROOT_PATH) {
  3901. if (RootType == RTLP_BAD_DOS_ROOT_PATH_NT_PATH) {
  3902. Status = STATUS_INVALID_PARAMETER;
  3903. goto Exit;
  3904. }
  3905. Path->Length = 0;
  3906. }
  3907. Status = STATUS_SUCCESS;
  3908. Exit:
  3909. return Status;
  3910. }
  3911. NTSTATUS
  3912. NTAPI
  3913. RtlGetLengthWithoutLastFullDosPathElement(
  3914. IN ULONG Flags,
  3915. IN PCUNICODE_STRING Path,
  3916. OUT ULONG* LengthOut
  3917. )
  3918. /*++
  3919. Routine Description:
  3920. Given a fulldospath, like c:\, \\machine\share, \\?\unc\machine\share, \\?\c:,
  3921. return (in an out parameter) the length if the last element was cut off.
  3922. Arguments:
  3923. Flags - room for future binary compatible expansion
  3924. Path - the path to be truncating
  3925. LengthOut - the length if the last path element is removed
  3926. Return Value:
  3927. STATUS_SUCCESS -
  3928. STATUS_INVALID_PARAMETER -
  3929. Path is NULL
  3930. or LengthOut is NULL
  3931. or Flags uses undefined values
  3932. --*/
  3933. {
  3934. NTSTATUS Status = STATUS_SUCCESS;
  3935. UNICODE_STRING CheckRootString = { 0 };
  3936. //
  3937. // parameter validation is done in RtlpGetLengthWithoutLastPathElement
  3938. //
  3939. Status = RtlpGetLengthWithoutLastPathElement(Flags, RTLP_LAST_PATH_ELEMENT_PATH_TYPE_FULL_DOS, Path, LengthOut);
  3940. if (!(NT_SUCCESS(Status))) {
  3941. goto Exit;
  3942. }
  3943. CheckRootString.Buffer = Path->Buffer;
  3944. CheckRootString.Length = (USHORT)(*LengthOut * sizeof(*Path->Buffer));
  3945. CheckRootString.MaximumLength = CheckRootString.Length;
  3946. if (!NT_SUCCESS(Status = RtlpBadDosRootPathToEmptyString(0, &CheckRootString))) {
  3947. goto Exit;
  3948. }
  3949. *LengthOut = RTL_STRING_GET_LENGTH_CHARS(&CheckRootString);
  3950. Status = STATUS_SUCCESS;
  3951. Exit:
  3952. KdPrintEx((
  3953. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  3954. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status));
  3955. return Status;
  3956. }
  3957. NTSTATUS
  3958. NTAPI
  3959. RtlAppendPathElement(
  3960. IN ULONG Flags,
  3961. IN OUT PRTL_UNICODE_STRING_BUFFER Path,
  3962. PCUNICODE_STRING ConstElement
  3963. )
  3964. /*++
  3965. Routine Description:
  3966. This function appends a path element to a path.
  3967. For now, like:
  3968. typedef PRTL_UNICODE_STRING_BUFFER PRTL_MUTABLE_PATH;
  3969. typedef PCUNICODE_STRING PCRTL_CONSTANT_PATH_ELEMENT;
  3970. Maybe something higher level in the future.
  3971. The result with regard to trailing slashes aims to be similar to the inputs.
  3972. If either Path or ConstElement contains a trailing slash, the result has a trailing slash.
  3973. The character used for the in between and trailing slash is picked among the existing
  3974. slashes in the strings.
  3975. Arguments:
  3976. Flags - the ever popular "room for future binary compatible expansion"
  3977. Path -
  3978. a string representing a path using \\ or / as seperators
  3979. ConstElement -
  3980. a string representing a path element
  3981. this can actually contain multiple \\ or / delimited path elements
  3982. only the start and end of the string are examined for slashes
  3983. Return Value:
  3984. STATUS_SUCCESS -
  3985. STATUS_INVALID_PARAMETER -
  3986. Path is NULL
  3987. or LengthOut is NULL
  3988. STATUS_NO_MEMORY - RtlHeapAllocate failed
  3989. STATUS_NAME_TOO_LONG - the resulting string does not fit in a UNICODE_STRING, due to its
  3990. use of USHORT instead of ULONG or SIZE_T
  3991. --*/
  3992. {
  3993. NTSTATUS Status = STATUS_SUCCESS;
  3994. UNICODE_STRING InBetweenSlashString = RtlpEmptyString;
  3995. UNICODE_STRING TrailingSlashString = RtlpEmptyString;
  3996. WCHAR Slashes[] = {0,0,0,0};
  3997. ULONG i;
  3998. UNICODE_STRING PathsToAppend[3]; // possible slash, element, possible slash
  3999. WCHAR PathSeperators[2] = { '/', '\\' };
  4000. #define LOCAL_IS_PATH_SEPERATOR(ch_) ((ch_) == PathSeperators[0] || (ch_) == PathSeperators[1])
  4001. if ( !RTL_SOFT_VERIFY((Flags & ~(RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR)) == 0)
  4002. || !RTL_SOFT_VERIFY(Path != NULL)
  4003. || !RTL_SOFT_VERIFY(ConstElement != NULL)
  4004. ) {
  4005. Status = STATUS_INVALID_PARAMETER;
  4006. goto Exit;
  4007. }
  4008. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) != 0) {
  4009. PathSeperators[0] = '\\';
  4010. }
  4011. if (ConstElement->Length != 0) {
  4012. UNICODE_STRING Element = *ConstElement;
  4013. //
  4014. // Note leading and trailing slashes on the inputs.
  4015. // So that we know if an in-between slash is needed, and if a trailing slash is needed,
  4016. // and to guide what sort of slash to place.
  4017. //
  4018. i = 0;
  4019. if (Path->String.Length != 0) {
  4020. ULONG j;
  4021. ULONG Length = Path->String.Length / sizeof(WCHAR);
  4022. //
  4023. // for the sake for dos drive paths, check the first three chars for a slash
  4024. //
  4025. for (j = 0 ; j < 3 && j < Length ; ++j) {
  4026. if (LOCAL_IS_PATH_SEPERATOR(Path->String.Buffer[j])) {
  4027. Slashes[i] = Path->String.Buffer[0];
  4028. break;
  4029. }
  4030. }
  4031. i += 1;
  4032. if (LOCAL_IS_PATH_SEPERATOR(Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1])) {
  4033. Slashes[i] = Path->String.Buffer[Path->String.Length/sizeof(WCHAR) - 1];
  4034. }
  4035. }
  4036. i = 2;
  4037. if (LOCAL_IS_PATH_SEPERATOR(Element.Buffer[0])) {
  4038. Slashes[i] = Element.Buffer[0];
  4039. }
  4040. i += 1;
  4041. if (LOCAL_IS_PATH_SEPERATOR(Element.Buffer[Element.Length/sizeof(WCHAR) - 1])) {
  4042. Slashes[i] = Element.Buffer[Element.Length/sizeof(WCHAR) - 1];
  4043. }
  4044. if (!Slashes[1] && !Slashes[2]) {
  4045. //
  4046. // first string lacks trailing slash and second string lacks leading slash,
  4047. // must insert one; we favor the types we have, otherwise use a default
  4048. //
  4049. InBetweenSlashString.Length = sizeof(WCHAR);
  4050. InBetweenSlashString.Buffer = RtlPathSeperatorString.Buffer;
  4051. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
  4052. if (Slashes[3]) {
  4053. InBetweenSlashString.Buffer = &Slashes[3];
  4054. } else if (Slashes[0]) {
  4055. InBetweenSlashString.Buffer = &Slashes[0];
  4056. }
  4057. }
  4058. }
  4059. if (Slashes[1] && !Slashes[3]) {
  4060. //
  4061. // first string has a trailing slash and second string does not,
  4062. // must add one, the same type
  4063. //
  4064. TrailingSlashString.Length = sizeof(WCHAR);
  4065. if ((Flags & RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR) == 0) {
  4066. TrailingSlashString.Buffer = &Slashes[1];
  4067. } else {
  4068. TrailingSlashString.Buffer = RtlPathSeperatorString.Buffer;
  4069. }
  4070. }
  4071. if (Slashes[1] && Slashes[2]) {
  4072. //
  4073. // have both trailing and leading slash, remove leading
  4074. //
  4075. Element.Buffer += 1;
  4076. Element.Length -= sizeof(WCHAR);
  4077. Element.MaximumLength -= sizeof(WCHAR);
  4078. }
  4079. i = 0;
  4080. PathsToAppend[i++] = InBetweenSlashString;
  4081. PathsToAppend[i++] = Element;
  4082. PathsToAppend[i++] = TrailingSlashString;
  4083. Status = RtlMultiAppendUnicodeStringBuffer(Path, RTL_NUMBER_OF(PathsToAppend), PathsToAppend);
  4084. if (!NT_SUCCESS(Status))
  4085. goto Exit;
  4086. }
  4087. Status = STATUS_SUCCESS;
  4088. Exit:
  4089. KdPrintEx((
  4090. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  4091. "%s(%d):%s(%wZ, %wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path ? &Path->String : NULL, ConstElement, Status));
  4092. return Status;
  4093. #undef LOCAL_IS_PATH_SEPERATOR
  4094. }
  4095. NTSTATUS
  4096. NTAPI
  4097. RtlGetLengthWithoutTrailingPathSeperators(
  4098. IN ULONG Flags,
  4099. IN PCUNICODE_STRING Path,
  4100. OUT ULONG* LengthOut
  4101. )
  4102. /*++
  4103. Routine Description:
  4104. This function computes the length of the string (in characters) if
  4105. trailing path seperators (\\ and /) are removed.
  4106. Arguments:
  4107. Path -
  4108. a string representing a path using \\ or / as seperators
  4109. LengthOut -
  4110. the length of String (in characters) having removed trailing characters
  4111. Return Value:
  4112. STATUS_SUCCESS -
  4113. STATUS_INVALID_PARAMETER -
  4114. Path is NULL
  4115. or LengthOut is NULL
  4116. --*/
  4117. {
  4118. NTSTATUS Status = STATUS_SUCCESS;
  4119. ULONG Index = 0;
  4120. ULONG Length = 0;
  4121. if (LengthOut != NULL) {
  4122. //
  4123. // Arguably this should be Path->Length / sizeof(*Path->Buffer), but as long
  4124. // as the callstack is all high quality code, it doesn't matter.
  4125. //
  4126. *LengthOut = 0;
  4127. }
  4128. if ( !RTL_SOFT_VERIFY(Path != NULL)
  4129. || !RTL_SOFT_VERIFY(LengthOut != NULL)
  4130. || !RTL_SOFT_VERIFY(Flags == 0)
  4131. ) {
  4132. Status = STATUS_INVALID_PARAMETER;
  4133. goto Exit;
  4134. }
  4135. Length = Path->Length / sizeof(*Path->Buffer);
  4136. for (Index = Length ; Index != 0 ; --Index) {
  4137. if (!RTL_IS_PATH_SEPERATOR(Path->Buffer[Index - 1])) {
  4138. break;
  4139. }
  4140. }
  4141. //*LengthOut = (Length - Index);
  4142. *LengthOut = Index;
  4143. Status = STATUS_SUCCESS;
  4144. Exit:
  4145. KdPrintEx((
  4146. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  4147. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status));
  4148. return Status;
  4149. }
  4150. NTSTATUS
  4151. NTAPI
  4152. RtlpApplyLengthFunction(
  4153. IN ULONG Flags,
  4154. IN SIZE_T SizeOfStruct,
  4155. IN OUT PVOID UnicodeStringOrUnicodeStringBuffer,
  4156. NTSTATUS (NTAPI* LengthFunction)(ULONG, PCUNICODE_STRING, ULONG*)
  4157. )
  4158. /*++
  4159. Routine Description:
  4160. This function is common code for patterns like
  4161. #define RtlRemoveTrailingPathSeperators(Path_) \
  4162. (RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutTrailingPathSeperators))
  4163. #define RtlRemoveLastPathElement(Path_) \
  4164. (RtlpApplyLengthFunction((Path_), sizeof(*(Path_)), RtlGetLengthWithoutLastPathElement))
  4165. Note that shortening a UNICODE_STRING only changes the length, whereas
  4166. shortening a RTL_UNICODE_STRING_BUFFER writes a terminal nul.
  4167. I expect this pattern will be less error prone than having clients pass the UNICODE_STRING
  4168. contained in the RTL_UNICODE_STRING_BUFFER followed by calling RTL_NUL_TERMINATE_STRING.
  4169. And, that pattern cannot be inlined with a macro while also preserving that we
  4170. return an NTSTATUS.
  4171. Arguments:
  4172. Flags - the ever popular "room for future binary compatible expansion"
  4173. UnicodeStringOrUnicodeStringBuffer -
  4174. a PUNICODE_STRING or PRTL_UNICODE_STRING_BUFFER, as indicated by
  4175. SizeOfStruct
  4176. SizeOfStruct -
  4177. a rough type indicator of UnicodeStringOrUnicodeStringBuffer, to allow for overloading in C
  4178. LengthFunction -
  4179. computes a length for UnicodeStringOrUnicodeStringBuffer to be shortened too
  4180. Return Value:
  4181. STATUS_SUCCESS -
  4182. STATUS_INVALID_PARAMETER -
  4183. SizeOfStruct not one of the expected sizes
  4184. or LengthFunction is NULL
  4185. or UnicodeStringOrUnicodeStringBuffer is NULL
  4186. --*/
  4187. {
  4188. PUNICODE_STRING UnicodeString = NULL;
  4189. PRTL_UNICODE_STRING_BUFFER UnicodeStringBuffer = NULL;
  4190. NTSTATUS Status = STATUS_SUCCESS;
  4191. ULONG Length = 0;
  4192. if (!RTL_SOFT_VERIFY(UnicodeStringOrUnicodeStringBuffer != NULL)) {
  4193. Status = STATUS_INVALID_PARAMETER;
  4194. goto Exit;
  4195. }
  4196. if (!RTL_SOFT_VERIFY(LengthFunction != NULL)) {
  4197. Status = STATUS_INVALID_PARAMETER;
  4198. goto Exit;
  4199. }
  4200. if (!RTL_SOFT_VERIFY(Flags == 0)) {
  4201. Status = STATUS_INVALID_PARAMETER;
  4202. goto Exit;
  4203. }
  4204. switch (SizeOfStruct)
  4205. {
  4206. default:
  4207. Status = STATUS_INVALID_PARAMETER;
  4208. goto Exit;
  4209. case sizeof(*UnicodeString):
  4210. UnicodeString = UnicodeStringOrUnicodeStringBuffer;
  4211. break;
  4212. case sizeof(*UnicodeStringBuffer):
  4213. UnicodeStringBuffer = UnicodeStringOrUnicodeStringBuffer;
  4214. UnicodeString = &UnicodeStringBuffer->String;
  4215. break;
  4216. }
  4217. Status = (*LengthFunction)(Flags, UnicodeString, &Length);
  4218. if (!NT_SUCCESS(Status)) {
  4219. goto Exit;
  4220. }
  4221. UnicodeString->Length = (USHORT)(Length * sizeof(UnicodeString->Buffer[0]));
  4222. if (UnicodeStringBuffer != NULL) {
  4223. RTL_NUL_TERMINATE_STRING(UnicodeString);
  4224. }
  4225. Status = STATUS_SUCCESS;
  4226. Exit:
  4227. KdPrintEx((
  4228. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  4229. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, UnicodeString, Status));
  4230. return Status;
  4231. }
  4232. NTSTATUS
  4233. NTAPI
  4234. RtlNtPathNameToDosPathName(
  4235. IN ULONG Flags,
  4236. IN OUT PRTL_UNICODE_STRING_BUFFER Path,
  4237. OUT ULONG* Disposition OPTIONAL,
  4238. IN OUT PWSTR* FilePart OPTIONAL
  4239. )
  4240. {
  4241. NTSTATUS Status = STATUS_SUCCESS;
  4242. SIZE_T NtFilePartOffset = 0;
  4243. SIZE_T DosFilePartOffset = 0;
  4244. BOOLEAN Unc = FALSE;
  4245. const static UNICODE_STRING DosUncPrefix = RTL_CONSTANT_STRING(L"\\\\");
  4246. PCUNICODE_STRING NtPrefix = NULL;
  4247. PCUNICODE_STRING DosPrefix = NULL;
  4248. RTL_STRING_LENGTH_TYPE Cch = 0;
  4249. if (ARGUMENT_PRESENT(Disposition)) {
  4250. *Disposition = 0;
  4251. }
  4252. if ( !RTL_SOFT_VERIFY(Path != NULL)
  4253. || !RTL_SOFT_VERIFY(Flags == 0)
  4254. ) {
  4255. Status = STATUS_INVALID_PARAMETER;
  4256. goto Exit;
  4257. }
  4258. if (ARGUMENT_PRESENT(FilePart) && *FilePart != NULL) {
  4259. NtFilePartOffset = *FilePart - Path->String.Buffer;
  4260. if (!RTL_SOFT_VERIFY(NtFilePartOffset < RTL_STRING_GET_LENGTH_CHARS(&Path->String))
  4261. ) {
  4262. Status = STATUS_INVALID_PARAMETER;
  4263. goto Exit;
  4264. }
  4265. }
  4266. if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesUncPrefix), &Path->String, TRUE)
  4267. ) {
  4268. NtPrefix = &RtlpDosDevicesUncPrefix;
  4269. DosPrefix = &DosUncPrefix;
  4270. if (ARGUMENT_PRESENT(Disposition)) {
  4271. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC;
  4272. }
  4273. }
  4274. else if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), &Path->String, TRUE)
  4275. ) {
  4276. NtPrefix = &RtlpDosDevicesPrefix;
  4277. DosPrefix = &RtlpEmptyString;
  4278. if (ARGUMENT_PRESENT(Disposition)) {
  4279. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE;
  4280. }
  4281. }
  4282. else {
  4283. //
  4284. // It is not recognizably an Nt path produced by RtlDosPathNameToNtPathName_U.
  4285. //
  4286. if (ARGUMENT_PRESENT(Disposition)) {
  4287. RTL_PATH_TYPE PathType = RtlDetermineDosPathNameType_Ustr(&Path->String);
  4288. switch (PathType) {
  4289. case RtlPathTypeUnknown:
  4290. case RtlPathTypeRooted: // NT paths are identified as this
  4291. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
  4292. break;
  4293. //
  4294. // "already" dospaths, but not gotten from this function, let's
  4295. // give a less good disposition
  4296. //
  4297. case RtlPathTypeDriveRelative:
  4298. case RtlPathTypeRelative:
  4299. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
  4300. break;
  4301. // these are pretty clearly dospaths already
  4302. case RtlPathTypeUncAbsolute:
  4303. case RtlPathTypeDriveAbsolute:
  4304. case RtlPathTypeLocalDevice: // "\\?\" or "\\.\" or "\\?\blah" or "\\.\blah"
  4305. case RtlPathTypeRootLocalDevice: // "\\?" or "\\."
  4306. *Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS;
  4307. break;
  4308. }
  4309. }
  4310. goto Exit;
  4311. }
  4312. Cch =
  4313. RTL_STRING_GET_LENGTH_CHARS(&Path->String)
  4314. + RTL_STRING_GET_LENGTH_CHARS(DosPrefix)
  4315. - RTL_STRING_GET_LENGTH_CHARS(NtPrefix);
  4316. Status =
  4317. RtlEnsureUnicodeStringBufferSizeChars(Path, Cch);
  4318. if (!NT_SUCCESS(Status)) {
  4319. goto Exit;
  4320. }
  4321. //
  4322. // overlapping buffer shuffle...careful.
  4323. //
  4324. RtlMoveMemory(
  4325. Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(DosPrefix),
  4326. Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(NtPrefix),
  4327. Path->String.Length - NtPrefix->Length
  4328. );
  4329. RtlMoveMemory(
  4330. Path->String.Buffer,
  4331. DosPrefix->Buffer,
  4332. DosPrefix->Length
  4333. );
  4334. Path->String.Length = Cch * sizeof(Path->String.Buffer[0]);
  4335. RTL_NUL_TERMINATE_STRING(&Path->String);
  4336. if (NtFilePartOffset != 0) {
  4337. // review/test..
  4338. *FilePart = Path->String.Buffer + (NtFilePartOffset - RTL_STRING_GET_LENGTH_CHARS(NtPrefix) + RTL_STRING_GET_LENGTH_CHARS(DosPrefix));
  4339. }
  4340. Status = STATUS_SUCCESS;
  4341. Exit:
  4342. KdPrintEx((
  4343. DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
  4344. "%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status));
  4345. return Status;
  4346. }