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