1 /* $Id: path.c,v 1.26 2003/11/30 20:48:07 gdalsnes Exp $
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
;
384 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 /* FIXME: this code is a mess! We should use Wine's implementation
559 of this function, since it's cleaner and IMO better. -Gunnar */
561 if (!DosName
|| !*DosName
)
564 len
= wcslen (DosName
);
566 /* strip trailing spaces */
567 while (len
&& DosName
[len
- 1] == L
' ')
572 /* strip trailing path separator (but don't change '\') */
574 IS_PATH_SEPARATOR(DosName
[len
- 1]))
581 /* check for DOS device name */
582 sz
= RtlIsDosDeviceName_U (DosName
);
589 wcscpy (buf
, L
"\\\\.\\");
590 wcsncat (buf
, DosName
+ offs
, sz
/ sizeof(WCHAR
));
595 type
= RtlDetermineDosPathNameType_U (DosName
);
599 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectoryName
);
600 DPRINT("type %ld\n", type
);
603 case 1: /* \\xxx or \\.xxx */
604 case 6: /* \\.\xxx */
610 /* this makes a direct CreateFileW call crash! */
613 *DosName
= RtlUpcaseUnicodeChar (*DosName
);
618 drive
= RtlUpcaseUnicodeChar (*DosName
);
622 if (drive
== RtlUpcaseUnicodeChar (cd
->DosPath
.Buffer
[0]))
625 memcpy (TempFullPathName
, cd
->DosPath
.Buffer
, cd
->DosPath
.Length
);
626 templen
= cd
->DosPath
.Length
/ sizeof(WCHAR
);
635 usvar
.Length
= 3 * sizeof(WCHAR
);
636 usvar
.MaximumLength
= 4 * sizeof(WCHAR
);
639 pfx
.MaximumLength
= MAX_PATH
;
640 pfx
.Buffer
= TempFullPathName
;
641 Status
= RtlQueryEnvironmentVariable_U (NULL
,
645 if (!NT_SUCCESS(Status
))
648 if (Status
== STATUS_BUFFER_TOO_SMALL
)
649 return pfx
.Length
+ len
* 2 + 2;
651 TempFullPathName
[0] = drive
;
652 TempFullPathName
[1] = L
':';
653 TempFullPathName
[2] = L
'\\';
654 TempFullPathName
[3] = 0;
660 templen
= pfx
.Length
/ sizeof(WCHAR
);
667 wcsncpy (TempFullPathName
, cd
->DosPath
.Buffer
, 2);
668 TempFullPathName
[2] = 0;
669 templen
= wcslen(TempFullPathName
);
673 memcpy (TempFullPathName
, cd
->DosPath
.Buffer
, cd
->DosPath
.Length
);
674 templen
= cd
->DosPath
.Length
/ sizeof(WCHAR
);
678 memcpy (TempFullPathName
, L
"\\\\.\\", 8);
689 DPRINT("TempFullPathName \'%S\' DosName \'%S\' len %ld\n", TempFullPathName
, DosName
, len
);
690 /* add dosname to prefix */
691 memcpy (TempFullPathName
+ templen
, DosName
, len
* sizeof(WCHAR
));
693 /* dirty/temporary fix for the CreateFileW problem */
695 TempFullPathName
[0] = RtlUpcaseUnicodeChar(TempFullPathName
[0]);
698 TempFullPathName
[len
] = 0;
701 /* replace slashes */
702 wcs
= wcschr(TempFullPathName
, L
'/');
706 wcs
= wcschr(wcs
+ 1, L
'/');
709 if (len
== 2 && TempFullPathName
[1] == L
':')
711 TempFullPathName
[len
++] = L
'\\';
712 TempFullPathName
[len
] = 0;
716 DPRINT("TempFullPathName \'%S\'\n", TempFullPathName
);
717 RtlpEatPath (TempFullPathName
);
718 DPRINT("TempFullPathName \'%S\'\n", TempFullPathName
);
720 len
= wcslen (TempFullPathName
);
722 if (len
< (size
/ sizeof(WCHAR
)))
724 memcpy (buf
, TempFullPathName
, (len
+ 1) * sizeof(WCHAR
));
730 *FilePart
= wcsrchr(buf
, L
'\\');
740 *FilePart
= buf
+ len
;
741 while (*FilePart
!= buf
&& **FilePart
!= L
'\\')
743 if (**FilePart
== L
'\\')
749 return len
* sizeof(WCHAR
);
757 RtlDosPathNameToNtPathName_U(PWSTR dosname
,
758 PUNICODE_STRING ntname
,
769 WCHAR fullname
[MAX_PATH
+ 1];
773 RtlAcquirePebLock ();
775 RtlInitUnicodeString (&us
, dosname
);
779 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
780 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
781 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
783 // if( f_77F68606( &us, ntname, shortname, nah ) )
785 // RtlReleasePebLock ();
789 RtlReleasePebLock ();
794 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
796 sizeof( fullname
) + MAX_PFX_SIZE
);
799 RtlReleasePebLock ();
803 Size
= RtlGetFullPathName_U (dosname
,
807 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
809 RtlFreeHeap (RtlGetProcessHeap (),
812 RtlReleasePebLock ();
818 memcpy (Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
821 Type
= RtlDetermineDosPathNameType_U (fullname
);
825 memcpy (Buffer
+ tmpLength
, L
"UNC\\", 4 * sizeof(WCHAR
));
834 Length
= wcslen(fullname
+ Offset
);
835 memcpy (Buffer
+ tmpLength
, fullname
+ Offset
, (Length
+ 1) * sizeof(WCHAR
));
838 /* set NT filename */
839 ntname
->Length
= Length
* sizeof(WCHAR
);
840 ntname
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
841 ntname
->Buffer
= Buffer
;
843 /* set pointer to file part if possible */
844 if (FilePart
&& *FilePart
)
845 *FilePart
= Buffer
+ Length
- wcslen (*FilePart
);
847 /* Set name and handle structure if possible */
850 memset (nah
, 0, sizeof(CURDIR
));
851 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectoryName
);
852 if (Type
== 5 && cd
->Handle
)
854 RtlInitUnicodeString(&us
, fullname
);
855 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
857 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
858 nah
->DosPath
.Buffer
= Buffer
+ Length
;
859 nah
->DosPath
.Length
= ntname
->Length
- (Length
* sizeof(WCHAR
));
860 nah
->DosPath
.MaximumLength
= nah
->DosPath
.Length
;
861 nah
->Handle
= cd
->Handle
;
891 Type
= RtlDetermineDosPathNameType_U (name
);
895 Length
= wcslen (sp
);
896 Length
+= wcslen (name
);
897 if (wcschr (name
, L
'.'))
900 Length
+= wcslen (ext
);
902 full_name
= (WCHAR
*)RtlAllocateHeap (RtlGetProcessHeap (),
904 (Length
+ 1) * sizeof(WCHAR
));
906 if (full_name
!= NULL
)
912 while (*path
&& *path
!= L
';')
916 if (wcs
!= full_name
&& *(wcs
- 1) != L
'\\')
921 if (RtlDoesFileExists_U (full_name
))
923 Length
= RtlGetFullPathName_U (full_name
,
931 RtlFreeHeap (RtlGetProcessHeap (),
936 else if (RtlDoesFileExists_U (name
))
938 Length
= RtlGetFullPathName_U (name
,
952 RtlDoesFileExists_U(IN PWSTR FileName
)
954 UNICODE_STRING NtFileName
;
955 OBJECT_ATTRIBUTES Attr
;
960 /* only used by replacement code */
962 IO_STATUS_BLOCK StatusBlock
;
964 if (!RtlDosPathNameToNtPathName_U (FileName
,
970 /* don't forget to free it! */
971 Buffer
= NtFileName
.Buffer
;
973 if (CurDir
.DosPath
.Length
)
974 NtFileName
= CurDir
.DosPath
;
978 InitializeObjectAttributes (&Attr
,
980 OBJ_CASE_INSENSITIVE
,
984 /* FIXME: not implemented yet */
985 // Status = NtQueryAttributesFile (&Attr, NULL);
987 /* REPLACEMENT start */
988 Status
= NtOpenFile (&FileHandle
,
993 FILE_SYNCHRONOUS_IO_NONALERT
);
994 if (NT_SUCCESS(Status
))
995 NtClose (FileHandle
);
996 /* REPLACEMENT end */
998 RtlFreeHeap (RtlGetProcessHeap (),
1002 if (NT_SUCCESS(Status
) ||
1003 Status
== STATUS_SHARING_VIOLATION
||
1004 Status
== STATUS_ACCESS_DENIED
)