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