3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/ntdll/rtl/path.c
6 * PURPOSE: Path and current directory functions
11 /* INCLUDES ******************************************************************/
13 #include <ddk/ntddk.h>
14 #include <ntdll/rtl.h>
18 #include <ddk/obfuncs.h>
21 #include <ntdll/ntdll.h>
23 /* DEFINITONS and MACROS ******************************************************/
25 #define MAX_PFX_SIZE 16
27 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
29 /* GLOBALS ********************************************************************/
31 static const UNICODE_STRING _condev =
33 .Length = sizeof(L"\\\\.\\CON") - sizeof(WCHAR),
34 .MaximumLength = sizeof(L"\\\\.\\CON"),
35 .Buffer = L"\\\\.\\CON"
38 static const UNICODE_STRING _lpt =
40 .Length = sizeof(L"LPT") - sizeof(WCHAR),
41 .MaximumLength = sizeof(L"LPT"),
45 static const UNICODE_STRING _com =
47 .Length = sizeof(L"COM") - sizeof(WCHAR),
48 .MaximumLength = sizeof(L"COM"),
52 static const UNICODE_STRING _prn =
54 .Length = sizeof(L"PRN") - sizeof(WCHAR),
55 .MaximumLength = sizeof(L"PRN"),
59 static const UNICODE_STRING _aux =
61 .Length = sizeof(L"AUX") - sizeof(WCHAR),
62 .MaximumLength = sizeof(L"AUX"),
66 static const UNICODE_STRING _con =
68 .Length = sizeof(L"CON") - sizeof(WCHAR),
69 .MaximumLength = sizeof(L"CON"),
73 static const UNICODE_STRING _nul =
75 .Length = sizeof(L"NUL") - sizeof(WCHAR),
76 .MaximumLength = sizeof(L"NUL"),
80 /* FUNCTIONS *****************************************************************/
82 static ULONG RtlpGetDotSequence (PWSTR p)
90 else if ((*p == '\\' || *p == '\0') && Count)
100 static VOID RtlpEatPath (PWSTR Path)
107 while ((*p) != 0 || ((*p) == L'\\' && (*(p+1)) == 0))
111 DotLen = RtlpGetDotSequence (p+1);
112 DPRINT("DotSequenceLength %u\n", DotLen);
113 DPRINT("prev '%S' p '%S'\n",prev,p);
118 p = wcschr(p + 1, L'\\');
124 else if (DotLen == 1)
134 while (n > 0 && prev > (Path + 2))
137 if ((*prev) == L'\\')
142 if (*(p + DotLen + 1) == 0)
145 wcscpy (prev, p + DotLen + 1);
147 if (prev > (Path + 2))
150 while ((*prev) != L'\\')
168 ULONG STDCALL RtlGetLongestNtPathLength (VOID)
170 return (MAX_PATH + 9);
178 RtlDetermineDosPathNameType_U(PWSTR Path)
180 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
187 if (IS_PATH_SEPARATOR(Path[0]))
189 if (!IS_PATH_SEPARATOR(Path[1]))
195 return 1; /* \\xxx */
197 if (IS_PATH_SEPARATOR(Path[3]))
198 return 6; /* \\.\xxx */
201 return 1; /* \\.xxxx */
210 if (IS_PATH_SEPARATOR(Path[2]))
211 return 2; /* x:\xxx */
213 return 3; /* x:xxx */
218 /* returns 0 if name is not valid DOS device name, or DWORD with
219 * offset in bytes to DOS device name from beginning of buffer in high word
220 * and size in bytes of DOS device name in low word */
226 RtlIsDosDeviceName_U(PWSTR DeviceName)
232 UNICODE_STRING DeviceNameU;
234 if (DeviceName == NULL)
239 while (DeviceName[Length])
244 Type = RtlDetermineDosPathNameType_U(DeviceName);
252 DeviceNameU.Length = DeviceNameU.MaximumLength = Length * sizeof(WCHAR);
253 DeviceNameU.Buffer = DeviceName;
255 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_condev, TRUE))
260 /* name can end with ':' */
261 if (Length && DeviceName[Length - 1 ] == L':')
266 /* there can be spaces or points at the end of name */
267 wc = DeviceName + Length - 1;
268 while (Length && (*wc == L'.' || *wc == L' '))
274 /* let's find a beginning of name */
275 wc = DeviceName + Length - 1;
276 while (wc > DeviceName && !IS_PATH_SEPARATOR(*(wc - 1)))
280 Offset = wc - DeviceName;
282 DeviceNameU.Length = DeviceNameU.MaximumLength = 3 * sizeof(WCHAR);
283 DeviceNameU.Buffer = wc;
285 /* check for LPTx or COMx */
286 if (Length == 4 && wc[3] >= L'0' && wc[3] <= L'9')
293 if (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_lpt, TRUE) ||
294 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_com, TRUE))
296 return ((Offset * 2) << 16 ) | 8;
301 /* check for PRN,AUX,NUL or CON */
303 (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_prn, TRUE) ||
304 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_aux, TRUE) ||
305 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_nul, TRUE) ||
306 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_con, TRUE)))
308 return ((Offset * 2) << 16) | 6;
319 RtlGetCurrentDirectory_U(ULONG MaximumLength,
325 DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
329 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
330 Length = cd->DosPath.Length / sizeof(WCHAR);
331 if (cd->DosPath.Buffer[Length - 1] == L'\\' &&
332 cd->DosPath.Buffer[Length - 2] != L':')
335 DPRINT ("cd->DosPath.Buffer %S Length %d\n",
336 cd->DosPath.Buffer, Length);
338 if (MaximumLength / sizeof(WCHAR) > Length)
342 Length * sizeof(WCHAR));
350 RtlReleasePebLock ();
352 DPRINT ("CurrentDirectory %S\n", Buffer);
354 return (Length * sizeof(WCHAR));
362 RtlSetCurrentDirectory_U(PUNICODE_STRING name)
365 UNICODE_STRING envvar;
366 OBJECT_ATTRIBUTES Attr;
367 IO_STATUS_BLOCK iosb;
371 HANDLE handle = NULL;
374 PFILE_NAME_INFORMATION filenameinfo;
375 ULONG backslashcount = 0;
379 DPRINT ("RtlSetCurrentDirectory %wZ\n", name);
381 RtlAcquirePebLock ();
382 cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName;
383 size = cd->DosPath.MaximumLength;
385 buf = RtlAllocateHeap (RtlGetProcessHeap(),
390 RtlReleasePebLock ();
391 return STATUS_NO_MEMORY;
394 size = RtlGetFullPathName_U (name->Buffer, size, buf, 0);
397 RtlFreeHeap (RtlGetProcessHeap (),
400 RtlReleasePebLock ();
401 return STATUS_OBJECT_NAME_INVALID;
404 if (!RtlDosPathNameToNtPathName_U (buf, &full, 0, 0))
406 RtlFreeHeap (RtlGetProcessHeap (),
409 RtlFreeHeap (RtlGetProcessHeap (),
412 RtlReleasePebLock ();
413 return STATUS_OBJECT_NAME_INVALID;
416 InitializeObjectAttributes (&Attr,
418 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
422 Status = NtOpenFile (&handle,
423 SYNCHRONIZE | FILE_TRAVERSE,
426 FILE_SHARE_READ | FILE_SHARE_WRITE,
427 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
428 if (!NT_SUCCESS(Status))
430 RtlFreeHeap (RtlGetProcessHeap (),
433 RtlFreeHeap (RtlGetProcessHeap (),
436 RtlReleasePebLock ();
440 filenameinfo = RtlAllocateHeap(RtlGetProcessHeap(),
442 MAX_PATH*sizeof(WCHAR)+sizeof(ULONG));
444 Status = NtQueryInformationFile(handle,
447 MAX_PATH*sizeof(WCHAR)+sizeof(ULONG),
448 FileNameInformation);
449 if (!NT_SUCCESS(Status))
451 RtlFreeHeap(RtlGetProcessHeap(),
454 RtlFreeHeap(RtlGetProcessHeap(),
457 RtlFreeHeap(RtlGetProcessHeap(),
464 if (filenameinfo->FileName[1]) // If it's just "\", we need special handling
466 wcs = buf + size / sizeof(WCHAR) - 1;
471 size -= sizeof(WCHAR);
474 for (cntr=filenameinfo->FileName;*cntr!=0;cntr++)
476 if (*cntr=='\\') backslashcount++;
479 DPRINT("%d \n",backslashcount);
480 for (;backslashcount;wcs--)
482 if (*wcs=='\\') backslashcount--;
486 wcscpy(wcs,filenameinfo->FileName);
488 size=((wcs-buf)+wcslen(filenameinfo->FileName))*sizeof(WCHAR);
491 RtlFreeHeap (RtlGetProcessHeap (),
495 /* append backslash if missing */
496 wcs = buf + size / sizeof(WCHAR) - 1;
501 size += sizeof(WCHAR);
504 memmove(cd->DosPath.Buffer,
506 size + sizeof(WCHAR));
507 cd->DosPath.Length = size;
513 if (cd->DosPath.Buffer[1]==':')
515 envvar.Length = 2 * swprintf (var, L"=%c:", cd->DosPath.Buffer[0]);
516 envvar.MaximumLength = 8;
519 RtlSetEnvironmentVariable(NULL,
524 RtlFreeHeap (RtlGetProcessHeap (),
528 RtlFreeHeap (RtlGetProcessHeap (),
534 return STATUS_SUCCESS;
541 RtlGetFullPathName_U(PWSTR DosName,
546 WCHAR *wcs, var[4], drive;
549 DWORD offs, sz, type;
550 UNICODE_STRING usvar, pfx;
553 WCHAR TempFullPathName[MAX_PATH] = L"";
555 DPRINT("RtlGetFullPathName_U %S %ld %p %p\n",
556 DosName, size, buf, FilePart);
558 if (!DosName || !*DosName)
561 len = wcslen (DosName);
563 /* strip trailing spaces */
564 while (len && DosName[len - 1] == L' ')
569 /* strip trailing path separator (but don't change '\') */
571 IS_PATH_SEPARATOR(DosName[len - 1]))
578 /* check for DOS device name */
579 sz = RtlIsDosDeviceName_U (DosName);
586 wcscpy (buf, L"\\\\.\\");
587 wcsncat (buf, DosName + offs, sz / sizeof(WCHAR));
592 type = RtlDetermineDosPathNameType_U (DosName);
596 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
597 DPRINT("type %ld\n", type);
600 case 1: /* \\xxx or \\.xxx */
601 case 6: /* \\.\xxx */
605 *DosName = RtlUpcaseUnicodeChar (*DosName);
609 drive = RtlUpcaseUnicodeChar (*DosName);
613 if (drive == RtlUpcaseUnicodeChar (cd->DosPath.Buffer[0]))
616 memcpy (TempFullPathName, cd->DosPath.Buffer, cd->DosPath.Length);
617 templen = cd->DosPath.Length / sizeof(WCHAR);
626 usvar.Length = 3 * sizeof(WCHAR);
627 usvar.MaximumLength = 4 * sizeof(WCHAR);
630 pfx.MaximumLength = MAX_PATH;
631 pfx.Buffer = TempFullPathName;
632 Status = RtlQueryEnvironmentVariable_U (NULL,
636 if (!NT_SUCCESS(Status))
639 if (Status == STATUS_BUFFER_TOO_SMALL)
640 return pfx.Length + len * 2 + 2;
641 memcpy (TempFullPathName, var, 6);
646 templen = pfx.Length / sizeof(WCHAR);
653 wcsncpy (TempFullPathName, cd->DosPath.Buffer, 2);
654 TempFullPathName[2] = 0;
655 templen = wcslen(TempFullPathName);
659 memcpy (TempFullPathName, cd->DosPath.Buffer, cd->DosPath.Length);
660 templen = cd->DosPath.Length / sizeof(WCHAR);
664 memcpy (TempFullPathName, L"\\\\.\\", 8);
674 DPRINT("TempFullPathName \'%S\' DosName \'%S\' len %ld\n", TempFullPathName, DosName, len);
675 /* add dosname to prefix */
676 memcpy (TempFullPathName + templen, DosName, len * sizeof(WCHAR));
678 TempFullPathName[len] = 0;
681 /* replace slashes */
682 wcs = wcschr(TempFullPathName, L'/');
686 wcs = wcschr(wcs + 1, L'/');
689 if (len == 2 && TempFullPathName[1] == L':')
691 TempFullPathName[len++] = L'\\';
692 TempFullPathName[len] = 0;
696 DPRINT("TempFullPathName \'%S\'\n", TempFullPathName);
697 RtlpEatPath (TempFullPathName);
698 DPRINT("TempFullPathName \'%S\'\n", TempFullPathName);
700 len = wcslen (TempFullPathName);
702 if (len < (size / sizeof(WCHAR)))
704 memcpy (buf, TempFullPathName, (len + 1) * sizeof(WCHAR));
709 *FilePart = wcsrchr(buf, L'\\');
721 return len * sizeof(WCHAR);
729 RtlDosPathNameToNtPathName_U(PWSTR dosname,
730 PUNICODE_STRING ntname,
741 WCHAR fullname[MAX_PATH + 1];
744 RtlAcquirePebLock ();
746 RtlInitUnicodeString (&us, dosname);
750 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
751 if (Buffer[0] == L'\\' && Buffer[1] == L'\\' &&
752 Buffer[2] == L'?' && Buffer[3] == L'\\')
754 // if( f_77F68606( &us, ntname, shortname, nah ) )
756 // RtlReleasePebLock ();
760 RtlReleasePebLock ();
765 Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
767 sizeof( fullname ) + MAX_PFX_SIZE);
770 RtlReleasePebLock ();
774 Size = RtlGetFullPathName_U (dosname,
778 if (Size == 0 || Size > MAX_PATH * sizeof(WCHAR))
780 RtlFreeHeap (RtlGetProcessHeap (),
783 RtlReleasePebLock ();
789 memcpy (Buffer, L"\\??\\", 4 * sizeof(WCHAR));
792 Type = RtlDetermineDosPathNameType_U (fullname);
796 memcpy (Buffer + tmpLength, L"UNC\\", 4 * sizeof(WCHAR));
805 Length = wcslen(fullname + Offset);
806 memcpy (Buffer + tmpLength, fullname + Offset, (Length + 1) * sizeof(WCHAR));
809 /* set NT filename */
810 ntname->Length = Length * sizeof(WCHAR);
811 ntname->MaximumLength = sizeof(fullname) + MAX_PFX_SIZE;
812 ntname->Buffer = Buffer;
814 /* set pointer to file part if possible */
815 if (FilePart && *FilePart)
816 *FilePart = Buffer + Length - wcslen (*FilePart);
818 /* Set name and handle structure if possible */
821 memset (nah, 0, sizeof(CURDIR));
822 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
823 if (Type == 5 && cd->Handle)
825 RtlInitUnicodeString(&us, fullname);
826 if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
828 Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
829 nah->DosPath.Buffer = Buffer + Length;
830 nah->DosPath.Length = ntname->Length - (Length * sizeof(WCHAR));
831 nah->DosPath.MaximumLength = nah->DosPath.Length;
832 nah->Handle = cd->Handle;
863 Type = RtlDetermineDosPathNameType_U (name);
867 Length = wcslen (sp);
868 Length += wcslen (name);
869 if (wcschr (name, L'.'))
872 Length += wcslen (ext);
874 full_name = (WCHAR*)RtlAllocateHeap (RtlGetProcessHeap (),
876 (Length + 1) * sizeof(WCHAR));
878 if (full_name != NULL)
884 while (*path && *path != L';')
888 if (wcs != full_name && *(wcs - 1) != L'\\')
893 if (RtlDoesFileExists_U (full_name))
895 Length = RtlGetFullPathName_U (full_name,
903 RtlFreeHeap (RtlGetProcessHeap (),
908 else if (RtlDoesFileExists_U (name))
910 Length = RtlGetFullPathName_U (name,
924 RtlDoesFileExists_U(IN PWSTR FileName)
926 UNICODE_STRING NtFileName;
927 OBJECT_ATTRIBUTES Attr;
932 /* only used by replacement code */
934 IO_STATUS_BLOCK StatusBlock;
936 if (!RtlDosPathNameToNtPathName_U (FileName,
942 /* don't forget to free it! */
943 Buffer = NtFileName.Buffer;
945 if (CurDir.DosPath.Length)
946 NtFileName = CurDir.DosPath;
950 InitializeObjectAttributes (&Attr,
952 OBJ_CASE_INSENSITIVE,
956 /* FIXME: not implemented yet */
957 // Status = NtQueryAttributesFile (&Attr, NULL);
959 /* REPLACEMENT start */
960 Status = NtOpenFile (&FileHandle,
965 FILE_SYNCHRONOUS_IO_NONALERT);
966 if (NT_SUCCESS(Status))
967 NtClose (FileHandle);
968 /* REPLACEMENT end */
970 RtlFreeHeap (RtlGetProcessHeap (),
974 if (NT_SUCCESS(Status) ||
975 Status == STATUS_SHARING_VIOLATION ||
976 Status == STATUS_ACCESS_DENIED)