Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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