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