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