[NTOSKRNL][RTL][DOC]
[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 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <rtl.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* DEFINITONS and MACROS ******************************************************/
19
20 #define MAX_PFX_SIZE 16
21
22 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
23
24
25 /* GLOBALS ********************************************************************/
26
27 static const WCHAR DeviceRootW[] = L"\\\\.\\";
28 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
29
30 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
31 const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\");
32 const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON");
33 const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\");
34
35 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");
36 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");
37 const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN");
38 const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");
39 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
40 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
41
42 /* PRIVATE FUNCTIONS **********************************************************/
43
44 ULONG
45 NTAPI
46 RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
47 {
48 UNICODE_STRING PathCopy;
49 PWCHAR Start, End;
50 ULONG PathChars, ColonCount = 0;
51 USHORT ReturnOffset = 0, ReturnLength;
52 WCHAR c;
53
54 /* Validate the input */
55 if (!PathString) return 0;
56
57 /* Check what type of path this is */
58 switch (RtlDetermineDosPathNameType_Ustr(PathString))
59 {
60 /* Fail for UNC or unknown paths */
61 case RtlPathTypeUnknown:
62 case RtlPathTypeUncAbsolute:
63 return 0;
64
65 /* Make special check for the CON device */
66 case RtlPathTypeLocalDevice:
67 if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE))
68 {
69 /* This should return 0x80006 */
70 return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length);
71 }
72 return 0;
73
74 default:
75 break;
76 }
77
78 /* Make a copy of the string */
79 PathCopy = *PathString;
80
81 /* Return if there's no characters */
82 PathChars = PathCopy.Length / sizeof(WCHAR);
83 if (!PathChars) return 0;
84
85 /* Check for drive path and truncate */
86 if (PathCopy.Buffer[PathChars - 1] == L':')
87 {
88 /* Fixup the lengths */
89 PathCopy.Length -= sizeof(WCHAR);
90 if (!--PathChars) return 0;
91
92 /* Remember this for later */
93 ColonCount = 1;
94 }
95
96 /* Check for extension or space, and truncate */
97 c = PathCopy.Buffer[PathChars - 1];
98 do
99 {
100 /* Stop if we hit a space or period */
101 if ((c != '.') && (c != ' ')) break;
102
103 /* Fixup the lengths and get the next character */
104 PathCopy.Length -= sizeof(WCHAR);
105 if (!--PathChars) c = PathCopy.Buffer[PathChars - 1];
106
107 /* Remember this for later */
108 ColonCount++;
109 } while (PathChars);
110
111 /* Anything still left? */
112 if (PathChars)
113 {
114 /* Loop from the end */
115 for (End = &PathCopy.Buffer[PathChars - 1];
116 End >= PathCopy.Buffer;
117 --End)
118 {
119 /* Check if the character is a path or drive separator */
120 c = *End;
121 if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1)))
122 {
123 /* Get the next lower case character */
124 End++;
125 c = *End | ' '; // ' ' == ('z' - 'Z')
126
127 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
128 if ((End < &PathCopy.Buffer[PathCopy.Length / sizeof(WCHAR)]) &&
129 ((c == 'l') || (c == 'c') || (c == 'p') || (c == 'a') || (c == 'n')))
130 {
131 /* Calculate the offset */
132 ReturnOffset = (PCHAR)End - (PCHAR)PathCopy.Buffer;
133
134 /* Build the final string */
135 PathCopy.Length -= ReturnOffset;
136 PathCopy.Length -= (ColonCount * sizeof(WCHAR));
137 PathCopy.Buffer = End;
138 break;
139 }
140 }
141
142 return 0;
143 }
144
145 /* Get the next lower case character and check if it's a DOS device */
146 c = *PathCopy.Buffer | ' '; // ' ' == ('z' - 'Z')
147 if ((c != 'l') && (c != 'c') && (c != 'p') && (c != 'a') && (c != 'n'))
148 {
149 /* Not LPT, COM, PRN, AUX, or NUL */
150 return 0;
151 }
152 }
153
154 /* Now skip past any extra extension or drive letter characters */
155 Start = PathCopy.Buffer;
156 End = &Start[PathChars];
157 while (Start < End)
158 {
159 c = *Start;
160 if ((c == '.') || (c == ':')) break;
161 Start++;
162 }
163
164 /* And then go backwards to get rid of spaces */
165 while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start;
166
167 /* Finally see how many characters are left, and that's our size */
168 PathChars = Start - PathCopy.Buffer;
169 PathCopy.Length = PathChars * sizeof(WCHAR);
170
171 /* Check if this is a COM or LPT port, which has a digit after it */
172 if ((PathChars == 4) &&
173 (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != '0')))
174 {
175 /* Don't compare the number part, just check for LPT or COM */
176 PathCopy.Length -= sizeof(WCHAR);
177 if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) ||
178 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
179 {
180 /* Found it */
181 ReturnLength = sizeof(L"COM1");
182 return MAKELONG(ReturnOffset, ReturnLength);
183 }
184 }
185 else if ((PathChars == 3) &&
186 ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) ||
187 (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) ||
188 (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) ||
189 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
190 {
191 /* Otherwise this was something like AUX, NUL, PRN, or CON */
192 ReturnLength = sizeof(L"AUX");
193 return MAKELONG(ReturnOffset, ReturnLength);
194 }
195
196 /* Otherwise, this isn't a valid DOS device */
197 return 0;
198 }
199
200 RTL_PATH_TYPE
201 NTAPI
202 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
203 {
204 PWCHAR Path;
205 ULONG Chars;
206
207 /* Validate the input */
208 if (!PathString) return RtlPathTypeUnknown;
209
210 Path = PathString->Buffer;
211 Chars = PathString->Length / sizeof(WCHAR);
212
213 /* Return if there are no characters */
214 if (!Chars) return RtlPathTypeUnknown;
215
216 /*
217 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
218 * actually check for the path length before touching the characters
219 */
220 if ((Chars < 1) || (IS_PATH_SEPARATOR(Path[0])))
221 {
222 if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */
223 if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */
224 if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
225 if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
226 return RtlPathTypeRootLocalDevice; /* \\. or \\? */
227 }
228 else
229 {
230 if ((Chars < 2) || (!(Path[0]) || (Path[1] != L':'))) return RtlPathTypeRelative; /* x */
231 if ((Chars < 3) || (IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveAbsolute; /* x:\ */
232 return RtlPathTypeDriveRelative; /* x: */
233 }
234 }
235
236 NTSTATUS
237 NTAPI
238 RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
239 IN ULONG Length,
240 OUT PBOOLEAN NameInvalid)
241 {
242 PWCHAR Buffer;
243 NTSTATUS Status;
244
245 /* Allocate a large enough buffer */
246 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length);
247 if (Buffer)
248 {
249 /* Assume failure */
250 *NameInvalid = TRUE;
251
252 /* Copy the filename */
253 RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length);
254
255 /* And add a dot at the end */
256 Buffer[Length / sizeof(WCHAR)] = L'.';
257 Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
258
259 /* Check if the file exists or not */
260 *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE;
261
262 /* Get rid of the buffer now */
263 Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
264 }
265 else
266 {
267 /* Assume the name is ok, but fail the call */
268 *NameInvalid = FALSE;
269 Status = STATUS_NO_MEMORY;
270 }
271
272 /* Return the status */
273 return Status;
274 }
275
276 ULONG
277 NTAPI
278 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName,
279 IN ULONG Size,
280 IN PWSTR Buffer,
281 OUT PCWSTR *ShortName,
282 OUT PBOOLEAN InvalidName,
283 OUT RTL_PATH_TYPE *PathType)
284 {
285 PWCHAR FileNameBuffer;
286 ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength;
287 WCHAR c;
288 NTSTATUS Status;
289
290 /* For now, assume the name is valid */
291 DPRINT("Filename: %wZ\n", FileName);
292 DPRINT("Size and buffer: %lx %S\n", Size, Buffer);
293 if (InvalidName) *InvalidName = FALSE;
294
295 /* Handle initial path type and failure case */
296 *PathType = RtlPathTypeUnknown;
297 if (!(Size) || !(Buffer) || !(FileName) ||
298 !(FileName->Length) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
299
300 /* Break filename into component parts */
301 FileNameBuffer = FileName->Buffer;
302 FileNameLength = FileName->Length;
303 FileNameChars = FileNameLength / sizeof(WCHAR);
304
305 /* Kill trailing spaces */
306 c = FileNameBuffer[FileNameChars - 1];
307 while ((FileNameLength) && (c == L' '))
308 {
309 /* Keep going, ignoring the spaces */
310 FileNameLength -= sizeof(WCHAR);
311 if (FileNameLength) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
312 }
313
314 /* Check if anything is left */
315 if (!FileNameLength) return 0;
316
317 /* Check if this is a DOS name */
318 DosLength = RtlIsDosDeviceName_Ustr(FileName);
319 DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
320 if (DosLength)
321 {
322 /* Zero out the short name */
323 if (ShortName) *ShortName = NULL;
324
325 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
326 DosLengthOffset = DosLength >> 16;
327 DosLength = DosLength & 0xFFFF;
328
329 /* Do we have a DOS length, and does the caller want validity? */
330 if ((InvalidName) && (DosLengthOffset))
331 {
332 /* Do the check */
333 Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
334
335 /* If the check failed, or the name is invalid, fail here */
336 if (!NT_SUCCESS(Status)) return 0;
337 if (*InvalidName) return 0;
338 }
339
340 /* Add the size of the device root and check if it fits in the size */
341 FullLength = DosLength + DeviceRootString.Length;
342 if (FullLength < Size)
343 {
344 /* Add the device string */
345 RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length);
346
347 /* Now add the DOS device name */
348 RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length,
349 (PCHAR)FileNameBuffer + DosLengthOffset,
350 DosLength);
351
352 /* Null terminate */
353 *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL;
354 return FullLength;
355 }
356
357 /* Otherwise, there's no space, so return the buffer size needed */
358 if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0;
359 return FullLength + sizeof(UNICODE_NULL);
360 }
361
362 /* This should work well enough for our current needs */
363 *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
364 DPRINT("Path type: %lx\n", *PathType);
365
366 /* This is disgusting... but avoids re-writing everything */
367 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer, Size, Buffer);
368 return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName);
369 }
370
371 NTSTATUS
372 NTAPI
373 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
374 OUT PUNICODE_STRING NtPath,
375 OUT PCWSTR *PartName,
376 OUT PRTL_RELATIVE_NAME_U RelativeName)
377 {
378 ULONG DosLength;
379 PWSTR NewBuffer, p;
380
381 /* Validate the input */
382 if (!DosPath) return STATUS_OBJECT_NAME_INVALID;
383
384 /* Validate the DOS length */
385 DosLength = DosPath->Length;
386 if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
387
388 /* Make space for the new path */
389 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
390 0,
391 DosLength + sizeof(UNICODE_NULL));
392 if (!NewBuffer) return STATUS_NO_MEMORY;
393
394 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
395 RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length);
396 RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length,
397 DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR),
398 DosPath->Length - RtlpDosDevicesPrefix.Length);
399 NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL;
400
401 /* Did the caller send a relative name? */
402 if (RelativeName)
403 {
404 /* Zero initialize it */
405 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
406 RelativeName->ContainingDirectory = NULL;
407 RelativeName->CurDirRef = 0;
408 }
409
410 /* Did the caller request a partial name? */
411 if (PartName)
412 {
413 /* Loop from the back until we find a path separator */
414 p = &NewBuffer[(DosLength - 1) / sizeof (WCHAR)];
415 while (p > NewBuffer) if (*p-- == '\\') break;
416
417 /* Was one found? */
418 if (p > NewBuffer)
419 {
420 /* Move past it -- anything left? */
421 p++;
422 if (!*p)
423 {
424 /* The path ends with a path separator, no part name */
425 *PartName = NULL;
426 }
427 else
428 {
429 /* What follows the path separator is the part name */
430 *PartName = p;
431 }
432 }
433 }
434
435 /* Build the final NT path string */
436 NtPath->Length = DosLength;
437 NtPath->Buffer = NewBuffer;
438 NtPath->MaximumLength = DosLength + sizeof(UNICODE_NULL);
439 return STATUS_SUCCESS;
440 }
441
442 NTSTATUS
443 NTAPI
444 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
445 IN PCUNICODE_STRING DosName,
446 OUT PUNICODE_STRING NtName,
447 OUT PCWSTR *PartName,
448 OUT PRTL_RELATIVE_NAME_U RelativeName)
449 {
450 WCHAR BigBuffer[MAX_PATH + 1];
451 PWCHAR PrefixBuffer, NewBuffer, Buffer;
452 ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
453 UNICODE_STRING CapturedDosName, PartNameString;
454 BOOLEAN QuickPath;
455 RTL_PATH_TYPE InputPathType, BufferPathType;
456 NTSTATUS Status;
457 BOOLEAN NameInvalid;
458
459 /* Assume MAX_PATH for now */
460 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
461 HaveRelative, DosName, NtName, PartName, RelativeName);
462 MaxLength = sizeof(BigBuffer);
463
464 /* Validate the input */
465 if (!DosName) return STATUS_OBJECT_NAME_INVALID;
466
467 /* Capture input string */
468 CapturedDosName = *DosName;
469
470 /* Check for \\?\\ form */
471 if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) ||
472 (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) ||
473 (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) ||
474 (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) ||
475 (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3]))
476 {
477 /* Quick path won't be used */
478 QuickPath = FALSE;
479
480 /* Use the static buffer */
481 Buffer = BigBuffer;
482 MaxLength += RtlpDosDevicesUncPrefix.Length;
483
484 /* Allocate a buffer to hold the path */
485 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
486 DPRINT("Length: %lx\n", MaxLength);
487 if (!NewBuffer) return STATUS_NO_MEMORY;
488 }
489 else
490 {
491 /* Use the optimized path after acquiring the lock */
492 QuickPath = TRUE;
493 NewBuffer = NULL;
494 }
495
496 /* Lock the PEB and check if the quick path can be used */
497 RtlAcquirePebLock();
498 if (QuickPath)
499 {
500 /* Some simple fixups will get us the correct path */
501 DPRINT("Quick path\n");
502 Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName,
503 NtName,
504 PartName,
505 RelativeName);
506
507 /* Release the lock, we're done here */
508 RtlReleasePebLock();
509 return Status;
510 }
511
512 /* Call the main function to get the full path name and length */
513 PathLength = RtlGetFullPathName_Ustr(&CapturedDosName,
514 MAX_PATH * sizeof(WCHAR),
515 Buffer,
516 PartName,
517 &NameInvalid,
518 &InputPathType);
519 if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR))))
520 {
521 /* Invalid name, fail */
522 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength);
523 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
524 RtlReleasePebLock();
525 return STATUS_OBJECT_NAME_INVALID;
526 }
527
528 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
529 PrefixLength = RtlpDosDevicesPrefix.Length;
530 PrefixBuffer = RtlpDosDevicesPrefix.Buffer;
531 PrefixCut = 0;
532
533 /* Check where it really is */
534 BufferPathType = RtlDetermineDosPathNameType_U(Buffer);
535 DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType);
536 switch (BufferPathType)
537 {
538 /* It's actually a UNC path in \??\UNC\ */
539 case RtlPathTypeUncAbsolute:
540 PrefixLength = RtlpDosDevicesUncPrefix.Length;
541 PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer;
542 PrefixCut = 2;
543 break;
544
545 case RtlPathTypeLocalDevice:
546 /* We made a good guess, go with it but skip the \??\ */
547 PrefixCut = 4;
548 break;
549
550 case RtlPathTypeDriveAbsolute:
551 case RtlPathTypeDriveRelative:
552 case RtlPathTypeRooted:
553 case RtlPathTypeRelative:
554 /* Our guess was good, roll with it */
555 break;
556
557 /* Nothing else is expected */
558 default:
559 ASSERT(FALSE);
560
561 }
562
563 /* Now copy the prefix and the buffer */
564 RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
565 RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
566 &Buffer[PrefixCut],
567 PathLength - (PrefixCut * sizeof(WCHAR)));
568
569 /* Compute the length */
570 Length = PathLength - PrefixCut * sizeof(WCHAR) + PrefixLength;
571 LengthChars = Length / sizeof(WCHAR);
572
573 /* Setup the actual NT path string and terminate it */
574 NtName->Buffer = NewBuffer;
575 NtName->Length = Length;
576 NtName->MaximumLength = MaxLength;
577 NewBuffer[LengthChars] = UNICODE_NULL;
578 DPRINT("new buffer: %S\n", NewBuffer);
579 DPRINT("NT Name: %wZ\n", NtName);
580
581 /* Check if a partial name was requested */
582 if ((PartName) && (*PartName))
583 {
584 /* Convert to Unicode */
585 Status = RtlInitUnicodeStringEx(&PartNameString, *PartName);
586 if (NT_SUCCESS(Status))
587 {
588 /* Set the partial name */
589 *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))];
590 }
591 else
592 {
593 /* Fail */
594 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
595 RtlReleasePebLock();
596 return Status;
597 }
598 }
599
600 /* Check if a relative name was asked for */
601 if (RelativeName)
602 {
603 /* Setup the structure */
604 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
605 RelativeName->ContainingDirectory = NULL;
606 RelativeName->CurDirRef = 0;
607
608 /* Check if the input path itself was relative */
609 if (InputPathType == RtlPathTypeRelative)
610 {
611 /* FIXME: HACK: Old code */
612 PCURDIR cd;
613 UNICODE_STRING us;
614 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
615 if (cd->Handle)
616 {
617 RtlInitUnicodeString(&us, Buffer);
618 us.Length = (cd->DosPath.Length < us.Length) ? cd->DosPath.Length : us.Length;
619 if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
620 {
621 Length = ((cd->DosPath.Length / sizeof(WCHAR)) - PrefixCut) + ((InputPathType == 1) ? 8 : 4);
622 RelativeName->RelativeName.Buffer = NewBuffer + Length;
623 RelativeName->RelativeName.Length = NtName->Length - (Length * sizeof(WCHAR));
624 RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
625 RelativeName->ContainingDirectory = cd->Handle;
626 }
627 }
628 }
629 }
630
631 /* Done */
632 RtlReleasePebLock();
633 return STATUS_SUCCESS;
634 }
635
636 NTSTATUS
637 NTAPI
638 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative,
639 IN PCWSTR DosName,
640 OUT PUNICODE_STRING NtName,
641 OUT PCWSTR *PartName,
642 OUT PRTL_RELATIVE_NAME_U RelativeName)
643 {
644 NTSTATUS Status;
645 UNICODE_STRING NameString;
646
647 /* Create the unicode name */
648 Status = RtlInitUnicodeStringEx(&NameString, DosName);
649 if (NT_SUCCESS(Status))
650 {
651 /* Call the unicode function */
652 Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative,
653 &NameString,
654 NtName,
655 PartName,
656 RelativeName);
657 }
658
659 /* Return status */
660 return Status;
661 }
662
663 BOOLEAN
664 NTAPI
665 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName,
666 OUT PUNICODE_STRING NtName,
667 OUT PCWSTR *PartName,
668 OUT PRTL_RELATIVE_NAME_U RelativeName)
669 {
670 /* Call the internal function */
671 ASSERT(RelativeName);
672 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE,
673 DosName,
674 NtName,
675 PartName,
676 RelativeName));
677 }
678
679 BOOLEAN
680 NTAPI
681 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
682 IN BOOLEAN SucceedIfBusy)
683 {
684 BOOLEAN Result;
685 RTL_RELATIVE_NAME_U RelativeName;
686 UNICODE_STRING NtPathName;
687 PVOID Buffer;
688 OBJECT_ATTRIBUTES ObjectAttributes;
689 NTSTATUS Status;
690 FILE_BASIC_INFORMATION BasicInformation;
691
692 /* Validate the input */
693 if (!FileName) return FALSE;
694
695 /* Get the NT Path */
696 Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
697 &NtPathName,
698 NULL,
699 &RelativeName);
700 if (!Result) return FALSE;
701
702 /* Save the buffer */
703 Buffer = NtPathName.Buffer;
704
705 /* Check if we have a relative name */
706 if (RelativeName.RelativeName.Length)
707 {
708 /* Use it */
709 NtPathName = RelativeName.RelativeName;
710 }
711 else
712 {
713 /* Otherwise ignore it */
714 RelativeName.ContainingDirectory = NULL;
715 }
716
717 /* Initialize the object attributes */
718 InitializeObjectAttributes(&ObjectAttributes,
719 &NtPathName,
720 OBJ_CASE_INSENSITIVE,
721 RelativeName.ContainingDirectory,
722 NULL);
723
724 /* Query the attributes and free the buffer now */
725 Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation);
726 RtlReleaseRelativeName(&RelativeName);
727 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
728
729 /* Check if we failed */
730 if (!NT_SUCCESS(Status))
731 {
732 /* Check if we failed because the file is in use */
733 if ((Status == STATUS_SHARING_VIOLATION) ||
734 (Status == STATUS_ACCESS_DENIED))
735 {
736 /* Check if the caller wants this to be considered OK */
737 Result = SucceedIfBusy ? TRUE : FALSE;
738 }
739 else
740 {
741 /* A failure because the file didn't exist */
742 Result = FALSE;
743 }
744 }
745 else
746 {
747 /* The file exists */
748 Result = TRUE;
749 }
750
751 /* Return the result */
752 return Result;
753 }
754
755 BOOLEAN
756 NTAPI
757 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName)
758 {
759 /* Call the updated API */
760 return RtlDoesFileExists_UstrEx(FileName, TRUE);
761 }
762
763 BOOLEAN
764 NTAPI
765 RtlDoesFileExists_UEx(IN PCWSTR FileName,
766 IN BOOLEAN SucceedIfBusy)
767 {
768 UNICODE_STRING NameString;
769
770 /* Create the unicode name*/
771 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName)))
772 {
773 /* Call the unicode function */
774 return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy);
775 }
776
777 /* Fail */
778 return FALSE;
779 }
780
781 /* PUBLIC FUNCTIONS ***********************************************************/
782
783 /*
784 * @implemented
785 */
786 VOID
787 NTAPI
788 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
789 {
790 /* Check if a directory reference was grabbed */
791 if (RelativeName->CurDirRef)
792 {
793 /* FIXME: Not yet supported */
794 UNIMPLEMENTED;
795 RelativeName->CurDirRef = NULL;
796 }
797 }
798
799 /*
800 * @implemented
801 */
802 ULONG
803 NTAPI
804 RtlGetLongestNtPathLength(VOID)
805 {
806 /*
807 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
808 * a mapped network drive), which is accessed through the DOS Global?? path.
809 * This is, and has always been equal to, 269 characters, except in Wine
810 * which claims this is 277. Go figure.
811 */
812 return (MAX_PATH + RtlpDosDevicesUncPrefix.Length + sizeof(ANSI_NULL));
813 }
814
815 /*
816 * @implemented
817 */
818 ULONG
819 NTAPI
820 RtlDetermineDosPathNameType_U(IN PCWSTR Path)
821 {
822 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
823
824 /* Validate the input */
825 if (!Path) return RtlPathTypeUnknown;
826
827 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
828 if (IS_PATH_SEPARATOR(Path[0]))
829 {
830 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */
831 if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */
832 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */
833 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */
834 return RtlPathTypeRootLocalDevice; /* \\. or \\? */
835 }
836 else
837 {
838 if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */
839 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */
840 return RtlPathTypeDriveRelative; /* x: */
841 }
842 }
843
844 /*
845 * @implemented
846 */
847 ULONG
848 NTAPI
849 RtlIsDosDeviceName_U(IN PWSTR Path)
850 {
851 UNICODE_STRING PathString;
852 NTSTATUS Status;
853
854 /* Build the string */
855 Status = RtlInitUnicodeStringEx(&PathString, Path);
856 if (!NT_SUCCESS(Status)) return 0;
857
858 /*
859 * Returns 0 if name is not valid DOS device name, or DWORD with
860 * offset in bytes to DOS device name from beginning of buffer in high word
861 * and size in bytes of DOS device name in low word
862 */
863 return RtlIsDosDeviceName_Ustr(&PathString);
864 }
865
866 /*
867 * @implemented
868 */
869 ULONG
870 NTAPI
871 RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
872 IN PWSTR Buffer)
873 {
874 ULONG Length, Bytes;
875 PCURDIR CurDir;
876 PWSTR CurDirName;
877 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
878
879 /* Lock the PEB to get the current directory */
880 RtlAcquirePebLock();
881 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
882
883 /* Get the buffer and character length */
884 CurDirName = CurDir->DosPath.Buffer;
885 Length = CurDir->DosPath.Length / sizeof(WCHAR);
886 ASSERT((CurDirName != NULL) && (Length > 0));
887
888 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
889 Bytes = Length * sizeof(WCHAR);
890 if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
891 {
892 /* Check if caller does not have enough space */
893 if (MaximumLength <= Bytes)
894 {
895 /* Call has no space for it, fail, add the trailing slash */
896 RtlReleasePebLock();
897 return Bytes + sizeof(L'\\');
898 }
899 }
900 else
901 {
902 /* Check if caller does not have enough space */
903 if (MaximumLength <= Bytes)
904 {
905 /* Call has no space for it, fail */
906 RtlReleasePebLock();
907 return Bytes;
908 }
909 }
910
911 /* Copy the buffer since we seem to have space */
912 RtlCopyMemory(Buffer, CurDirName, Bytes);
913
914 /* The buffer should end with a path separator */
915 ASSERT(Buffer[Length - 1] == L'\\');
916
917 /* Again check for our two cases (drive root vs path) */
918 if ((Length <= 1) || (Buffer[Length - 2] != L':'))
919 {
920 /* Replace the trailing slash with a null */
921 Buffer[Length - 1] = UNICODE_NULL;
922 --Length;
923 }
924 else
925 {
926 /* Append the null char since there's no trailing slash */
927 Buffer[Length] = UNICODE_NULL;
928 }
929
930 /* Release PEB lock */
931 RtlReleasePebLock();
932 DPRINT("CurrentDirectory %S\n", Buffer);
933 return Length * sizeof(WCHAR);
934 }
935
936 /*
937 * @implemented
938 */
939 NTSTATUS NTAPI
940 RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
941 {
942 UNICODE_STRING full;
943 FILE_FS_DEVICE_INFORMATION device_info;
944 OBJECT_ATTRIBUTES Attr;
945 IO_STATUS_BLOCK iosb;
946 PCURDIR cd;
947 NTSTATUS Status;
948 ULONG size;
949 HANDLE handle = NULL;
950 PWSTR ptr;
951
952 DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
953
954 full.Buffer = NULL;
955
956 RtlAcquirePebLock ();
957
958 cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
959
960 if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
961 {
962 RtlReleasePebLock ();
963 return STATUS_OBJECT_NAME_INVALID;
964 }
965
966 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
967
968 InitializeObjectAttributes (&Attr,
969 &full,
970 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
971 NULL,
972 NULL);
973
974 Status = ZwOpenFile (&handle,
975 SYNCHRONIZE | FILE_TRAVERSE,
976 &Attr,
977 &iosb,
978 FILE_SHARE_READ | FILE_SHARE_WRITE,
979 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
980
981 if (!NT_SUCCESS(Status))
982 {
983 RtlFreeUnicodeString( &full);
984 RtlReleasePebLock ();
985 return Status;
986 }
987
988 /* don't keep the directory handle open on removable media */
989 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
990 sizeof(device_info), FileFsDeviceInformation )) &&
991 (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
992 {
993 DPRINT1("don't keep the directory handle open on removable media\n");
994 ZwClose( handle );
995 handle = 0;
996 }
997
998 if (cd->Handle)
999 ZwClose(cd->Handle);
1000 cd->Handle = handle;
1001
1002 /* append trailing \ if missing */
1003 size = full.Length / sizeof(WCHAR);
1004 ptr = full.Buffer;
1005 ptr += 4; /* skip \??\ prefix */
1006 size -= 4;
1007
1008 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
1009 * So the nullterm is replaced with \
1010 * -Gunnar
1011 */
1012 if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
1013
1014 memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
1015 cd->DosPath.Buffer[size] = 0;
1016 cd->DosPath.Length = size * sizeof(WCHAR);
1017
1018 RtlFreeUnicodeString( &full);
1019 RtlReleasePebLock();
1020
1021 return STATUS_SUCCESS;
1022 }
1023
1024
1025 /******************************************************************
1026 * collapse_path
1027 *
1028 * Helper for RtlGetFullPathName_U.
1029 * Get rid of . and .. components in the path.
1030 */
1031 void FORCEINLINE collapse_path( WCHAR *path, UINT mark )
1032 {
1033 WCHAR *p, *next;
1034
1035 /* convert every / into a \ */
1036 for (p = path; *p; p++) if (*p == '/') *p = '\\';
1037
1038 /* collapse duplicate backslashes */
1039 next = path + max( 1, mark );
1040 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
1041 *next = 0;
1042
1043 p = path + mark;
1044 while (*p)
1045 {
1046 if (*p == '.')
1047 {
1048 switch(p[1])
1049 {
1050 case '\\': /* .\ component */
1051 next = p + 2;
1052 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
1053 continue;
1054 case 0: /* final . */
1055 if (p > path + mark) p--;
1056 *p = 0;
1057 continue;
1058 case '.':
1059 if (p[2] == '\\') /* ..\ component */
1060 {
1061 next = p + 3;
1062 if (p > path + mark)
1063 {
1064 p--;
1065 while (p > path + mark && p[-1] != '\\') p--;
1066 }
1067 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
1068 continue;
1069 }
1070 else if (!p[2]) /* final .. */
1071 {
1072 if (p > path + mark)
1073 {
1074 p--;
1075 while (p > path + mark && p[-1] != '\\') p--;
1076 if (p > path + mark) p--;
1077 }
1078 *p = 0;
1079 continue;
1080 }
1081 break;
1082 }
1083 }
1084 /* skip to the next component */
1085 while (*p && *p != '\\') p++;
1086 if (*p == '\\')
1087 {
1088 /* remove last dot in previous dir name */
1089 if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
1090 else p++;
1091 }
1092 }
1093
1094 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
1095 while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
1096 *p = 0;
1097 }
1098
1099
1100
1101 /******************************************************************
1102 * skip_unc_prefix
1103 *
1104 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
1105 */
1106 static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
1107 {
1108 ptr += 2;
1109 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */
1110 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
1111 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */
1112 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
1113 return ptr;
1114 }
1115
1116
1117 /******************************************************************
1118 * get_full_path_helper
1119 *
1120 * Helper for RtlGetFullPathName_U
1121 * Note: name and buffer are allowed to point to the same memory spot
1122 */
1123 static ULONG get_full_path_helper(
1124 LPCWSTR name,
1125 LPWSTR buffer,
1126 ULONG size)
1127 {
1128 ULONG reqsize = 0, mark = 0, dep = 0, deplen;
1129 LPWSTR ins_str = NULL;
1130 LPCWSTR ptr;
1131 const UNICODE_STRING* cd;
1132 WCHAR tmp[4];
1133
1134 /* return error if name only consists of spaces */
1135 for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
1136 if (!*ptr) return 0;
1137
1138 RtlAcquirePebLock();
1139
1140 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
1141 cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath;
1142
1143 switch (RtlDetermineDosPathNameType_U(name))
1144 {
1145 case RtlPathTypeUncAbsolute: /* \\foo */
1146 ptr = skip_unc_prefix( name );
1147 mark = (ptr - name);
1148 break;
1149
1150 case RtlPathTypeLocalDevice: /* \\.\foo */
1151 mark = 4;
1152 break;
1153
1154 case RtlPathTypeDriveAbsolute: /* c:\foo */
1155 reqsize = sizeof(WCHAR);
1156 tmp[0] = towupper(name[0]);
1157 ins_str = tmp;
1158 dep = 1;
1159 mark = 3;
1160 break;
1161
1162 case RtlPathTypeDriveRelative: /* c:foo */
1163 dep = 2;
1164 if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
1165 {
1166 UNICODE_STRING var, val;
1167
1168 tmp[0] = '=';
1169 tmp[1] = name[0];
1170 tmp[2] = ':';
1171 tmp[3] = '\0';
1172 var.Length = 3 * sizeof(WCHAR);
1173 var.MaximumLength = 4 * sizeof(WCHAR);
1174 var.Buffer = tmp;
1175 val.Length = 0;
1176 val.MaximumLength = size;
1177 val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
1178 if (val.Buffer == NULL)
1179 {
1180 reqsize = 0;
1181 goto done;
1182 }
1183
1184 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
1185 {
1186 case STATUS_SUCCESS:
1187 /* FIXME: Win2k seems to check that the environment variable actually points
1188 * to an existing directory. If not, root of the drive is used
1189 * (this seems also to be the only spot in RtlGetFullPathName that the
1190 * existence of a part of a path is checked)
1191 */
1192 /* fall thru */
1193 case STATUS_BUFFER_TOO_SMALL:
1194 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
1195 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
1196 ins_str = val.Buffer;
1197 break;
1198 case STATUS_VARIABLE_NOT_FOUND:
1199 reqsize = 3 * sizeof(WCHAR);
1200 tmp[0] = name[0];
1201 tmp[1] = ':';
1202 tmp[2] = '\\';
1203 ins_str = tmp;
1204 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
1205 break;
1206 default:
1207 DPRINT1("Unsupported status code\n");
1208 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
1209 break;
1210 }
1211 mark = 3;
1212 break;
1213 }
1214 /* fall through */
1215
1216 case RtlPathTypeRelative: /* foo */
1217 reqsize = cd->Length;
1218 ins_str = cd->Buffer;
1219 if (cd->Buffer[1] != ':')
1220 {
1221 ptr = skip_unc_prefix( cd->Buffer );
1222 mark = ptr - cd->Buffer;
1223 }
1224 else mark = 3;
1225 break;
1226
1227 case RtlPathTypeRooted: /* \xxx */
1228 #ifdef __WINE__
1229 if (name[0] == '/') /* may be a Unix path */
1230 {
1231 const WCHAR *ptr = name;
1232 int drive = find_drive_root( &ptr );
1233 if (drive != -1)
1234 {
1235 reqsize = 3 * sizeof(WCHAR);
1236 tmp[0] = 'A' + drive;
1237 tmp[1] = ':';
1238 tmp[2] = '\\';
1239 ins_str = tmp;
1240 mark = 3;
1241 dep = ptr - name;
1242 break;
1243 }
1244 }
1245 #endif
1246 if (cd->Buffer[1] == ':')
1247 {
1248 reqsize = 2 * sizeof(WCHAR);
1249 tmp[0] = cd->Buffer[0];
1250 tmp[1] = ':';
1251 ins_str = tmp;
1252 mark = 3;
1253 }
1254 else
1255 {
1256 ptr = skip_unc_prefix( cd->Buffer );
1257 reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
1258 mark = reqsize / sizeof(WCHAR);
1259 ins_str = cd->Buffer;
1260 }
1261 break;
1262
1263 case RtlPathTypeRootLocalDevice: /* \\. */
1264 reqsize = 4 * sizeof(WCHAR);
1265 dep = 3;
1266 tmp[0] = '\\';
1267 tmp[1] = '\\';
1268 tmp[2] = '.';
1269 tmp[3] = '\\';
1270 ins_str = tmp;
1271 mark = 4;
1272 break;
1273
1274 case RtlPathTypeUnknown:
1275 goto done;
1276 }
1277
1278 /* enough space ? */
1279 deplen = wcslen(name + dep) * sizeof(WCHAR);
1280 if (reqsize + deplen + sizeof(WCHAR) > size)
1281 {
1282 /* not enough space, return need size (including terminating '\0') */
1283 reqsize += deplen + sizeof(WCHAR);
1284 goto done;
1285 }
1286
1287 memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
1288 if (reqsize) memcpy(buffer, ins_str, reqsize);
1289 reqsize += deplen;
1290
1291 if (ins_str != tmp && ins_str != cd->Buffer)
1292 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
1293
1294 collapse_path( buffer, mark );
1295 reqsize = wcslen(buffer) * sizeof(WCHAR);
1296
1297 done:
1298 RtlReleasePebLock();
1299 return reqsize;
1300 }
1301
1302
1303 /******************************************************************
1304 * RtlGetFullPathName_U (NTDLL.@)
1305 *
1306 * Returns the number of bytes written to buffer (not including the
1307 * terminating NULL) if the function succeeds, or the required number of bytes
1308 * (including the terminating NULL) if the buffer is too small.
1309 *
1310 * file_part will point to the filename part inside buffer (except if we use
1311 * DOS device name, in which case file_in_buf is NULL)
1312 *
1313 * @implemented
1314 */
1315 ULONG NTAPI RtlGetFullPathName_U(
1316 const WCHAR* name,
1317 ULONG size,
1318 WCHAR* buffer,
1319 WCHAR** file_part)
1320 {
1321 WCHAR* ptr;
1322 ULONG dosdev;
1323 ULONG reqsize;
1324
1325 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
1326
1327 if (!name || !*name) return 0;
1328
1329 if (file_part) *file_part = NULL;
1330
1331 /* check for DOS device name */
1332 dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
1333 if (dosdev)
1334 {
1335 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
1336 DWORD sz = LOWORD(dosdev); /* in bytes */
1337
1338 if (8 + sz + 2 > size) return sz + 10;
1339 wcscpy(buffer, DeviceRootW);
1340 memmove(buffer + 4, name + offset, sz);
1341 buffer[4 + sz / sizeof(WCHAR)] = '\0';
1342 /* file_part isn't set in this case */
1343 return sz + 8;
1344 }
1345
1346 reqsize = get_full_path_helper(name, buffer, size);
1347 if (!reqsize) return 0;
1348 if (reqsize > size)
1349 {
1350 LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
1351 if (tmp == NULL) return 0;
1352 reqsize = get_full_path_helper(name, tmp, reqsize);
1353 if (reqsize + sizeof(WCHAR) > size) /* it may have worked the second time */
1354 {
1355 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1356 return reqsize + sizeof(WCHAR);
1357 }
1358 memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
1359 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1360 }
1361
1362 /* find file part */
1363 if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
1364 *file_part = ptr;
1365 return reqsize;
1366 }
1367
1368 /*
1369 * @implemented
1370 */
1371 BOOLEAN
1372 NTAPI
1373 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,
1374 OUT PUNICODE_STRING NtName,
1375 OUT PCWSTR *PartName,
1376 OUT PRTL_RELATIVE_NAME_U RelativeName)
1377 {
1378 /* Call the internal function */
1379 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE,
1380 DosName,
1381 NtName,
1382 PartName,
1383 RelativeName));
1384 }
1385
1386 /*
1387 * @implemented
1388 */
1389 NTSTATUS
1390 NTAPI
1391 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,
1392 OUT PUNICODE_STRING NtName,
1393 OUT PCWSTR *PartName,
1394 OUT PRTL_RELATIVE_NAME_U RelativeName)
1395 {
1396 /* Call the internal function */
1397 return RtlpDosPathNameToRelativeNtPathName_U(FALSE,
1398 DosName,
1399 NtName,
1400 PartName,
1401 RelativeName);
1402 }
1403
1404 /*
1405 * @implemented
1406 */
1407 BOOLEAN
1408 NTAPI
1409 RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName,
1410 OUT PUNICODE_STRING NtName,
1411 OUT PCWSTR *PartName,
1412 OUT PRTL_RELATIVE_NAME_U RelativeName)
1413 {
1414 /* Call the internal function */
1415 ASSERT(RelativeName);
1416 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE,
1417 DosName,
1418 NtName,
1419 PartName,
1420 RelativeName));
1421 }
1422
1423 /*
1424 * @implemented
1425 */
1426 NTSTATUS
1427 NTAPI
1428 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PWSTR DosName,
1429 OUT PUNICODE_STRING NtName,
1430 OUT PCWSTR *PartName,
1431 OUT PRTL_RELATIVE_NAME_U RelativeName)
1432 {
1433 /* Call the internal function */
1434 ASSERT(RelativeName);
1435 return RtlpDosPathNameToRelativeNtPathName_U(TRUE,
1436 DosName,
1437 NtName,
1438 PartName,
1439 RelativeName);
1440 }
1441
1442 /*
1443 * @unimplemented
1444 */
1445 NTSTATUS NTAPI
1446 RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
1447 {
1448 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1449 return STATUS_NOT_IMPLEMENTED;
1450 }
1451
1452 /*
1453 * @implemented
1454 */
1455 ULONG
1456 NTAPI
1457 RtlDosSearchPath_U(IN PCWSTR Path,
1458 IN PCWSTR FileName,
1459 IN PCWSTR Extension,
1460 IN ULONG Size,
1461 IN PWSTR Buffer,
1462 OUT PWSTR *PartName)
1463 {
1464 NTSTATUS Status;
1465 ULONG ExtensionLength, Length, FileNameLength, PathLength;
1466 UNICODE_STRING TempString;
1467 PWCHAR NewBuffer, BufferStart;
1468 PCWSTR p;
1469
1470 /* Validate the input */
1471 if (!(Path) || !(FileName)) return 0;
1472
1473 /* Check if this is an absolute path */
1474 if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
1475 {
1476 /* Check if the file exists */
1477 if (RtlDoesFileExists_UEx(FileName, TRUE))
1478 {
1479 /* Get the full name, which does the DOS lookup */
1480 return RtlGetFullPathName_U(FileName, Size, Buffer, PartName);
1481 }
1482
1483 /* Doesn't exist, so fail */
1484 return 0;
1485 }
1486
1487 /* Scan the filename */
1488 p = FileName;
1489 while (*p)
1490 {
1491 /* Looking for an extension */
1492 if (*p == '.')
1493 {
1494 /* No extension string needed -- it's part of the filename */
1495 Extension = NULL;
1496 break;
1497 }
1498
1499 /* Next character */
1500 p++;
1501 }
1502
1503 /* Do we have an extension? */
1504 if (!Extension)
1505 {
1506 /* Nope, don't worry about one */
1507 ExtensionLength = 0;
1508 }
1509 else
1510 {
1511 /* Build a temporary string to get the extension length */
1512 Status = RtlInitUnicodeStringEx(&TempString, Extension);
1513 if (!NT_SUCCESS(Status)) return 0;
1514 ExtensionLength = TempString.Length;
1515 }
1516
1517 /* Build a temporary string to get the path length */
1518 Status = RtlInitUnicodeStringEx(&TempString, Path);
1519 if (!NT_SUCCESS(Status)) return 0;
1520 PathLength = TempString.Length;
1521
1522 /* Build a temporary string to get the filename length */
1523 Status = RtlInitUnicodeStringEx(&TempString, FileName);
1524 if (!NT_SUCCESS(Status)) return 0;
1525 FileNameLength = TempString.Length;
1526
1527 /* Allocate the buffer for the new string name */
1528 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
1529 0,
1530 FileNameLength +
1531 ExtensionLength +
1532 PathLength +
1533 3 * sizeof(WCHAR));
1534 if (!NewBuffer)
1535 {
1536 /* Fail the call */
1537 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1538 __FUNCTION__);
1539 return 0;
1540 }
1541
1542 /* Final loop to build the path */
1543 while (TRUE)
1544 {
1545 /* Check if we have a valid character */
1546 BufferStart = NewBuffer;
1547 if (*Path)
1548 {
1549 /* Loop as long as there's no semicolon */
1550 while (*Path != ';')
1551 {
1552 /* Copy the next character */
1553 *BufferStart++ = *Path++;
1554 if (!*Path) break;
1555 }
1556
1557 /* We found a semi-colon, to stop path processing on this loop */
1558 if (*Path == ';') ++Path;
1559 }
1560
1561 /* Add a terminating slash if needed */
1562 if ((BufferStart != NewBuffer) && (BufferStart[-1] != '\\'))
1563 {
1564 *BufferStart++ = '\\';
1565 }
1566
1567 /* Bail out if we reached the end */
1568 if (!*Path) Path = NULL;
1569
1570 /* Copy the file name and check if an extension is needed */
1571 RtlCopyMemory(BufferStart, FileName, FileNameLength);
1572 if (ExtensionLength)
1573 {
1574 /* Copy the extension too */
1575 RtlCopyMemory((PCHAR)BufferStart + FileNameLength,
1576 Extension,
1577 ExtensionLength + sizeof(WCHAR));
1578 }
1579 else
1580 {
1581 /* Just NULL-terminate */
1582 *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL;
1583 }
1584
1585 /* Now, does this file exist? */
1586 if (RtlDoesFileExists_UEx(NewBuffer, FALSE))
1587 {
1588 /* Call the full-path API to get the length */
1589 Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName);
1590 break;
1591 }
1592
1593 /* If we got here, path doesn't exist, so fail the call */
1594 Length = 0;
1595 if (!Path) break;
1596 }
1597
1598 /* Free the allocation and return the length */
1599 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1600 return Length;
1601 }
1602
1603 /*
1604 * @implemented
1605 */
1606 BOOLEAN
1607 NTAPI
1608 RtlDoesFileExists_U(IN PCWSTR FileName)
1609 {
1610 /* Call the new function */
1611 return RtlDoesFileExists_UEx(FileName, TRUE);
1612 }
1613
1614 /* EOF */