1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/path.c
5 * PURPOSE: Path and current directory functions
6 * PROGRAMMERS: Wine team
7 * Thomas Weidenmueller
8 * Gunnar Dalsnes
9 * Alex Ionescu (alex.ionescu@reactos.org)
10 * Pierre Schweitzer (pierre@reactos.org)
11 */
12
13 /* INCLUDES *******************************************************************/
14
15 #include <rtl.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 /* DEFINITONS and MACROS ******************************************************/
21
22 #define MAX_PFX_SIZE 16
23
24 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
25
26 #define RTL_CURDIR_IS_REMOVABLE 0x1
27 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2
28 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
29 C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
30
31
32 /* GLOBALS ********************************************************************/
33
34 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
35
36 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
37 const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\");
38 const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON");
39 const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\");
40
41 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");
42 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");
43 const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN");
44 const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");
45 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
46 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
47
48 const UNICODE_STRING RtlpDoubleSlashPrefix = RTL_CONSTANT_STRING(L"\\\\");
49
50 static const UNICODE_STRING RtlpDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
51 static const UNICODE_STRING RtlpDotLocal = RTL_CONSTANT_STRING(L".Local\\");
52 static const UNICODE_STRING RtlpPathDividers = RTL_CONSTANT_STRING(L"\\/");
53
54
55 PRTLP_CURDIR_REF RtlpCurDirRef;
56
57 /* PRIVATE FUNCTIONS **********************************************************/
58
59 RTL_PATH_TYPE
60 NTAPI
RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)61 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
62 {
63 PWCHAR Path;
64 ULONG Chars;
65
66 Path = PathString->Buffer;
67 Chars = PathString->Length / sizeof(WCHAR);
68
69 /* Return if there are no characters */
70 if (!Chars) return RtlPathTypeRelative;
71
72 /*
73 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
74 * actually check for the path length before touching the characters
75 */
76 if (IS_PATH_SEPARATOR(Path[0]))
77 {
78 if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */
79 if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */
80 if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
81 if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
82 return RtlPathTypeRootLocalDevice; /* \\. or \\? */
83 }
84 else
85 {
86 if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
87 if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */
88 return RtlPathTypeDriveAbsolute; /* x:\ */
89 }
90 }
91
92 ULONG
93 NTAPI
RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)94 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
95 {
96 UNICODE_STRING PathCopy;
97 PWCHAR Start, End;
98 USHORT PathChars, ColonCount = 0;
99 USHORT ReturnOffset = 0, ReturnLength, OriginalLength;
100 WCHAR c;
101
102 /* Validate the input */
103 if (!PathString) return 0;
104
105 /* Check what type of path this is */
106 switch (RtlDetermineDosPathNameType_Ustr(PathString))
107 {
108 /* Fail for UNC or unknown paths */
109 case RtlPathTypeUnknown:
110 case RtlPathTypeUncAbsolute:
111 return 0;
112
113 /* Make special check for the CON device */
114 case RtlPathTypeLocalDevice:
115 if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE))
116 {
117 /* This should return 0x80006 */
118 return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length);
119 }
120 return 0;
121
122 default:
123 break;
124 }
125
126 /* Make a copy of the string */
127 PathCopy = *PathString;
128 OriginalLength = PathString->Length;
129
130 /* Return if there's no characters */
131 PathChars = PathCopy.Length / sizeof(WCHAR);
132 if (!PathChars) return 0;
133
134 /* Check for drive path and truncate */
135 if (PathCopy.Buffer[PathChars - 1] == L':')
136 {
137 /* Fixup the lengths */
138 PathCopy.Length -= sizeof(WCHAR);
139 if (!--PathChars) return 0;
140
141 /* Remember this for later */
142 ColonCount = 1;
143 }
144
145 /* Check for extension or space, and truncate */
146 do
147 {
148 /* Stop if we hit something else than a space or period */
149 c = PathCopy.Buffer[PathChars - 1];
150 if ((c != L'.') && (c != L' ')) break;
151
152 /* Fixup the lengths */
153 PathCopy.Length -= sizeof(WCHAR);
154
155 /* Remember this for later */
156 ColonCount++;
157 } while (--PathChars);
158
159 /* Anything still left? */
160 if (PathChars)
161 {
162 /* Loop from the end */
163 for (End = &PathCopy.Buffer[PathChars - 1];
164 End >= PathCopy.Buffer;
165 --End)
166 {
167 /* Check if the character is a path or drive separator */
168 c = *End;
169 if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1)))
170 {
171 /* Get the next lower case character */
172 End++;
173 c = RtlpDowncaseUnicodeChar(*End);
174
175 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
176 if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) &&
177 ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n')))
178 {
179 /* Calculate the offset */
180 ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
181
182 /* Build the final string */
183 PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
184 PathCopy.Buffer = End;
185
186 /* Save new amount of chars in the path */
187 PathChars = PathCopy.Length / sizeof(WCHAR);
188
189 break;
190 }
191 else
192 {
193 return 0;
194 }
195 }
196 }
197
198 /* Get the next lower case character and check if it's a DOS device */
199 c = RtlpDowncaseUnicodeChar(*PathCopy.Buffer);
200 if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n'))
201 {
202 /* Not LPT, COM, PRN, AUX, or NUL */
203 return 0;
204 }
205 }
206
207 /* Now skip past any extra extension or drive letter characters */
208 Start = PathCopy.Buffer;
209 End = &Start[PathChars];
210 while (Start < End)
211 {
212 c = *Start;
213 if ((c == L'.') || (c == L':')) break;
214 Start++;
215 }
216
217 /* And then go backwards to get rid of spaces */
218 while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start;
219
220 /* Finally see how many characters are left, and that's our size */
221 PathChars = (USHORT)(Start - PathCopy.Buffer);
222 PathCopy.Length = PathChars * sizeof(WCHAR);
223
224 /* Check if this is a COM or LPT port, which has a digit after it */
225 if ((PathChars == 4) &&
226 (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0')))
227 {
228 /* Don't compare the number part, just check for LPT or COM */
229 PathCopy.Length -= sizeof(WCHAR);
230 if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) ||
231 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
232 {
233 /* Found it */
234 ReturnLength = sizeof(L"COM1") - sizeof(WCHAR);
235 return MAKELONG(ReturnLength, ReturnOffset);
236 }
237 }
238 else if ((PathChars == 3) &&
239 ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) ||
240 (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) ||
241 (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) ||
242 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
243 {
244 /* Otherwise this was something like AUX, NUL, PRN, or CON */
245 ReturnLength = sizeof(L"AUX") - sizeof(WCHAR);
246 return MAKELONG(ReturnLength, ReturnOffset);
247 }
248
249 /* Otherwise, this is not a valid DOS device */
250 return 0;
251 }
252
253 NTSTATUS
254 NTAPI
RtlpCheckDeviceName(IN PUNICODE_STRING FileName,IN ULONG Length,OUT PBOOLEAN NameInvalid)255 RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
256 IN ULONG Length,
257 OUT PBOOLEAN NameInvalid)
258 {
259 PWCHAR Buffer;
260 NTSTATUS Status;
261
262 /* Allocate a large enough buffer */
263 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length);
264 if (Buffer)
265 {
266 /* Assume failure */
267 *NameInvalid = TRUE;
268
269 /* Copy the filename */
270 RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length);
271
272 /* And add a dot at the end */
273 Buffer[Length / sizeof(WCHAR)] = L'.';
274 Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
275
276 /* Check if the file exists or not */
277 *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE;
278
279 /* Get rid of the buffer now */
280 Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
281 }
282 else
283 {
284 /* Assume the name is ok, but fail the call */
285 *NameInvalid = FALSE;
286 Status = STATUS_NO_MEMORY;
287 }
288
289 /* Return the status */
290 return Status;
291 }
292
293
294
295 /******************************************************************
296 * RtlpCollapsePath (from WINE)
297 *
298 * Helper for RtlGetFullPathName_U
299 *
300 * 1) Converts slashes into backslashes and gets rid of duplicated ones;
301 * 2) Gets rid of . and .. components in the path.
302 *
303 * Returns the full path length without its terminating NULL character.
304 */
305 static ULONG
RtlpCollapsePath(PWSTR Path,ULONG mark,BOOLEAN SkipTrailingPathSeparators)306 RtlpCollapsePath(PWSTR Path, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark, BOOLEAN SkipTrailingPathSeparators)
307 {
308 PWSTR p, next;
309
310 // FIXME: Do not suppose NULL-terminated strings!!
311
312 SIZE_T PathLength = wcslen(Path);
313 PWSTR EndBuffer = Path + PathLength; // Path + PathBufferSize / sizeof(WCHAR);
314 PWSTR EndPath;
315
316 /* Convert slashes into backslashes */
317 for (p = Path; *p; p++)
318 {
319 if (*p == L'/') *p = L'\\';
320 }
321
322 /* Collapse duplicate backslashes */
323 next = Path + max( 1, mark );
324 for (p = next; *p; p++)
325 {
326 if (*p != L'\\' || next[-1] != L'\\') *next++ = *p;
327 }
328 *next = UNICODE_NULL;
329 EndPath = next;
330
331 p = Path + mark;
332 while (*p)
333 {
334 if (*p == L'.')
335 {
336 switch (p[1])
337 {
338 case UNICODE_NULL: /* final . */
339 if (p > Path + mark) p--;
340 *p = UNICODE_NULL;
341 EndPath = p;
342 continue;
343
344 case L'\\': /* .\ component */
345 next = p + 2;
346 // ASSERT(EndPath - next == wcslen(next));
347 RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
348 EndPath -= (next - p);
349 continue;
350
351 case L'.':
352 if (p[2] == L'\\') /* ..\ component */
353 {
354 next = p + 3;
355 if (p > Path + mark)
356 {
357 p--;
358 while (p > Path + mark && p[-1] != L'\\') p--;
359 }
360 // ASSERT(EndPath - next == wcslen(next));
361 RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
362 EndPath -= (next - p);
363 continue;
364 }
365 else if (p[2] == UNICODE_NULL) /* final .. */
366 {
367 if (p > Path + mark)
368 {
369 p--;
370 while (p > Path + mark && p[-1] != L'\\') p--;
371 if (p > Path + mark) p--;
372 }
373 *p = UNICODE_NULL;
374 EndPath = p;
375 continue;
376 }
377 break;
378 }
379 }
380
381 /* Skip to the next component */
382 while (*p && *p != L'\\') p++;
383 if (*p == L'\\')
384 {
385 /* Remove last dot in previous dir name */
386 if (p > Path + mark && p[-1] == L'.')
387 {
388 // ASSERT(EndPath - p == wcslen(p));
389 RtlMoveMemory(p - 1, p, (EndPath - p + 1) * sizeof(WCHAR));
390 EndPath--;
391 }
392 else
393 {
394 p++;
395 }
396 }
397 }
398
399 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
400 if (SkipTrailingPathSeparators)
401 {
402 while (p > Path + mark && IS_PATH_SEPARATOR(p[-1])) p--;
403 }
404
405 /* Remove trailing spaces and dots (for all the path) */
406 while (p > Path && (p[-1] == L' ' || p[-1] == L'.')) p--;
407
408 /*
409 * Zero-out the discarded buffer zone, starting just after
410 * the path string and going up to the end of the buffer.
411 * It also NULL-terminate the path string.
412 */
413 ASSERT(EndBuffer >= p);
414 RtlZeroMemory(p, (EndBuffer - p + 1) * sizeof(WCHAR));
415
416 /* Return the real path length */
417 PathLength = (p - Path);
418 // ASSERT(PathLength == wcslen(Path));
419 return (PathLength * sizeof(WCHAR));
420 }
421
422 /******************************************************************
423 * RtlpSkipUNCPrefix (from WINE)
424 *
425 * Helper for RtlGetFullPathName_U
426 *
427 * Skips the \\share\dir part of a file name and returns the new position
428 * (which can point on the last backslash of "dir\").
429 */
430 static SIZE_T
RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)431 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
432 {
433 PCWSTR UncPath = FileNameBuffer + 2;
434 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
435
436 while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* share name */
437 while (IS_PATH_SEPARATOR(*UncPath)) UncPath++;
438 while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* dir name */
439 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
440
441 return (UncPath - FileNameBuffer);
442 }
443
444 NTSTATUS
445 NTAPI
RtlpApplyLengthFunction(IN ULONG Flags,IN ULONG Type,IN PVOID UnicodeStringOrUnicodeStringBuffer,IN NTSTATUS (NTAPI * LengthFunction)(ULONG,PUNICODE_STRING,PULONG))446 RtlpApplyLengthFunction(IN ULONG Flags,
447 IN ULONG Type,
448 IN PVOID UnicodeStringOrUnicodeStringBuffer,
449 IN NTSTATUS(NTAPI* LengthFunction)(ULONG, PUNICODE_STRING, PULONG))
450 {
451 NTSTATUS Status;
452 PUNICODE_STRING String;
453 ULONG Length;
454
455 if (Flags || UnicodeStringOrUnicodeStringBuffer == NULL || LengthFunction == NULL)
456 {
457 DPRINT1("ERROR: Flags=0x%x, UnicodeStringOrUnicodeStringBuffer=%p, LengthFunction=%p\n",
458 Flags, UnicodeStringOrUnicodeStringBuffer, LengthFunction);
459 return STATUS_INVALID_PARAMETER;
460 }
461
462 if (Type == sizeof(UNICODE_STRING))
463 {
464 String = (PUNICODE_STRING)UnicodeStringOrUnicodeStringBuffer;
465 }
466 else if (Type == sizeof(RTL_UNICODE_STRING_BUFFER))
467 {
468 String = &((PRTL_UNICODE_STRING_BUFFER)UnicodeStringOrUnicodeStringBuffer)->String;
469 }
470 else
471 {
472 DPRINT1("ERROR: Type = %u\n", Type);
473 return STATUS_INVALID_PARAMETER;
474 }
475
476 Length = 0;
477 Status = LengthFunction(0, String, &Length);
478 if (!NT_SUCCESS(Status))
479 return Status;
480
481 if (Length > UNICODE_STRING_MAX_CHARS)
482 return STATUS_NAME_TOO_LONG;
483
484 String->Length = (USHORT)(Length * sizeof(WCHAR));
485
486 if (Type == sizeof(RTL_UNICODE_STRING_BUFFER))
487 {
488 String->Buffer[Length] = UNICODE_NULL;
489 }
490
491 return STATUS_SUCCESS;
492 }
493
494 NTSTATUS
495 NTAPI
RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags,IN PCUNICODE_STRING Path,OUT PULONG LengthOut)496 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags,
497 IN PCUNICODE_STRING Path,
498 OUT PULONG LengthOut)
499 {
500 static const UNICODE_STRING PathDividers = RTL_CONSTANT_STRING(L"\\/");
501 USHORT Position;
502 RTL_PATH_TYPE PathType;
503
504 /* All failure paths have this in common, so simplify code */
505 if (LengthOut)
506 *LengthOut = 0;
507
508 if (Flags || !Path || !LengthOut)
509 {
510 return STATUS_INVALID_PARAMETER;
511 }
512
513 if ((Path->Length / sizeof(WCHAR)) == 0)
514 {
515 /* Nothing to do here */
516 return STATUS_SUCCESS;
517 }
518
519
520 PathType = RtlDetermineDosPathNameType_Ustr(Path);
521 switch (PathType)
522 {
523 case RtlPathTypeLocalDevice:
524 // Handle \\\\?\\C:\\ with the last ':' or '\\' missing:
525 if (Path->Length / sizeof(WCHAR) < 7 ||
526 Path->Buffer[5] != ':' ||
527 !IS_PATH_SEPARATOR(Path->Buffer[6]))
528 {
529 return STATUS_INVALID_PARAMETER;
530 }
531 break;
532 case RtlPathTypeRooted:
533 // "\\??\\"
534 break;
535 case RtlPathTypeUncAbsolute:
536 // "\\\\"
537 break;
538 case RtlPathTypeDriveAbsolute:
539 // "C:\\"
540 break;
541 default:
542 return STATUS_INVALID_PARAMETER;
543 }
544
545 /* Find the last path separator */
546 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, Path, &PathDividers, &Position)))
547 Position = 0;
548
549 /* Is it the last char of the string? */
550 if (Position && Position + sizeof(WCHAR) == Path->Length)
551 {
552 UNICODE_STRING Tmp = *Path;
553 Tmp.Length = Position;
554
555 /* Keep walking path separators to eliminate multiple next to eachother */
556 while (Tmp.Length > sizeof(WCHAR) && IS_PATH_SEPARATOR(Tmp.Buffer[Tmp.Length / sizeof(WCHAR)]))
557 Tmp.Length -= sizeof(WCHAR);
558
559 /* Find the previous occurence */
560 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &Tmp, &PathDividers, &Position)))
561 Position = 0;
562 }
563
564 /* Simplify code by working in chars instead of bytes */
565 if (Position)
566 Position /= sizeof(WCHAR);
567
568 if (Position)
569 {
570 // Keep walking path separators to eliminate multiple next to eachother, but ensure we leave one in place!
571 while (Position > 1 && IS_PATH_SEPARATOR(Path->Buffer[Position - 1]))
572 Position--;
573 }
574
575 if (Position > 0)
576 {
577 /* Return a length, not an index */
578 *LengthOut = Position + 1;
579 }
580
581 return STATUS_SUCCESS;
582 }
583
584 NTSTATUS
585 NTAPI
RtlComputePrivatizedDllName_U(_In_ PUNICODE_STRING DllName,_Inout_ PUNICODE_STRING RealName,_Inout_ PUNICODE_STRING LocalName)586 RtlComputePrivatizedDllName_U(
587 _In_ PUNICODE_STRING DllName,
588 _Inout_ PUNICODE_STRING RealName,
589 _Inout_ PUNICODE_STRING LocalName)
590 {
591 static const UNICODE_STRING ExtensionChar = RTL_CONSTANT_STRING(L".");
592
593 USHORT Position;
594 UNICODE_STRING ImagePathName, DllNameOnly, CopyRealName, CopyLocalName;
595 BOOLEAN HasExtension;
596 ULONG RequiredSize;
597 NTSTATUS Status;
598 C_ASSERT(sizeof(UNICODE_NULL) == sizeof(WCHAR));
599
600 CopyRealName = *RealName;
601 CopyLocalName = *LocalName;
602
603
604 /* Get the image path */
605 ImagePathName = RtlGetCurrentPeb()->ProcessParameters->ImagePathName;
606
607 /* Check if it's not normalized */
608 if (!(RtlGetCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_NORMALIZED))
609 {
610 /* Normalize it */
611 ImagePathName.Buffer = (PWSTR)((ULONG_PTR)ImagePathName.Buffer + (ULONG_PTR)RtlGetCurrentPeb()->ProcessParameters);
612 }
613
614
615 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
616 DllName, &RtlpPathDividers, &Position)))
617 {
618 DllNameOnly = *DllName;
619 }
620 else
621 {
622 /* Just keep the dll name, ignore path components */
623 Position += sizeof(WCHAR);
624 DllNameOnly.Buffer = DllName->Buffer + Position / sizeof(WCHAR);
625 DllNameOnly.Length = DllName->Length - Position;
626 DllNameOnly.MaximumLength = DllName->MaximumLength - Position;
627 }
628
629 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
630 &DllNameOnly, &ExtensionChar, &Position)))
631 {
632 Position = 0;
633 }
634
635 HasExtension = Position > 1;
636
637 /* First we create the c:\path\processname.exe.Local\something.dll path */
638 RequiredSize = ImagePathName.Length + RtlpDotLocal.Length + DllNameOnly.Length +
639 (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
640
641 /* This is not going to work out */
642 if (RequiredSize > UNICODE_STRING_MAX_BYTES)
643 return STATUS_NAME_TOO_LONG;
644
645 /* We need something extra */
646 if (RequiredSize > CopyLocalName.MaximumLength)
647 {
648 CopyLocalName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR);
649 if (CopyLocalName.Buffer == NULL)
650 return STATUS_NO_MEMORY;
651 CopyLocalName.MaximumLength = RequiredSize;
652 }
653 /* Now build the entire path */
654 CopyLocalName.Length = 0;
655 Status = RtlAppendUnicodeStringToString(&CopyLocalName, &ImagePathName);
656 ASSERT(NT_SUCCESS(Status));
657 if (NT_SUCCESS(Status))
658 {
659 Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDotLocal);
660 ASSERT(NT_SUCCESS(Status));
661 }
662 if (NT_SUCCESS(Status))
663 {
664 Status = RtlAppendUnicodeStringToString(&CopyLocalName, &DllNameOnly);
665 ASSERT(NT_SUCCESS(Status));
666 }
667 /* Do we need to append an extension? */
668 if (NT_SUCCESS(Status) && !HasExtension)
669 {
670 Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDefaultExtension);
671 ASSERT(NT_SUCCESS(Status));
672 }
673
674 if (NT_SUCCESS(Status))
675 {
676 /* then we create the c:\path\something.dll path */
677 if (NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
678 &ImagePathName, &RtlpPathDividers, &Position)))
679 {
680 ImagePathName.Length = Position + sizeof(WCHAR);
681 }
682
683 RequiredSize = ImagePathName.Length + DllNameOnly.Length +
684 (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
685
686 if (RequiredSize >= UNICODE_STRING_MAX_BYTES)
687 {
688 Status = STATUS_NAME_TOO_LONG;
689 }
690 else
691 {
692 if (RequiredSize > CopyRealName.MaximumLength)
693 {
694 CopyRealName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR);
695 if (CopyRealName.Buffer == NULL)
696 Status = STATUS_NO_MEMORY;
697 CopyRealName.MaximumLength = RequiredSize;
698 }
699 CopyRealName.Length = 0;
700 if (NT_SUCCESS(Status))
701 {
702 Status = RtlAppendUnicodeStringToString(&CopyRealName, &ImagePathName);
703 ASSERT(NT_SUCCESS(Status));
704 }
705 if (NT_SUCCESS(Status))
706 {
707 Status = RtlAppendUnicodeStringToString(&CopyRealName, &DllNameOnly);
708 ASSERT(NT_SUCCESS(Status));
709 }
710 if (NT_SUCCESS(Status) && !HasExtension)
711 {
712 Status = RtlAppendUnicodeStringToString(&CopyRealName, &RtlpDefaultExtension);
713 ASSERT(NT_SUCCESS(Status));
714 }
715 }
716 }
717
718 if (!NT_SUCCESS(Status))
719 {
720 if (CopyRealName.Buffer && CopyRealName.Buffer != RealName->Buffer)
721 RtlpFreeStringMemory(CopyRealName.Buffer, TAG_USTR);
722 if (CopyLocalName.Buffer && CopyLocalName.Buffer != LocalName->Buffer)
723 RtlpFreeStringMemory(CopyLocalName.Buffer, TAG_USTR);
724 return Status;
725 }
726
727 *RealName = CopyRealName;
728 *LocalName = CopyLocalName;
729 return STATUS_SUCCESS;
730 }
731
732 ULONG
733 NTAPI
RtlGetFullPathName_Ustr(_In_ PUNICODE_STRING FileName,_In_ ULONG Size,_Out_z_bytecap_ (Size)PWSTR Buffer,_Out_opt_ PCWSTR * ShortName,_Out_opt_ PBOOLEAN InvalidName,_Out_ RTL_PATH_TYPE * PathType)734 RtlGetFullPathName_Ustr(
735 _In_ PUNICODE_STRING FileName,
736 _In_ ULONG Size,
737 _Out_z_bytecap_(Size) PWSTR Buffer,
738 _Out_opt_ PCWSTR *ShortName,
739 _Out_opt_ PBOOLEAN InvalidName,
740 _Out_ RTL_PATH_TYPE *PathType)
741 {
742 NTSTATUS Status;
743 PWCHAR FileNameBuffer;
744 ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength;
745 BOOLEAN SkipTrailingPathSeparators;
746 WCHAR c;
747
748
749 ULONG reqsize = 0;
750 PCWSTR ptr;
751
752 PCUNICODE_STRING CurDirName;
753 UNICODE_STRING EnvVarName, EnvVarValue;
754 WCHAR EnvVarNameBuffer[4];
755
756 ULONG PrefixCut = 0; // Where the path really starts (after the skipped prefix)
757 PWCHAR Prefix = NULL; // pointer to the string to be inserted as the new path prefix
758 ULONG PrefixLength = 0;
759 PWCHAR Source;
760 ULONG SourceLength;
761
762
763 /* For now, assume the name is valid */
764 DPRINT("Filename: %wZ\n", FileName);
765 DPRINT("Size and buffer: %lx %p\n", Size, Buffer);
766 if (InvalidName) *InvalidName = FALSE;
767
768 /* Handle initial path type and failure case */
769 *PathType = RtlPathTypeUnknown;
770 if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
771
772 /* Break filename into component parts */
773 FileNameBuffer = FileName->Buffer;
774 FileNameLength = FileName->Length;
775 FileNameChars = FileNameLength / sizeof(WCHAR);
776
777 /* Kill trailing spaces */
778 c = FileNameBuffer[FileNameChars - 1];
779 while ((FileNameLength != 0) && (c == L' '))
780 {
781 /* Keep going, ignoring the spaces */
782 FileNameLength -= sizeof(WCHAR);
783 if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
784 }
785
786 /* Check if anything is left */
787 if (FileNameLength == 0) return 0;
788
789 /*
790 * Check whether we'll need to skip trailing path separators in the
791 * computed full path name. If the original file name already contained
792 * trailing separators, then we keep them in the full path name. On the
793 * other hand, if the original name didn't contain any trailing separators
794 * then we'll skip it in the full path name.
795 */
796 SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]);
797
798 /* Check if this is a DOS name */
799 DosLength = RtlIsDosDeviceName_Ustr(FileName);
800 DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
801 if (DosLength != 0)
802 {
803 /* Zero out the short name */
804 if (ShortName) *ShortName = NULL;
805
806 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
807 DosLengthOffset = HIWORD(DosLength);
808 DosLength = LOWORD(DosLength);
809
810 /* Do we have a DOS length, and does the caller want validity? */
811 if (InvalidName && (DosLengthOffset != 0))
812 {
813 /* Do the check */
814 Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
815
816 /* If the check failed, or the name is invalid, fail here */
817 if (!NT_SUCCESS(Status)) return 0;
818 if (*InvalidName) return 0;
819 }
820
821 /* Add the size of the device root and check if it fits in the size */
822 FullLength = DosLength + DeviceRootString.Length;
823 if (FullLength < Size)
824 {
825 /* Add the device string */
826 RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length);
827
828 /* Now add the DOS device name */
829 RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length,
830 (PCHAR)FileNameBuffer + DosLengthOffset,
831 DosLength);
832
833 /* Null terminate */
834 *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL;
835 return FullLength;
836 }
837
838 /* Otherwise, there's no space, so return the buffer size needed */
839 if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0;
840 return FullLength + sizeof(UNICODE_NULL);
841 }
842
843 /* Zero-out the destination buffer. FileName must be different from Buffer */
844 RtlZeroMemory(Buffer, Size);
845
846 /* Get the path type */
847 *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
848
849
850
851 /**********************************************
852 ** CODE REWRITING IS HAPPENING THERE **
853 **********************************************/
854 Source = FileNameBuffer;
855 SourceLength = FileNameLength;
856 EnvVarValue.Buffer = NULL;
857
858 /* Lock the PEB to get the current directory */
859 RtlAcquirePebLock();
860 CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
861
862 switch (*PathType)
863 {
864 case RtlPathTypeUncAbsolute: /* \\foo */
865 {
866 PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer);
867 break;
868 }
869
870 case RtlPathTypeLocalDevice: /* \\.\foo */
871 {
872 PrefixCut = 4;
873 break;
874 }
875
876 case RtlPathTypeDriveAbsolute: /* c:\foo */
877 {
878 ASSERT(FileNameBuffer[1] == L':');
879 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
880
881 // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
882 Prefix = FileNameBuffer;
883 PrefixLength = 3 * sizeof(WCHAR);
884 Source += 3;
885 SourceLength -= 3 * sizeof(WCHAR);
886
887 PrefixCut = 3;
888 break;
889 }
890
891 case RtlPathTypeDriveRelative: /* c:foo */
892 {
893 WCHAR CurDrive, NewDrive;
894
895 Source += 2;
896 SourceLength -= 2 * sizeof(WCHAR);
897
898 CurDrive = RtlpUpcaseUnicodeChar(CurDirName->Buffer[0]);
899 NewDrive = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
900
901 if ((NewDrive != CurDrive) || CurDirName->Buffer[1] != L':')
902 {
903 EnvVarNameBuffer[0] = L'=';
904 EnvVarNameBuffer[1] = NewDrive;
905 EnvVarNameBuffer[2] = L':';
906 EnvVarNameBuffer[3] = UNICODE_NULL;
907
908 EnvVarName.Length = 3 * sizeof(WCHAR);
909 EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR);
910 EnvVarName.Buffer = EnvVarNameBuffer;
911
912 // FIXME: Is it possible to use the user-given buffer ?
913 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
914 EnvVarValue.Length = 0;
915 EnvVarValue.MaximumLength = (USHORT)Size;
916 EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
917 if (EnvVarValue.Buffer == NULL)
918 {
919 Prefix = NULL;
920 PrefixLength = 0;
921 goto Quit;
922 }
923
924 Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue);
925 switch (Status)
926 {
927 case STATUS_SUCCESS:
928 /*
929 * (From Wine)
930 * FIXME: Win2k seems to check that the environment
931 * variable actually points to an existing directory.
932 * If not, root of the drive is used (this seems also
933 * to be the only place in RtlGetFullPathName that the
934 * existence of a part of a path is checked).
935 */
936 EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\';
937 Prefix = EnvVarValue.Buffer;
938 PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */
939 break;
940
941 case STATUS_BUFFER_TOO_SMALL:
942 reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL);
943 goto Quit;
944
945 default:
946 DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName, Status);
947
948 EnvVarNameBuffer[0] = NewDrive;
949 EnvVarNameBuffer[1] = L':';
950 EnvVarNameBuffer[2] = L'\\';
951 EnvVarNameBuffer[3] = UNICODE_NULL;
952 Prefix = EnvVarNameBuffer;
953 PrefixLength = 3 * sizeof(WCHAR);
954
955 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
956 EnvVarValue.Buffer = NULL;
957 break;
958 }
959 PrefixCut = 3;
960 break;
961 }
962 /* Fall through */
963 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
964 }
965
966 case RtlPathTypeRelative: /* foo */
967 {
968 Prefix = CurDirName->Buffer;
969 PrefixLength = CurDirName->Length;
970 if (CurDirName->Buffer[1] != L':')
971 {
972 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
973 }
974 else
975 {
976 PrefixCut = 3;
977 }
978 break;
979 }
980
981 case RtlPathTypeRooted: /* \xxx */
982 {
983 if (CurDirName->Buffer[1] == L':')
984 {
985 // The path starts with "C:\"
986 ASSERT(CurDirName->Buffer[1] == L':');
987 ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2]));
988
989 Prefix = CurDirName->Buffer;
990 PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\"
991
992 PrefixCut = 3; // Source index location incremented of + 3
993 }
994 else
995 {
996 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
997 PrefixLength = PrefixCut * sizeof(WCHAR);
998 Prefix = CurDirName->Buffer;
999 }
1000 break;
1001 }
1002
1003 case RtlPathTypeRootLocalDevice: /* \\. */
1004 {
1005 Prefix = DeviceRootString.Buffer;
1006 PrefixLength = DeviceRootString.Length;
1007 Source += 3;
1008 SourceLength -= 3 * sizeof(WCHAR);
1009
1010 PrefixCut = 4;
1011 break;
1012 }
1013
1014 case RtlPathTypeUnknown:
1015 goto Quit;
1016 }
1017
1018 /* Do we have enough space for storing the full path? */
1019 reqsize = PrefixLength;
1020 if (reqsize + SourceLength + sizeof(WCHAR) > Size)
1021 {
1022 /* Not enough space, return needed size (including terminating '\0') */
1023 reqsize += SourceLength + sizeof(WCHAR);
1024 goto Quit;
1025 }
1026
1027 /*
1028 * Build the full path
1029 */
1030 /* Copy the path's prefix */
1031 if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength);
1032 /* Copy the remaining part of the path */
1033 RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
1034
1035 /* Some cleanup */
1036 Prefix = NULL;
1037 if (EnvVarValue.Buffer)
1038 {
1039 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
1040 EnvVarValue.Buffer = NULL;
1041 }
1042
1043 /*
1044 * Finally, put the path in canonical form (remove redundant . and ..,
1045 * (back)slashes...) and retrieve the length of the full path name
1046 * (without its terminating null character) (in chars).
1047 */
1048 reqsize = RtlpCollapsePath(Buffer, /* Size, reqsize, */ PrefixCut, SkipTrailingPathSeparators);
1049
1050 /* Find the file part, which is present after the last path separator */
1051 if (ShortName)
1052 {
1053 ptr = wcsrchr(Buffer, L'\\');
1054 if (ptr) ++ptr; // Skip it
1055
1056 /*
1057 * For UNC paths, the file part is after the \\share\dir part of the path.
1058 */
1059 PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3);
1060
1061 if (ptr && *ptr && (ptr >= Buffer + PrefixCut))
1062 {
1063 *ShortName = ptr;
1064 }
1065 else
1066 {
1067 /* Zero-out the short name */
1068 *ShortName = NULL;
1069 }
1070 }
1071
1072 Quit:
1073 /* Release PEB lock */
1074 RtlReleasePebLock();
1075
1076 return reqsize;
1077 }
1078
1079 NTSTATUS
1080 NTAPI
RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,OUT PUNICODE_STRING NtPath,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)1081 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
1082 OUT PUNICODE_STRING NtPath,
1083 OUT PCWSTR *PartName,
1084 OUT PRTL_RELATIVE_NAME_U RelativeName)
1085 {
1086 ULONG DosLength;
1087 PWSTR NewBuffer, p;
1088
1089 /* Validate the input */
1090 if (!DosPath) return STATUS_OBJECT_NAME_INVALID;
1091
1092 /* Validate the DOS length */
1093 DosLength = DosPath->Length;
1094 if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
1095
1096 /* Make space for the new path */
1097 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
1098 0,
1099 DosLength + sizeof(UNICODE_NULL));
1100 if (!NewBuffer) return STATUS_NO_MEMORY;
1101
1102 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
1103 RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length);
1104 RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length,
1105 DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR),
1106 DosPath->Length - RtlpDosDevicesPrefix.Length);
1107 NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL;
1108
1109 /* Did the caller send a relative name? */
1110 if (RelativeName)
1111 {
1112 /* Zero initialize it */
1113 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
1114 RelativeName->ContainingDirectory = NULL;
1115 RelativeName->CurDirRef = 0;
1116 }
1117
1118 /* Did the caller request a partial name? */
1119 if (PartName)
1120 {
1121 /* Loop from the back until we find a path separator */
1122 p = &NewBuffer[DosLength / sizeof(WCHAR)];
1123 while (--p > NewBuffer)
1124 {
1125 /* We found a path separator, move past it */
1126 if (*p == OBJ_NAME_PATH_SEPARATOR)
1127 {
1128 ++p;
1129 break;
1130 }
1131 }
1132
1133 /* Check whether a separator was found and if something remains */
1134 if ((p > NewBuffer) && *p)
1135 {
1136 /* What follows the path separator is the partial name */
1137 *PartName = p;
1138 }
1139 else
1140 {
1141 /* The path ends with a path separator, no partial name */
1142 *PartName = NULL;
1143 }
1144 }
1145
1146 /* Build the final NT path string */
1147 NtPath->Buffer = NewBuffer;
1148 NtPath->Length = (USHORT)DosLength;
1149 NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
1150 return STATUS_SUCCESS;
1151 }
1152
1153 NTSTATUS
1154 NTAPI
RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,IN PCUNICODE_STRING DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)1155 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
1156 IN PCUNICODE_STRING DosName,
1157 OUT PUNICODE_STRING NtName,
1158 OUT PCWSTR *PartName,
1159 OUT PRTL_RELATIVE_NAME_U RelativeName)
1160 {
1161 WCHAR BigBuffer[MAX_PATH + 1];
1162 PWCHAR PrefixBuffer, NewBuffer, Buffer;
1163 ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
1164 UNICODE_STRING CapturedDosName, PartNameString, FullPath;
1165 BOOLEAN QuickPath;
1166 RTL_PATH_TYPE InputPathType, BufferPathType;
1167 NTSTATUS Status;
1168 BOOLEAN NameInvalid;
1169 PCURDIR CurrentDirectory;
1170
1171 /* Assume MAX_PATH for now */
1172 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
1173 HaveRelative, DosName, NtName, PartName, RelativeName);
1174 MaxLength = sizeof(BigBuffer);
1175
1176 /* Validate the input */
1177 if (!DosName) return STATUS_OBJECT_NAME_INVALID;
1178
1179 /* Capture input string */
1180 CapturedDosName = *DosName;
1181
1182 /* Check for the presence or absence of the NT prefix "\\?\" form */
1183 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
1184 if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) ||
1185 (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) ||
1186 (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) ||
1187 (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) ||
1188 (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3]))
1189 {
1190 /* NT prefix not present */
1191
1192 /* Quick path won't be used */
1193 QuickPath = FALSE;
1194
1195 /* Use the static buffer */
1196 Buffer = BigBuffer;
1197 MaxLength += RtlpDosDevicesUncPrefix.Length;
1198
1199 /* Allocate a buffer to hold the path */
1200 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
1201 DPRINT("MaxLength: %lx\n", MaxLength);
1202 if (!NewBuffer) return STATUS_NO_MEMORY;
1203 }
1204 else
1205 {
1206 /* NT prefix present */
1207
1208 /* Use the optimized path after acquiring the lock */
1209 QuickPath = TRUE;
1210 NewBuffer = NULL;
1211 }
1212
1213 /* Lock the PEB and check if the quick path can be used */
1214 RtlAcquirePebLock();
1215 if (QuickPath)
1216 {
1217 /* Some simple fixups will get us the correct path */
1218 DPRINT("Quick path\n");
1219 Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName,
1220 NtName,
1221 PartName,
1222 RelativeName);
1223
1224 /* Release the lock, we're done here */
1225 RtlReleasePebLock();
1226 return Status;
1227 }
1228
1229 /* Call the main function to get the full path name and length */
1230 PathLength = RtlGetFullPathName_Ustr(&CapturedDosName,
1231 MAX_PATH * sizeof(WCHAR),
1232 Buffer,
1233 PartName,
1234 &NameInvalid,
1235 &InputPathType);
1236 if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR))))
1237 {
1238 /* Invalid name, fail */
1239 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength);
1240 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1241 RtlReleasePebLock();
1242 return STATUS_OBJECT_NAME_INVALID;
1243 }
1244
1245 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
1246 PrefixLength = RtlpDosDevicesPrefix.Length;
1247 PrefixBuffer = RtlpDosDevicesPrefix.Buffer;
1248 PrefixCut = 0;
1249
1250 /* Check where it really is */
1251 BufferPathType = RtlDetermineDosPathNameType_U(Buffer);
1252 DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType);
1253 switch (BufferPathType)
1254 {
1255 /* It's actually a UNC path in \??\UNC\ */
1256 case RtlPathTypeUncAbsolute:
1257 PrefixLength = RtlpDosDevicesUncPrefix.Length;
1258 PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer;
1259 PrefixCut = 2;
1260 break;
1261
1262 case RtlPathTypeLocalDevice:
1263 /* We made a good guess, go with it but skip the \??\ */
1264 PrefixCut = 4;
1265 break;
1266
1267 case RtlPathTypeDriveAbsolute:
1268 case RtlPathTypeDriveRelative:
1269 case RtlPathTypeRooted:
1270 case RtlPathTypeRelative:
1271 /* Our guess was good, roll with it */
1272 break;
1273
1274 /* Nothing else is expected */
1275 default:
1276 ASSERT(FALSE);
1277 }
1278
1279 /* Now copy the prefix and the buffer */
1280 RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
1281 RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
1282 Buffer + PrefixCut,
1283 PathLength - (PrefixCut * sizeof(WCHAR)));
1284
1285 /* Compute the length */
1286 Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR);
1287 LengthChars = Length / sizeof(WCHAR);
1288
1289 /* Setup the actual NT path string and terminate it */
1290 NtName->Buffer = NewBuffer;
1291 NtName->Length = (USHORT)Length;
1292 NtName->MaximumLength = (USHORT)MaxLength;
1293 NewBuffer[LengthChars] = UNICODE_NULL;
1294 DPRINT("New buffer: %S\n", NewBuffer);
1295 DPRINT("NT Name: %wZ\n", NtName);
1296
1297 /* Check if a partial name was requested */
1298 if ((PartName) && (*PartName))
1299 {
1300 /* Convert to Unicode */
1301 Status = RtlInitUnicodeStringEx(&PartNameString, *PartName);
1302 if (NT_SUCCESS(Status))
1303 {
1304 /* Set the partial name */
1305 *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))];
1306 }
1307 else
1308 {
1309 /* Fail */
1310 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1311 RtlReleasePebLock();
1312 return Status;
1313 }
1314 }
1315
1316 /* Check if a relative name was asked for */
1317 if (RelativeName)
1318 {
1319 /* Setup the structure */
1320 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
1321 RelativeName->ContainingDirectory = NULL;
1322 RelativeName->CurDirRef = NULL;
1323
1324 /* Check if the input path itself was relative */
1325 if (InputPathType == RtlPathTypeRelative)
1326 {
1327 /* Get current directory */
1328 CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
1329 if (CurrentDirectory->Handle)
1330 {
1331 Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
1332 if (!NT_SUCCESS(Status))
1333 {
1334 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1335 RtlReleasePebLock();
1336 return Status;
1337 }
1338
1339 /* If current directory is bigger than full path, there's no way */
1340 if (CurrentDirectory->DosPath.Length > FullPath.Length)
1341 {
1342 RtlReleasePebLock();
1343 return Status;
1344 }
1345
1346 /* File is in current directory */
1347 if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE))
1348 {
1349 /* Make relative name string */
1350 RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR));
1351 RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
1352 /* If relative name starts with \, skip it */
1353 if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1354 {
1355 RelativeName->RelativeName.Buffer++;
1356 RelativeName->RelativeName.Length -= sizeof(WCHAR);
1357 }
1358 RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
1359 DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName));
1360
1361 if (!HaveRelative)
1362 {
1363 RelativeName->ContainingDirectory = CurrentDirectory->Handle;
1364 return Status;
1365 }
1366
1367 /* Give back current directory data & reference counter */
1368 RelativeName->CurDirRef = RtlpCurDirRef;
1369 if (RelativeName->CurDirRef)
1370 {
1371 InterlockedIncrement(&RtlpCurDirRef->RefCount);
1372 }
1373
1374 RelativeName->ContainingDirectory = CurrentDirectory->Handle;
1375 }
1376 }
1377 }
1378 }
1379
1380 /* Done */
1381 RtlReleasePebLock();
1382 return STATUS_SUCCESS;
1383 }
1384
1385 NTSTATUS
1386 NTAPI
RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative,IN PCWSTR DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)1387 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative,
1388 IN PCWSTR DosName,
1389 OUT PUNICODE_STRING NtName,
1390 OUT PCWSTR *PartName,
1391 OUT PRTL_RELATIVE_NAME_U RelativeName)
1392 {
1393 NTSTATUS Status;
1394 UNICODE_STRING NameString;
1395
1396 /* Create the unicode name */
1397 Status = RtlInitUnicodeStringEx(&NameString, DosName);
1398 if (NT_SUCCESS(Status))
1399 {
1400 /* Call the unicode function */
1401 Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative,
1402 &NameString,
1403 NtName,
1404 PartName,
1405 RelativeName);
1406 }
1407
1408 /* Return status */
1409 return Status;
1410 }
1411
1412 BOOLEAN
1413 NTAPI
RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)1414 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName,
1415 OUT PUNICODE_STRING NtName,
1416 OUT PCWSTR *PartName,
1417 OUT PRTL_RELATIVE_NAME_U RelativeName)
1418 {
1419 /* Call the internal function */
1420 ASSERT(RelativeName);
1421 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE,
1422 DosName,
1423 NtName,
1424 PartName,
1425 RelativeName));
1426 }
1427
1428 BOOLEAN
1429 NTAPI
RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,IN BOOLEAN SucceedIfBusy)1430 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
1431 IN BOOLEAN SucceedIfBusy)
1432 {
1433 BOOLEAN Result;
1434 RTL_RELATIVE_NAME_U RelativeName;
1435 UNICODE_STRING NtPathName;
1436 PVOID Buffer;
1437 OBJECT_ATTRIBUTES ObjectAttributes;
1438 NTSTATUS Status;
1439 FILE_BASIC_INFORMATION BasicInformation;
1440
1441 /* Get the NT Path */
1442 Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
1443 &NtPathName,
1444 NULL,
1445 &RelativeName);
1446 if (!Result) return FALSE;
1447
1448 /* Save the buffer */
1449 Buffer = NtPathName.Buffer;
1450
1451 /* Check if we have a relative name */
1452 if (RelativeName.RelativeName.Length)
1453 {
1454 /* Use it */
1455 NtPathName = RelativeName.RelativeName;
1456 }
1457 else
1458 {
1459 /* Otherwise ignore it */
1460 RelativeName.ContainingDirectory = NULL;
1461 }
1462
1463 /* Initialize the object attributes */
1464 InitializeObjectAttributes(&ObjectAttributes,
1465 &NtPathName,
1466 OBJ_CASE_INSENSITIVE,
1467 RelativeName.ContainingDirectory,
1468 NULL);
1469
1470 /* Query the attributes and free the buffer now */
1471 Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation);
1472 RtlReleaseRelativeName(&RelativeName);
1473 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1474
1475 /* Check if we failed */
1476 if (!NT_SUCCESS(Status))
1477 {
1478 /* Check if we failed because the file is in use */
1479 if ((Status == STATUS_SHARING_VIOLATION) ||
1480 (Status == STATUS_ACCESS_DENIED))
1481 {
1482 /* Check if the caller wants this to be considered OK */
1483 Result = SucceedIfBusy ? TRUE : FALSE;
1484 }
1485 else
1486 {
1487 /* A failure because the file didn't exist */
1488 Result = FALSE;
1489 }
1490 }
1491 else
1492 {
1493 /* The file exists */
1494 Result = TRUE;
1495 }
1496
1497 /* Return the result */
1498 return Result;
1499 }
1500
1501 BOOLEAN
1502 NTAPI
RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName)1503 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName)
1504 {
1505 /* Call the updated API */
1506 return RtlDoesFileExists_UstrEx(FileName, TRUE);
1507 }
1508
1509 BOOLEAN
1510 NTAPI
RtlDoesFileExists_UEx(IN PCWSTR FileName,IN BOOLEAN SucceedIfBusy)1511 RtlDoesFileExists_UEx(IN PCWSTR FileName,
1512 IN BOOLEAN SucceedIfBusy)
1513 {
1514 UNICODE_STRING NameString;
1515
1516 /* Create the unicode name*/
1517 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName)))
1518 {
1519 /* Call the unicode function */
1520 return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy);
1521 }
1522
1523 /* Fail */
1524 return FALSE;
1525 }
1526
1527 /* PUBLIC FUNCTIONS ***********************************************************/
1528
1529 /*
1530 * @implemented
1531 */
1532 VOID
1533 NTAPI
RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)1534 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
1535 {
1536 /* Check if a directory reference was grabbed */
1537 if (RelativeName->CurDirRef)
1538 {
1539 /* Decrease reference count */
1540 if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount))
1541 {
1542 /* If no one uses it any longer, close handle & free */
1543 NtClose(RelativeName->CurDirRef->Handle);
1544 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef);
1545 }
1546 RelativeName->CurDirRef = NULL;
1547 }
1548 }
1549
1550 /*
1551 * @implemented
1552 */
1553 ULONG
1554 NTAPI
RtlGetLongestNtPathLength(VOID)1555 RtlGetLongestNtPathLength(VOID)
1556 {
1557 /*
1558 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1559 * a mapped network drive), which is accessed through the DOS Global?? path.
1560 * This is, and has always been equal to, 269 characters, except in Wine
1561 * which claims this is 277. Go figure.
1562 */
1563 return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL);
1564 }
1565
1566 /*
1567 * @implemented
1568 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1569 * (with a 'e' instead of a 'a' in "Seperators").
1570 */
1571 NTSTATUS
1572 NTAPI
RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags,IN PCUNICODE_STRING PathString,OUT PULONG Length)1573 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags,
1574 IN PCUNICODE_STRING PathString,
1575 OUT PULONG Length)
1576 {
1577 ULONG NumChars;
1578
1579 /* Parameters validation */
1580 if (Length == NULL) return STATUS_INVALID_PARAMETER;
1581
1582 *Length = 0;
1583
1584 if (PathString == NULL) return STATUS_INVALID_PARAMETER;
1585
1586 /* No flags are supported yet */
1587 if (Flags != 0) return STATUS_INVALID_PARAMETER;
1588
1589 NumChars = PathString->Length / sizeof(WCHAR);
1590
1591 /*
1592 * Notice that we skip the last character, therefore:
1593 * - if we have: "some/path/f" we test for: "some/path/"
1594 * - if we have: "some/path/" we test for: "some/path"
1595 * - if we have: "s" we test for: ""
1596 * - if we have: "" then NumChars was already zero and we aren't there
1597 */
1598
1599 while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1]))
1600 {
1601 --NumChars;
1602 }
1603
1604 *Length = NumChars;
1605 return STATUS_SUCCESS;
1606 }
1607
1608 /*
1609 * @implemented
1610 */
1611 RTL_PATH_TYPE
1612 NTAPI
RtlDetermineDosPathNameType_U(IN PCWSTR Path)1613 RtlDetermineDosPathNameType_U(IN PCWSTR Path)
1614 {
1615 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
1616
1617 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1618 if (IS_PATH_SEPARATOR(Path[0]))
1619 {
1620 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */
1621 if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */
1622 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
1623 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
1624 return RtlPathTypeRootLocalDevice; /* \\. or \\? */
1625 }
1626 else
1627 {
1628 if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
1629 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */
1630 return RtlPathTypeDriveRelative; /* x: */
1631 }
1632 }
1633
1634 /*
1635 * @implemented
1636 */
1637 ULONG
1638 NTAPI
RtlIsDosDeviceName_U(IN PCWSTR Path)1639 RtlIsDosDeviceName_U(IN PCWSTR Path)
1640 {
1641 UNICODE_STRING PathString;
1642 NTSTATUS Status;
1643
1644 /* Build the string */
1645 Status = RtlInitUnicodeStringEx(&PathString, Path);
1646 if (!NT_SUCCESS(Status)) return 0;
1647
1648 /*
1649 * Returns 0 if name is not valid DOS device name, or DWORD with
1650 * offset in bytes to DOS device name from beginning of buffer in high word
1651 * and size in bytes of DOS device name in low word
1652 */
1653 return RtlIsDosDeviceName_Ustr(&PathString);
1654 }
1655
1656 /*
1657 * @implemented
1658 */
1659 ULONG
1660 NTAPI
RtlGetCurrentDirectory_U(_In_ ULONG MaximumLength,_Out_bytecap_ (MaximumLength)PWSTR Buffer)1661 RtlGetCurrentDirectory_U(
1662 _In_ ULONG MaximumLength,
1663 _Out_bytecap_(MaximumLength) PWSTR Buffer)
1664 {
1665 ULONG Length, Bytes;
1666 PCURDIR CurDir;
1667 PWSTR CurDirName;
1668 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
1669
1670 /* Lock the PEB to get the current directory */
1671 RtlAcquirePebLock();
1672 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
1673
1674 /* Get the buffer and character length */
1675 CurDirName = CurDir->DosPath.Buffer;
1676 Length = CurDir->DosPath.Length / sizeof(WCHAR);
1677 ASSERT((CurDirName != NULL) && (Length > 0));
1678
1679 /*
1680 * DosPath.Buffer should always have a trailing slash. There is an assert
1681 * below which checks for this.
1682 *
1683 * This function either returns x:\ for a root (keeping the original buffer)
1684 * or it returns x:\path\foo for a directory (replacing the trailing slash
1685 * with a NULL.
1686 */
1687 Bytes = Length * sizeof(WCHAR);
1688 if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
1689 {
1690 /* Check if caller does not have enough space */
1691 if (MaximumLength <= Bytes)
1692 {
1693 /* Call has no space for it, fail, add the trailing slash */
1694 RtlReleasePebLock();
1695 return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR);
1696 }
1697 }
1698 else
1699 {
1700 /* Check if caller does not have enough space */
1701 if (MaximumLength < Bytes)
1702 {
1703 /* Call has no space for it, fail */
1704 RtlReleasePebLock();
1705 return Bytes;
1706 }
1707 }
1708
1709 /* Copy the buffer since we seem to have space */
1710 RtlCopyMemory(Buffer, CurDirName, Bytes);
1711
1712 /* The buffer should end with a path separator */
1713 ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR);
1714
1715 /* Again check for our two cases (drive root vs path) */
1716 if ((Length <= 1) || (Buffer[Length - 2] != L':'))
1717 {
1718 /* Replace the trailing slash with a null */
1719 Buffer[Length - 1] = UNICODE_NULL;
1720 --Length;
1721 }
1722 else
1723 {
1724 /* Append the null char since there's no trailing slash */
1725 Buffer[Length] = UNICODE_NULL;
1726 }
1727
1728 /* Release PEB lock */
1729 RtlReleasePebLock();
1730 DPRINT("CurrentDirectory %S\n", Buffer);
1731 return Length * sizeof(WCHAR);
1732 }
1733
1734 /*
1735 * @implemented
1736 */
1737 NTSTATUS
1738 NTAPI
RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)1739 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
1740 {
1741 PCURDIR CurDir;
1742 NTSTATUS Status;
1743 RTL_PATH_TYPE PathType;
1744 IO_STATUS_BLOCK IoStatusBlock;
1745 UNICODE_STRING FullPath, NtName;
1746 PRTLP_CURDIR_REF OldCurDir = NULL;
1747 OBJECT_ATTRIBUTES ObjectAttributes;
1748 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo;
1749 ULONG SavedLength, CharLength, FullPathLength;
1750 HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL;
1751
1752 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
1753
1754 /* Initialize for failure case */
1755 RtlInitEmptyUnicodeString(&NtName, NULL, 0);
1756
1757 /* Can't set current directory on DOS device */
1758 if (RtlIsDosDeviceName_Ustr(Path))
1759 {
1760 return STATUS_NOT_A_DIRECTORY;
1761 }
1762
1763 /* Get current directory */
1764 RtlAcquirePebLock();
1765 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
1766
1767 /* Check if we have to drop current handle */
1768 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE)
1769 {
1770 OldHandle = CurDir->Handle;
1771 CurDir->Handle = NULL;
1772 }
1773
1774 /* Allocate a buffer for full path (using max possible length */
1775 FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength);
1776 if (!FullPath.Buffer)
1777 {
1778 Status = STATUS_NO_MEMORY;
1779 goto Leave;
1780 }
1781
1782 /* Init string */
1783 FullPath.Length = 0;
1784 FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
1785
1786 /* Get new directory full path */
1787 FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType);
1788 if (!FullPathLength)
1789 {
1790 Status = STATUS_OBJECT_NAME_INVALID;
1791 goto Leave;
1792 }
1793
1794 SavedLength = FullPath.MaximumLength;
1795 CharLength = FullPathLength / sizeof(WCHAR);
1796
1797 if (FullPathLength > FullPath.MaximumLength)
1798 {
1799 Status = STATUS_NAME_TOO_LONG;
1800 goto Leave;
1801 }
1802
1803 /* Translate it to NT name */
1804 if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
1805 {
1806 Status = STATUS_OBJECT_NAME_INVALID;
1807 goto Leave;
1808 }
1809
1810 InitializeObjectAttributes(&ObjectAttributes, &NtName,
1811 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
1812 NULL, NULL);
1813
1814 /* If previous current directory was removable, then check it for dropping */
1815 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
1816 {
1817 /* Get back normal handle */
1818 CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
1819 CurDir->Handle = NULL;
1820
1821 /* Get device information */
1822 Status = NtQueryVolumeInformationFile(CurDirHandle,
1823 &IoStatusBlock,
1824 &FileFsDeviceInfo,
1825 sizeof(FileFsDeviceInfo),
1826 FileFsDeviceInformation);
1827 /* Retry without taking care of removable device */
1828 if (!NT_SUCCESS(Status))
1829 {
1830 Status = RtlSetCurrentDirectory_U(Path);
1831 goto Leave;
1832 }
1833 }
1834 else
1835 {
1836 /* Open directory */
1837 Status = NtOpenFile(&CurDirHandle,
1838 SYNCHRONIZE | FILE_TRAVERSE,
1839 &ObjectAttributes,
1840 &IoStatusBlock,
1841 FILE_SHARE_READ | FILE_SHARE_WRITE,
1842 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
1843 if (!NT_SUCCESS(Status)) goto Leave;
1844
1845 /* Get device information */
1846 Status = NtQueryVolumeInformationFile(CurDirHandle,
1847 &IoStatusBlock,
1848 &FileFsDeviceInfo,
1849 sizeof(FileFsDeviceInfo),
1850 FileFsDeviceInformation);
1851 if (!NT_SUCCESS(Status)) goto Leave;
1852 }
1853
1854 /* If device is removable, mark handle */
1855 if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
1856 {
1857 CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
1858 }
1859
1860 FullPath.Length = (USHORT)FullPathLength;
1861
1862 /* If full path isn't \ terminated, do it */
1863 if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR)
1864 {
1865 if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
1866 {
1867 Status = STATUS_NAME_TOO_LONG;
1868 goto Leave;
1869 }
1870
1871 FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR;
1872 FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
1873 FullPath.Length += sizeof(WCHAR);
1874 }
1875
1876 /* If we have previous current directory with only us as reference, save it */
1877 if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1)
1878 {
1879 OldCurDirHandle = RtlpCurDirRef->Handle;
1880 }
1881 else
1882 {
1883 /* Allocate new current directory struct saving previous one */
1884 OldCurDir = RtlpCurDirRef;
1885 RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF));
1886 if (!RtlpCurDirRef)
1887 {
1888 RtlpCurDirRef = OldCurDir;
1889 OldCurDir = NULL;
1890 Status = STATUS_NO_MEMORY;
1891 goto Leave;
1892 }
1893
1894 /* Set reference to 1 (us) */
1895 RtlpCurDirRef->RefCount = 1;
1896 }
1897
1898 /* Save new data */
1899 CurDir->Handle = CurDirHandle;
1900 RtlpCurDirRef->Handle = CurDirHandle;
1901 CurDirHandle = NULL;
1902
1903 /* Copy full path */
1904 RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR));
1905 CurDir->DosPath.Length = FullPath.Length;
1906
1907 Status = STATUS_SUCCESS;
1908
1909 Leave:
1910 RtlReleasePebLock();
1911
1912 if (FullPath.Buffer)
1913 {
1914 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
1915 }
1916
1917 if (NtName.Buffer)
1918 {
1919 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
1920 }
1921
1922 if (CurDirHandle) NtClose(CurDirHandle);
1923
1924 if (OldHandle) NtClose(OldHandle);
1925
1926 if (OldCurDirHandle) NtClose(OldCurDirHandle);
1927
1928 if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
1929 {
1930 NtClose(OldCurDir->Handle);
1931 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
1932 }
1933
1934 return Status;
1935 }
1936
1937 /*
1938 * @implemented
1939 */
1940 ULONG
1941 NTAPI
RtlGetFullPathName_UEx(_In_ PWSTR FileName,_In_ ULONG BufferLength,_Out_writes_bytes_ (BufferLength)PWSTR Buffer,_Out_opt_ PWSTR * FilePart,_Out_opt_ RTL_PATH_TYPE * InputPathType)1942 RtlGetFullPathName_UEx(
1943 _In_ PWSTR FileName,
1944 _In_ ULONG BufferLength,
1945 _Out_writes_bytes_(BufferLength) PWSTR Buffer,
1946 _Out_opt_ PWSTR *FilePart,
1947 _Out_opt_ RTL_PATH_TYPE *InputPathType)
1948 {
1949 UNICODE_STRING FileNameString;
1950 NTSTATUS status;
1951
1952 if (InputPathType)
1953 *InputPathType = 0;
1954
1955 /* Build the string */
1956 status = RtlInitUnicodeStringEx(&FileNameString, FileName);
1957 if (!NT_SUCCESS(status)) return 0;
1958
1959 /* Call the extended function */
1960 return RtlGetFullPathName_Ustr(
1961 &FileNameString,
1962 BufferLength,
1963 Buffer,
1964 (PCWSTR*)FilePart,
1965 NULL,
1966 InputPathType);
1967 }
1968
1969 /******************************************************************
1970 * RtlGetFullPathName_U (NTDLL.@)
1971 *
1972 * Returns the number of bytes written to buffer (not including the
1973 * terminating NULL) if the function succeeds, or the required number of bytes
1974 * (including the terminating NULL) if the buffer is too small.
1975 *
1976 * file_part will point to the filename part inside buffer (except if we use
1977 * DOS device name, in which case file_in_buf is NULL)
1978 *
1979 * @implemented
1980 */
1981
1982 /*
1983 * @implemented
1984 */
1985 ULONG
1986 NTAPI
RtlGetFullPathName_U(_In_ PCWSTR FileName,_In_ ULONG Size,_Out_z_bytecap_ (Size)PWSTR Buffer,_Out_opt_ PWSTR * ShortName)1987 RtlGetFullPathName_U(
1988 _In_ PCWSTR FileName,
1989 _In_ ULONG Size,
1990 _Out_z_bytecap_(Size) PWSTR Buffer,
1991 _Out_opt_ PWSTR *ShortName)
1992 {
1993 RTL_PATH_TYPE PathType;
1994
1995 /* Call the extended function */
1996 return RtlGetFullPathName_UEx((PWSTR)FileName,
1997 Size,
1998 Buffer,
1999 ShortName,
2000 &PathType);
2001 }
2002
2003 /*
2004 * @implemented
2005 */
2006 BOOLEAN
2007 NTAPI
RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)2008 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,
2009 OUT PUNICODE_STRING NtName,
2010 OUT PCWSTR *PartName,
2011 OUT PRTL_RELATIVE_NAME_U RelativeName)
2012 {
2013 /* Call the internal function */
2014 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE,
2015 DosName,
2016 NtName,
2017 PartName,
2018 RelativeName));
2019 }
2020
2021 /*
2022 * @implemented
2023 */
2024 NTSTATUS
2025 NTAPI
RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)2026 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,
2027 OUT PUNICODE_STRING NtName,
2028 OUT PCWSTR *PartName,
2029 OUT PRTL_RELATIVE_NAME_U RelativeName)
2030 {
2031 /* Call the internal function */
2032 return RtlpDosPathNameToRelativeNtPathName_U(FALSE,
2033 DosName,
2034 NtName,
2035 PartName,
2036 RelativeName);
2037 }
2038
2039 /*
2040 * @implemented
2041 */
2042 BOOLEAN
2043 NTAPI
RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)2044 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,
2045 OUT PUNICODE_STRING NtName,
2046 OUT PCWSTR *PartName,
2047 OUT PRTL_RELATIVE_NAME_U RelativeName)
2048 {
2049 /* Call the internal function */
2050 ASSERT(RelativeName);
2051 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE,
2052 DosName,
2053 NtName,
2054 PartName,
2055 RelativeName));
2056 }
2057
2058 /*
2059 * @implemented
2060 */
2061 NTSTATUS
2062 NTAPI
RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,OUT PUNICODE_STRING NtName,OUT PCWSTR * PartName,OUT PRTL_RELATIVE_NAME_U RelativeName)2063 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,
2064 OUT PUNICODE_STRING NtName,
2065 OUT PCWSTR *PartName,
2066 OUT PRTL_RELATIVE_NAME_U RelativeName)
2067 {
2068 /* Call the internal function */
2069 ASSERT(RelativeName);
2070 return RtlpDosPathNameToRelativeNtPathName_U(TRUE,
2071 DosName,
2072 NtName,
2073 PartName,
2074 RelativeName);
2075 }
2076
2077 /*
2078 * @implemented
2079 */
RtlNtPathNameToDosPathName(IN ULONG Flags,IN OUT PRTL_UNICODE_STRING_BUFFER Path,OUT PULONG PathType,PULONG Unknown)2080 NTSTATUS NTAPI RtlNtPathNameToDosPathName(IN ULONG Flags,
2081 IN OUT PRTL_UNICODE_STRING_BUFFER Path,
2082 OUT PULONG PathType,
2083 PULONG Unknown)
2084 {
2085 PCUNICODE_STRING UsePrefix = NULL, AlternatePrefix = NULL;
2086
2087 if (PathType)
2088 *PathType = 0;
2089
2090 if (!Path || Flags)
2091 return STATUS_INVALID_PARAMETER;
2092
2093 /* The initial check is done on Path->String */
2094 if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix, &Path->String, TRUE))
2095 {
2096 UsePrefix = &RtlpDosDevicesUncPrefix;
2097 AlternatePrefix = &RtlpDoubleSlashPrefix;
2098 if (PathType)
2099 *PathType = RTL_CONVERTED_UNC_PATH;
2100 }
2101 else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix, &Path->String, FALSE))
2102 {
2103 UsePrefix = &RtlpDosDevicesPrefix;
2104 if (PathType)
2105 *PathType = RTL_CONVERTED_NT_PATH;
2106 }
2107
2108 if (UsePrefix)
2109 {
2110 NTSTATUS Status;
2111
2112 USHORT Len = Path->String.Length - UsePrefix->Length;
2113 if (AlternatePrefix)
2114 Len += AlternatePrefix->Length;
2115
2116 Status = RtlEnsureBufferSize(0, &Path->ByteBuffer, Len);
2117 if (!NT_SUCCESS(Status))
2118 return Status;
2119
2120 if (Len + sizeof(UNICODE_NULL) <= Path->ByteBuffer.Size)
2121 {
2122 /* Then, the contents of Path->ByteBuffer are always used... */
2123 if (AlternatePrefix)
2124 {
2125 memcpy(Path->ByteBuffer.Buffer, AlternatePrefix->Buffer, AlternatePrefix->Length);
2126 memmove(Path->ByteBuffer.Buffer + AlternatePrefix->Length, Path->ByteBuffer.Buffer + UsePrefix->Length,
2127 Len - AlternatePrefix->Length);
2128 }
2129 else
2130 {
2131 memmove(Path->ByteBuffer.Buffer, Path->ByteBuffer.Buffer + UsePrefix->Length, Len);
2132 }
2133 Path->String.Buffer = (PWSTR)Path->ByteBuffer.Buffer;
2134 Path->String.Length = Len;
2135 Path->String.MaximumLength = Path->ByteBuffer.Size;
2136 Path->String.Buffer[Len / sizeof(WCHAR)] = UNICODE_NULL;
2137 }
2138 return STATUS_SUCCESS;
2139 }
2140
2141 if (PathType)
2142 {
2143 switch (RtlDetermineDosPathNameType_Ustr(&Path->String))
2144 {
2145 case RtlPathTypeUncAbsolute:
2146 case RtlPathTypeDriveAbsolute:
2147 case RtlPathTypeLocalDevice:
2148 case RtlPathTypeRootLocalDevice:
2149 *PathType = RTL_UNCHANGED_DOS_PATH;
2150 break;
2151 case RtlPathTypeUnknown:
2152 case RtlPathTypeDriveRelative:
2153 case RtlPathTypeRooted:
2154 case RtlPathTypeRelative:
2155 *PathType = RTL_UNCHANGED_UNK_PATH;
2156 break;
2157 }
2158 }
2159
2160 return STATUS_SUCCESS;
2161 }
2162
2163 /*
2164 * @implemented
2165 */
2166 ULONG
2167 NTAPI
RtlDosSearchPath_U(IN PCWSTR Path,IN PCWSTR FileName,IN PCWSTR Extension,IN ULONG Size,IN PWSTR Buffer,OUT PWSTR * PartName)2168 RtlDosSearchPath_U(IN PCWSTR Path,
2169 IN PCWSTR FileName,
2170 IN PCWSTR Extension,
2171 IN ULONG Size,
2172 IN PWSTR Buffer,
2173 OUT PWSTR *PartName)
2174 {
2175 NTSTATUS Status;
2176 ULONG ExtensionLength, Length, FileNameLength, PathLength;
2177 UNICODE_STRING TempString;
2178 PWCHAR NewBuffer, BufferStart;
2179 PCWSTR p;
2180
2181 /* Check if this is an absolute path */
2182 if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
2183 {
2184 /* Check if the file exists */
2185 if (RtlDoesFileExists_UEx(FileName, TRUE))
2186 {
2187 /* Get the full name, which does the DOS lookup */
2188 return RtlGetFullPathName_U(FileName, Size, Buffer, PartName);
2189 }
2190
2191 /* Doesn't exist, so fail */
2192 return 0;
2193 }
2194
2195 /* Scan the filename */
2196 p = FileName;
2197 while (*p)
2198 {
2199 /* Looking for an extension */
2200 if (*p == L'.')
2201 {
2202 /* No extension string needed -- it's part of the filename */
2203 Extension = NULL;
2204 break;
2205 }
2206
2207 /* Next character */
2208 p++;
2209 }
2210
2211 /* Do we have an extension? */
2212 if (!Extension)
2213 {
2214 /* Nope, don't worry about one */
2215 ExtensionLength = 0;
2216 }
2217 else
2218 {
2219 /* Build a temporary string to get the extension length */
2220 Status = RtlInitUnicodeStringEx(&TempString, Extension);
2221 if (!NT_SUCCESS(Status)) return 0;
2222 ExtensionLength = TempString.Length;
2223 }
2224
2225 /* Build a temporary string to get the path length */
2226 Status = RtlInitUnicodeStringEx(&TempString, Path);
2227 if (!NT_SUCCESS(Status)) return 0;
2228 PathLength = TempString.Length;
2229
2230 /* Build a temporary string to get the filename length */
2231 Status = RtlInitUnicodeStringEx(&TempString, FileName);
2232 if (!NT_SUCCESS(Status)) return 0;
2233 FileNameLength = TempString.Length;
2234
2235 /* Allocate the buffer for the new string name */
2236 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2237 0,
2238 FileNameLength +
2239 ExtensionLength +
2240 PathLength +
2241 3 * sizeof(WCHAR));
2242 if (!NewBuffer)
2243 {
2244 /* Fail the call */
2245 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
2246 __FUNCTION__);
2247 return 0;
2248 }
2249
2250 /* Final loop to build the path */
2251 while (TRUE)
2252 {
2253 /* Check if we have a valid character */
2254 BufferStart = NewBuffer;
2255 if (*Path)
2256 {
2257 /* Loop as long as there's no semicolon */
2258 while (*Path != L';')
2259 {
2260 /* Copy the next character */
2261 *BufferStart++ = *Path++;
2262 if (!*Path) break;
2263 }
2264
2265 /* We found a semi-colon, to stop path processing on this loop */
2266 if (*Path == L';') ++Path;
2267 }
2268
2269 /* Add a terminating slash if needed */
2270 if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR))
2271 {
2272 *BufferStart++ = OBJ_NAME_PATH_SEPARATOR;
2273 }
2274
2275 /* Bail out if we reached the end */
2276 if (!*Path) Path = NULL;
2277
2278 /* Copy the file name and check if an extension is needed */
2279 RtlCopyMemory(BufferStart, FileName, FileNameLength);
2280 if (ExtensionLength)
2281 {
2282 /* Copy the extension too */
2283 RtlCopyMemory((PCHAR)BufferStart + FileNameLength,
2284 Extension,
2285 ExtensionLength + sizeof(WCHAR));
2286 }
2287 else
2288 {
2289 /* Just NULL-terminate */
2290 *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL;
2291 }
2292
2293 /* Now, does this file exist? */
2294 if (RtlDoesFileExists_UEx(NewBuffer, FALSE))
2295 {
2296 /* Call the full-path API to get the length */
2297 Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName);
2298 break;
2299 }
2300
2301 /* If we got here, path doesn't exist, so fail the call */
2302 Length = 0;
2303 if (!Path) break;
2304 }
2305
2306 /* Free the allocation and return the length */
2307 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
2308 return Length;
2309 }
2310
2311 /*
2312 * @implemented
2313 */
2314 NTSTATUS
2315 NTAPI
RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,IN PUNICODE_STRING StaticString,IN PUNICODE_STRING DynamicString,IN PUNICODE_STRING * StringUsed,IN PSIZE_T FilePartSize,OUT PBOOLEAN NameInvalid,OUT RTL_PATH_TYPE * PathType,OUT PSIZE_T LengthNeeded)2316 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,
2317 IN PUNICODE_STRING StaticString,
2318 IN PUNICODE_STRING DynamicString,
2319 IN PUNICODE_STRING *StringUsed,
2320 IN PSIZE_T FilePartSize,
2321 OUT PBOOLEAN NameInvalid,
2322 OUT RTL_PATH_TYPE* PathType,
2323 OUT PSIZE_T LengthNeeded)
2324 {
2325 NTSTATUS Status;
2326 PWCHAR StaticBuffer;
2327 PCWCH ShortName;
2328 ULONG Length;
2329 USHORT StaticLength;
2330 UNICODE_STRING TempDynamicString;
2331
2332 /* Initialize all our locals */
2333 ShortName = NULL;
2334 StaticBuffer = NULL;
2335 TempDynamicString.Buffer = NULL;
2336
2337 /* Initialize the input parameters */
2338 if (StringUsed) *StringUsed = NULL;
2339 if (LengthNeeded) *LengthNeeded = 0;
2340 if (FilePartSize) *FilePartSize = 0;
2341
2342 /* Check for invalid parameters */
2343 if ((DynamicString) && !(StringUsed) && (StaticString))
2344 {
2345 return STATUS_INVALID_PARAMETER;
2346 }
2347
2348 /* Check if we did not get an input string */
2349 if (!StaticString)
2350 {
2351 /* Allocate one */
2352 StaticLength = MAX_PATH * sizeof(WCHAR);
2353 StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR);
2354 if (!StaticBuffer) return STATUS_NO_MEMORY;
2355 }
2356 else
2357 {
2358 /* Use the one we received */
2359 StaticBuffer = StaticString->Buffer;
2360 StaticLength = StaticString->MaximumLength;
2361 }
2362
2363 /* Call the lower-level function */
2364 Length = RtlGetFullPathName_Ustr(FileName,
2365 StaticLength,
2366 StaticBuffer,
2367 &ShortName,
2368 NameInvalid,
2369 PathType);
2370 DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer);
2371 if (!Length)
2372 {
2373 /* Fail if it failed */
2374 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
2375 __FUNCTION__,
2376 __LINE__);
2377 Status = STATUS_OBJECT_NAME_INVALID;
2378 goto Quickie;
2379 }
2380
2381 /* Check if it fits inside our static string */
2382 if ((StaticString) && (Length < StaticLength))
2383 {
2384 /* Set the final length */
2385 StaticString->Length = (USHORT)Length;
2386
2387 /* Set the file part size */
2388 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
2389
2390 /* Return the static string if requested */
2391 if (StringUsed) *StringUsed = StaticString;
2392
2393 /* We are done with success */
2394 Status = STATUS_SUCCESS;
2395 goto Quickie;
2396 }
2397
2398 /* Did we not have an input dynamic string ?*/
2399 if (!DynamicString)
2400 {
2401 /* Return the length we need */
2402 if (LengthNeeded) *LengthNeeded = Length;
2403
2404 /* And fail such that the caller can try again */
2405 Status = STATUS_BUFFER_TOO_SMALL;
2406 goto Quickie;
2407 }
2408
2409 /* Check if it fits in our static buffer */
2410 if ((StaticBuffer) && (Length < StaticLength))
2411 {
2412 /* NULL-terminate it */
2413 StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
2414
2415 /* Set the settings for the dynamic string the caller sent */
2416 DynamicString->MaximumLength = StaticLength;
2417 DynamicString->Length = (USHORT)Length;
2418 DynamicString->Buffer = StaticBuffer;
2419
2420 /* Set the part size */
2421 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0;
2422
2423 /* Return the dynamic string if requested */
2424 if (StringUsed) *StringUsed = DynamicString;
2425
2426 /* Do not free the static buffer on exit, and return success */
2427 StaticBuffer = NULL;
2428 Status = STATUS_SUCCESS;
2429 goto Quickie;
2430 }
2431
2432 /* Now try again under the PEB lock */
2433 RtlAcquirePebLock();
2434 Length = RtlGetFullPathName_Ustr(FileName,
2435 StaticLength,
2436 StaticBuffer,
2437 &ShortName,
2438 NameInvalid,
2439 PathType);
2440 if (!Length)
2441 {
2442 /* It failed */
2443 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2444 __FUNCTION__, __LINE__);
2445 Status = STATUS_OBJECT_NAME_INVALID;
2446 goto Release;
2447 }
2448
2449 /* Check if it fits inside our static string now */
2450 if ((StaticString) && (Length < StaticLength))
2451 {
2452 /* Set the final length */
2453 StaticString->Length = (USHORT)Length;
2454
2455 /* Set the file part size */
2456 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
2457
2458 /* Return the static string if requested */
2459 if (StringUsed) *StringUsed = StaticString;
2460
2461 /* We are done with success */
2462 Status = STATUS_SUCCESS;
2463 goto Release;
2464 }
2465
2466 /* Check if the path won't even fit in a real string */
2467 if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
2468 {
2469 /* Name is way too long, fail */
2470 Status = STATUS_NAME_TOO_LONG;
2471 goto Release;
2472 }
2473
2474 /* Allocate the string to hold the path name now */
2475 TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR),
2476 TAG_USTR);
2477 if (!TempDynamicString.Buffer)
2478 {
2479 /* Out of memory, fail */
2480 Status = STATUS_NO_MEMORY;
2481 goto Release;
2482 }
2483
2484 /* Add space for a NULL terminator, and now check the full path */
2485 TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
2486 Length = RtlGetFullPathName_Ustr(FileName,
2487 Length,
2488 TempDynamicString.Buffer,
2489 &ShortName,
2490 NameInvalid,
2491 PathType);
2492 if (!Length)
2493 {
2494 /* Some path error, so fail out */
2495 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2496 __FUNCTION__, __LINE__);
2497 Status = STATUS_OBJECT_NAME_INVALID;
2498 goto Release;
2499 }
2500
2501 /* It should fit in the string we just allocated */
2502 ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR)));
2503 if (Length > TempDynamicString.MaximumLength)
2504 {
2505 /* This is really weird and would mean some kind of race */
2506 Status = STATUS_INTERNAL_ERROR;
2507 goto Release;
2508 }
2509
2510 /* Return the file part size */
2511 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0;
2512
2513 /* Terminate the whole string now */
2514 TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
2515
2516 /* Finalize the string and return it to the user */
2517 DynamicString->Buffer = TempDynamicString.Buffer;
2518 DynamicString->Length = (USHORT)Length;
2519 DynamicString->MaximumLength = TempDynamicString.MaximumLength;
2520 if (StringUsed) *StringUsed = DynamicString;
2521
2522 /* Return success and make sure we don't free the buffer on exit */
2523 TempDynamicString.Buffer = NULL;
2524 Status = STATUS_SUCCESS;
2525
2526 Release:
2527 /* Release the PEB lock */
2528 RtlReleasePebLock();
2529
2530 Quickie:
2531 /* Free any buffers we should be freeing */
2532 DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer);
2533 if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer))
2534 {
2535 RtlpFreeStringMemory(StaticBuffer, TAG_USTR);
2536 }
2537 if (TempDynamicString.Buffer)
2538 {
2539 RtlpFreeStringMemory(TempDynamicString.Buffer, TAG_USTR);
2540 }
2541
2542 /* Print out any unusual errors */
2543 if ((NT_ERROR(Status)) &&
2544 (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
2545 {
2546 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2547 __FUNCTION__, FileName, Status);
2548 }
2549
2550 /* Return, we're all done */
2551 return Status;
2552 }
2553
2554 /*
2555 * @implemented
2556 */
2557 NTSTATUS
2558 NTAPI
RtlDosSearchPath_Ustr(IN ULONG Flags,IN PUNICODE_STRING PathString,IN PUNICODE_STRING FileNameString,IN PUNICODE_STRING ExtensionString,IN PUNICODE_STRING CallerBuffer,IN OUT PUNICODE_STRING DynamicString OPTIONAL,OUT PUNICODE_STRING * FullNameOut OPTIONAL,OUT PSIZE_T FilePartSize OPTIONAL,OUT PSIZE_T LengthNeeded OPTIONAL)2559 RtlDosSearchPath_Ustr(IN ULONG Flags,
2560 IN PUNICODE_STRING PathString,
2561 IN PUNICODE_STRING FileNameString,
2562 IN PUNICODE_STRING ExtensionString,
2563 IN PUNICODE_STRING CallerBuffer,
2564 IN OUT PUNICODE_STRING DynamicString OPTIONAL,
2565 OUT PUNICODE_STRING* FullNameOut OPTIONAL,
2566 OUT PSIZE_T FilePartSize OPTIONAL,
2567 OUT PSIZE_T LengthNeeded OPTIONAL)
2568 {
2569 WCHAR StaticCandidateBuffer[MAX_PATH];
2570 UNICODE_STRING StaticCandidateString;
2571 NTSTATUS Status;
2572 RTL_PATH_TYPE PathType;
2573 PWCHAR p, End, CandidateEnd, SegmentEnd;
2574 SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0;
2575 USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0;
2576 PUNICODE_STRING FullIsolatedPath;
2577 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2578 Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString);
2579
2580 /* Initialize the input string */
2581 RtlInitEmptyUnicodeString(&StaticCandidateString,
2582 StaticCandidateBuffer,
2583 sizeof(StaticCandidateBuffer));
2584
2585 /* Initialize optional arguments */
2586 if (FullNameOut ) *FullNameOut = NULL;
2587 if (FilePartSize) *FilePartSize = 0;
2588 if (LengthNeeded) *LengthNeeded = 0;
2589 if (DynamicString)
2590 {
2591 DynamicString->Length = DynamicString->MaximumLength = 0;
2592 DynamicString->Buffer = NULL;
2593 }
2594
2595 /* Check for invalid parameters */
2596 if ((Flags & ~7) ||
2597 !(PathString) ||
2598 !(FileNameString) ||
2599 ((CallerBuffer) && (DynamicString) && !(FullNameOut)))
2600 {
2601 /* Fail */
2602 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__);
2603 Status = STATUS_INVALID_PARAMETER;
2604 goto Quickie;
2605 }
2606
2607 /* First check what kind of path this is */
2608 PathType = RtlDetermineDosPathNameType_Ustr(FileNameString);
2609
2610 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2611 if ((Flags & 2) &&
2612 (PathType == RtlPathTypeRelative) &&
2613 (FileNameString->Length >= (2 * sizeof(WCHAR))) &&
2614 (FileNameString->Buffer[0] == L'.') &&
2615 ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) ||
2616 ((FileNameString->Buffer[1] == L'.') &&
2617 ((FileNameString->Length >= (3 * sizeof(WCHAR))) &&
2618 (IS_PATH_SEPARATOR(FileNameString->Buffer[2]))))))
2619 {
2620 /* Yes, and this path is like that, so make it seem unknown */
2621 PathType = RtlPathTypeUnknown;
2622 }
2623
2624 /* Now check relative vs non-relative paths */
2625 if (PathType == RtlPathTypeRelative)
2626 {
2627 /* Does the caller want SxS? */
2628 if (Flags & 1)
2629 {
2630 /* Apply the SxS magic */
2631 FullIsolatedPath = NULL;
2632 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
2633 FileNameString,
2634 ExtensionString,
2635 CallerBuffer,
2636 DynamicString,
2637 &FullIsolatedPath,
2638 NULL,
2639 FilePartSize,
2640 LengthNeeded);
2641 if (NT_SUCCESS(Status))
2642 {
2643 /* We found the SxS path, return it */
2644 if (FullNameOut) *FullNameOut = FullIsolatedPath;
2645 goto Quickie;
2646 }
2647 else if (Status != STATUS_SXS_KEY_NOT_FOUND)
2648 {
2649 /* Critical SxS error, fail */
2650 DbgPrint("%s: Failing because call to "
2651 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2652 "status 0x%08lx\n",
2653 __FUNCTION__,
2654 FileNameString,
2655 Status);
2656 goto Quickie;
2657 }
2658 }
2659
2660 /* No SxS key found, or not requested, check if there's an extension */
2661 if (ExtensionString)
2662 {
2663 /* Save the extension length, and check if there's a file name */
2664 ExtensionLength = ExtensionString->Length;
2665 if (FileNameString->Length)
2666 {
2667 /* Start parsing the file name */
2668 End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)];
2669 while (End > FileNameString->Buffer)
2670 {
2671 /* If we find a path separator, there's no extension */
2672 if (IS_PATH_SEPARATOR(*--End)) break;
2673
2674 /* Otherwise, did we find an extension dot? */
2675 if (*End == L'.')
2676 {
2677 /* Ignore what the caller sent it, use the filename's */
2678 ExtensionString = NULL;
2679 ExtensionLength = 0;
2680 break;
2681 }
2682 }
2683 }
2684 }
2685
2686 /* Check if we got a path */
2687 if (PathString->Length)
2688 {
2689 /* Start parsing the path name, looking for path separators */
2690 End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)];
2691 p = End;
2692 while ((p > PathString->Buffer) && (*--p == L';'))
2693 {
2694 /* This is the size of the path -- handle a trailing slash */
2695 PathSize = End - p - 1;
2696 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
2697
2698 /* Check if we found a bigger path than before */
2699 if (PathSize > MaxPathSize) MaxPathSize = PathSize;
2700
2701 /* Keep going with the path after this path separator */
2702 End = p;
2703 }
2704
2705 /* This is the trailing path, run the same code as above */
2706 PathSize = End - p;
2707 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
2708 if (PathSize > MaxPathSize) MaxPathSize = PathSize;
2709
2710 /* Finally, convert the largest path size into WCHAR */
2711 MaxPathSize *= sizeof(WCHAR);
2712 }
2713
2714 /* Use the extension, the file name, and the largest path as the size */
2715 WorstCaseLength = ExtensionLength +
2716 FileNameString->Length +
2717 (USHORT)MaxPathSize +
2718 sizeof(UNICODE_NULL);
2719 if (WorstCaseLength > UNICODE_STRING_MAX_BYTES)
2720 {
2721 /* It has to fit in a registry string, if not, fail here */
2722 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2723 "worst case file name length is %Iu bytes\n",
2724 __FUNCTION__,
2725 WorstCaseLength);
2726 Status = STATUS_NAME_TOO_LONG;
2727 goto Quickie;
2728 }
2729
2730 /* Scan the path now, to see if we can find the file */
2731 p = PathString->Buffer;
2732 End = &p[PathString->Length / sizeof(WCHAR)];
2733 while (p < End)
2734 {
2735 /* Find out where this path ends */
2736 for (SegmentEnd = p;
2737 ((SegmentEnd != End) && (*SegmentEnd != L';'));
2738 SegmentEnd++);
2739
2740 /* Compute the size of this path */
2741 ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR);
2742
2743 /* Handle trailing slash if there isn't one */
2744 if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1))))
2745 {
2746 /* Add space for one */
2747 SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR);
2748 }
2749
2750 /* Now check if our initial static buffer is too small */
2751 if (StaticCandidateString.MaximumLength <
2752 (SegmentSize + ExtensionLength + FileNameString->Length))
2753 {
2754 /* At this point we should've been using our static buffer */
2755 ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer);
2756 if (StaticCandidateString.Buffer != StaticCandidateBuffer)
2757 {
2758 /* Something is really messed up if this was the dynamic string */
2759 DbgPrint("%s: internal error #1; "
2760 "CandidateString.Buffer = %p; "
2761 "StaticCandidateBuffer = %p\n",
2762 __FUNCTION__,
2763 StaticCandidateString.Buffer,
2764 StaticCandidateBuffer);
2765 Status = STATUS_INTERNAL_ERROR;
2766 goto Quickie;
2767 }
2768
2769 /* We checked before that the maximum possible size shoudl fit! */
2770 ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) <
2771 UNICODE_STRING_MAX_BYTES);
2772 if ((SegmentSize + ExtensionLength + FileNameString->Length) >
2773 (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR)))
2774 {
2775 /* For some reason it's not fitting anymore. Something messed up */
2776 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2777 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2778 __FUNCTION__,
2779 SegmentSize,
2780 FileNameString->Length,
2781 ExtensionLength);
2782 Status = STATUS_INTERNAL_ERROR;
2783 goto Quickie;
2784 }
2785
2786 /* Now allocate the dynamic string */
2787 StaticCandidateString.MaximumLength = FileNameString->Length +
2788 WorstCaseLength;
2789 StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength,
2790 TAG_USTR);
2791 if (!StaticCandidateString.Buffer)
2792 {
2793 /* Out of memory, fail */
2794 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2795 __FUNCTION__,
2796 StaticCandidateString.MaximumLength);
2797 Status = STATUS_NO_MEMORY;
2798 goto Quickie;
2799 }
2800 }
2801
2802 /* Copy the path in the string */
2803 RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount);
2804
2805 /* Get to the end of the string, and add the trailing slash if missing */
2806 CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)];
2807 if ((SegmentSize) && (SegmentSize != ByteCount))
2808 {
2809 *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR;
2810 }
2811
2812 /* Copy the filename now */
2813 RtlCopyMemory(CandidateEnd,
2814 FileNameString->Buffer,
2815 FileNameString->Length);
2816 CandidateEnd += (FileNameString->Length / sizeof(WCHAR));
2817
2818 /* Check if there was an extension */
2819 if (ExtensionString)
2820 {
2821 /* Copy the extension too */
2822 RtlCopyMemory(CandidateEnd,
2823 ExtensionString->Buffer,
2824 ExtensionString->Length);
2825 CandidateEnd += (ExtensionString->Length / sizeof(WCHAR));
2826 }
2827
2828 /* We are done, terminate it */
2829 *CandidateEnd = UNICODE_NULL;
2830
2831 /* Now set the final length of the string so it becomes valid */
2832 StaticCandidateString.Length = (USHORT)(CandidateEnd -
2833 StaticCandidateString.Buffer) *
2834 sizeof(WCHAR);
2835
2836 /* Check if this file exists */
2837 DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer);
2838 if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE))
2839 {
2840 /* Awesome, it does, now get the full path */
2841 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
2842 CallerBuffer,
2843 DynamicString,
2844 (PUNICODE_STRING*)FullNameOut,
2845 FilePartSize,
2846 NULL,
2847 &PathType,
2848 LengthNeeded);
2849 if (!(NT_SUCCESS(Status)) &&
2850 ((Status != STATUS_NO_SUCH_FILE) &&
2851 (Status != STATUS_BUFFER_TOO_SMALL)))
2852 {
2853 DbgPrint("%s: Failing because we thought we found %wZ on "
2854 "the search path, but RtlGetfullPathNameUStrEx() "
2855 "returned %08lx\n",
2856 __FUNCTION__,
2857 &StaticCandidateString,
2858 Status);
2859 }
2860 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
2861 goto Quickie;
2862 }
2863 else
2864 {
2865 /* Otherwise, move to the next path */
2866 if (SegmentEnd != End)
2867 {
2868 /* Handle the case of the path separator trailing */
2869 p = SegmentEnd + 1;
2870 }
2871 else
2872 {
2873 p = SegmentEnd;
2874 }
2875 }
2876 }
2877
2878 /* Loop finished and we didn't break out -- fail */
2879 Status = STATUS_NO_SUCH_FILE;
2880 }
2881 else
2882 {
2883 /* We have a full path, so check if it does exist */
2884 DPRINT("%wZ\n", FileNameString);
2885 if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE))
2886 {
2887 /* It doesn't exist, did we have an extension? */
2888 if (!(ExtensionString) || !(ExtensionString->Length))
2889 {
2890 /* No extension, so just fail */
2891 Status = STATUS_NO_SUCH_FILE;
2892 goto Quickie;
2893 }
2894
2895 /* There was an extension, check if the filename already had one */
2896 if (!(Flags & 4) && (FileNameString->Length))
2897 {
2898 /* Parse the filename */
2899 p = FileNameString->Buffer;
2900 End = &p[FileNameString->Length / sizeof(WCHAR)];
2901 while (End > p)
2902 {
2903 /* If there's a path separator, there's no extension */
2904 if (IS_PATH_SEPARATOR(*--End)) break;
2905
2906 /* Othwerwise, did we find an extension dot? */
2907 if (*End == L'.')
2908 {
2909 /* File already had an extension, so fail */
2910 Status = STATUS_NO_SUCH_FILE;
2911 goto Quickie;
2912 }
2913 }
2914 }
2915
2916 /* So there is an extension, we'll try again by adding it */
2917 NamePlusExtLength = FileNameString->Length +
2918 ExtensionString->Length +
2919 sizeof(UNICODE_NULL);
2920 if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES)
2921 {
2922 /* It won't fit in any kind of valid string, so fail */
2923 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2924 __FUNCTION__,
2925 NamePlusExtLength);
2926 Status = STATUS_NAME_TOO_LONG;
2927 goto Quickie;
2928 }
2929
2930 /* Fill it fit in our temporary string? */
2931 if (NamePlusExtLength > StaticCandidateString.MaximumLength)
2932 {
2933 /* It won't fit anymore, allocate a dynamic string for it */
2934 StaticCandidateString.MaximumLength = NamePlusExtLength;
2935 StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength,
2936 TAG_USTR);
2937 if (!StaticCandidateString.Buffer)
2938 {
2939 /* Ran out of memory, so fail */
2940 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2941 __FUNCTION__);
2942 Status = STATUS_NO_MEMORY;
2943 goto Quickie;
2944 }
2945 }
2946
2947 /* Copy the filename */
2948 RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
2949
2950 /* Copy the extension */
2951 RtlAppendUnicodeStringToString(&StaticCandidateString,
2952 ExtensionString);
2953
2954 DPRINT("SB: %wZ\n", &StaticCandidateString);
2955
2956 /* And check if this file now exists */
2957 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE))
2958 {
2959 /* Still no joy, fail out */
2960 Status = STATUS_NO_SUCH_FILE;
2961 goto Quickie;
2962 }
2963
2964 /* File was found, get the final full path */
2965 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
2966 CallerBuffer,
2967 DynamicString,
2968 (PUNICODE_STRING*)FullNameOut,
2969 FilePartSize,
2970 NULL,
2971 &PathType,
2972 LengthNeeded);
2973 if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE))
2974 {
2975 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2976 "failed with status %08lx\n",
2977 __FUNCTION__,
2978 &StaticCandidateString,
2979 Status);
2980 }
2981 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
2982 }
2983 else
2984 {
2985 /* File was found on the first try, get the final full path */
2986 Status = RtlGetFullPathName_UstrEx(FileNameString,
2987 CallerBuffer,
2988 DynamicString,
2989 (PUNICODE_STRING*)FullNameOut,
2990 FilePartSize,
2991 NULL,
2992 &PathType,
2993 LengthNeeded);
2994 if (!(NT_SUCCESS(Status)) &&
2995 ((Status != STATUS_NO_SUCH_FILE) &&
2996 (Status != STATUS_BUFFER_TOO_SMALL)))
2997 {
2998 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2999 "failed with status %08lx\n",
3000 __FUNCTION__,
3001 FileNameString,
3002 Status);
3003 }
3004 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
3005 }
3006 }
3007
3008 Quickie:
3009 /* Anything that was not an error, turn into STATUS_SUCCESS */
3010 if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS;
3011
3012 /* Check if we had a dynamic string */
3013 if ((StaticCandidateString.Buffer) &&
3014 (StaticCandidateString.Buffer != StaticCandidateBuffer))
3015 {
3016 /* Free it */
3017 RtlFreeUnicodeString(&StaticCandidateString);
3018 }
3019
3020 /* Return the status */
3021 return Status;
3022 }
3023
3024 /*
3025 * @implemented
3026 */
3027 BOOLEAN
3028 NTAPI
RtlDoesFileExists_U(IN PCWSTR FileName)3029 RtlDoesFileExists_U(IN PCWSTR FileName)
3030 {
3031 /* Call the new function */
3032 return RtlDoesFileExists_UEx(FileName, TRUE);
3033 }
3034
3035 /* EOF */
3036