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