1 /* $Id: path.c,v 1.13 2002/09/07 15:12:40 chorns 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 #define NTOS_USER_MODE
22 /* DEFINITONS and MACROS ******************************************************/
24 #define MAX_PFX_SIZE 16
26 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
29 /* FUNCTIONS *****************************************************************/
31 static ULONG
RtlpGetDotSequence (PWSTR p
)
39 else if ((*p
== '\\' || *p
== '\0') && Count
)
49 static VOID
RtlpEatPath (PWSTR Path
)
56 while ((*p
) != 0 || ((*p
) == L
'\\' && (*(p
+1)) == 0))
60 DotLen
= RtlpGetDotSequence (p
+1);
61 DPRINT("DotSequenceLength %u\n", DotLen
);
62 DPRINT("prev %S p %S\n",prev
,p
);
71 while ((*p
) != 0 && (*p
) != L
'\\');
83 while (n
> 0 && prev
> (Path
+ 2))
91 if (*(p
+ DotLen
+ 1) == 0)
94 wcscpy (prev
, p
+ DotLen
+ 1);
96 if (prev
> (Path
+ 2))
99 while ((*prev
) != L
'\\')
109 ULONG STDCALL
RtlGetLongestNtPathLength (VOID
)
111 return (MAX_PATH
+ 9);
116 RtlDetermineDosPathNameType_U(PWSTR Path
)
118 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
125 if (IS_PATH_SEPARATOR(Path
[0]))
127 if (!IS_PATH_SEPARATOR(Path
[1]))
133 return 1; /* \\xxx */
135 if (IS_PATH_SEPARATOR(Path
[3]))
136 return 6; /* \\.\xxx */
139 return 1; /* \\.xxxx */
148 if (IS_PATH_SEPARATOR(Path
[2]))
149 return 2; /* x:\xxx */
151 return 3; /* x:xxx */
156 /* returns 0 if name is not valid DOS device name, or DWORD with
157 * offset in bytes to DOS device name from beginning of buffer in high word
158 * and size in bytes of DOS device name in low word */
161 RtlIsDosDeviceName_U(PWSTR DeviceName
)
168 if (DeviceName
== NULL
)
173 while (DeviceName
[Length
])
178 Type
= RtlDetermineDosPathNameType_U(DeviceName
);
187 !_wcsnicmp (DeviceName
, L
"\\\\.\\CON", 7))
192 /* name can end with ':' */
193 if (Length
&& DeviceName
[Length
- 1 ] == L
':')
198 /* there can be spaces or points at the end of name */
199 wc
= DeviceName
+ Length
- 1;
200 while (Length
&& (*wc
== L
'.' || *wc
== L
' '))
206 /* let's find a beginning of name */
207 wc
= DeviceName
+ Length
- 1;
208 while (wc
> DeviceName
&& !IS_PATH_SEPARATOR(*(wc
- 1)))
212 Offset
= wc
- DeviceName
;
215 /* check for LPTx or COMx */
216 if (Length
== 4 && wc
[3] >= L
'0' && wc
[3] <= L
'9')
223 if (!_wcsnicmp (wc
, L
"LPT", 3) ||
224 !_wcsnicmp (wc
, L
"COM", 3))
226 return ((Offset
* 2) << 16 ) | 8;
231 /* check for PRN,AUX,NUL or CON */
233 (!_wcsnicmp (wc
, L
"PRN", 3) ||
234 !_wcsnicmp (wc
, L
"AUX", 3) ||
235 !_wcsnicmp (wc
, L
"NUL", 3) ||
236 !_wcsnicmp (wc
, L
"CON", 3)))
238 return ((Offset
* 2) << 16) | 6;
246 RtlGetCurrentDirectory_U(ULONG MaximumLength
,
252 DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
254 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
257 Length
= cd
->DosPath
.Length
/ sizeof(WCHAR
);
258 if (cd
->DosPath
.Buffer
[Length
- 1] == L
'\\' &&
259 cd
->DosPath
.Buffer
[Length
- 2] != L
':')
262 DPRINT ("cd->DosPath.Buffer %S Length %d\n",
263 cd
->DosPath
.Buffer
, Length
);
265 if (MaximumLength
/ sizeof(WCHAR
) > Length
)
269 Length
* sizeof(WCHAR
));
277 RtlReleasePebLock ();
279 DPRINT ("CurrentDirectory %S\n", Buffer
);
281 return (Length
* sizeof(WCHAR
));
286 RtlSetCurrentDirectory_U(PUNICODE_STRING name
)
289 UNICODE_STRING envvar
;
290 OBJECT_ATTRIBUTES Attr
;
291 IO_STATUS_BLOCK iosb
;
295 HANDLE handle
= NULL
;
298 PFILE_NAME_INFORMATION filenameinfo
;
299 ULONG backslashcount
= 0;
303 DPRINT ("RtlSetCurrentDirectory %wZ\n", name
);
305 RtlAcquirePebLock ();
306 cd
= &NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
;
307 size
= cd
->DosPath
.MaximumLength
;
309 buf
= RtlAllocateHeap (RtlGetProcessHeap(),
314 RtlReleasePebLock ();
315 return STATUS_NO_MEMORY
;
318 size
= RtlGetFullPathName_U (name
->Buffer
, size
, buf
, 0);
321 RtlFreeHeap (RtlGetProcessHeap (),
324 RtlReleasePebLock ();
325 return STATUS_OBJECT_NAME_INVALID
;
328 if (!RtlDosPathNameToNtPathName_U (buf
, &full
, 0, 0))
330 RtlFreeHeap (RtlGetProcessHeap (),
333 RtlFreeHeap (RtlGetProcessHeap (),
336 RtlReleasePebLock ();
337 return STATUS_OBJECT_NAME_INVALID
;
340 InitializeObjectAttributes (&Attr
,
342 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
346 Status
= NtOpenFile (&handle
,
347 SYNCHRONIZE
| FILE_TRAVERSE
,
350 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
351 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
352 if (!NT_SUCCESS(Status
))
354 RtlFreeHeap (RtlGetProcessHeap (),
357 RtlFreeHeap (RtlGetProcessHeap (),
360 RtlReleasePebLock ();
364 filenameinfo
= RtlAllocateHeap(RtlGetProcessHeap(),
366 MAX_PATH
*sizeof(WCHAR
)+sizeof(ULONG
));
368 Status
= NtQueryInformationFile(handle
,
371 MAX_PATH
*sizeof(WCHAR
)+sizeof(ULONG
),
372 FileNameInformation
);
373 if (!NT_SUCCESS(Status
))
375 RtlFreeHeap(RtlGetProcessHeap(),
378 RtlFreeHeap(RtlGetProcessHeap(),
381 RtlFreeHeap(RtlGetProcessHeap(),
388 if (filenameinfo
->FileName
[1]) // If it's just "\", we need special handling
390 wcs
= buf
+ size
/ sizeof(WCHAR
) - 1;
395 size
-= sizeof(WCHAR
);
398 for (cntr
=filenameinfo
->FileName
;*cntr
!=0;cntr
++)
400 if (*cntr
=='\\') backslashcount
++;
403 DPRINT("%d \n",backslashcount
);
404 for (;backslashcount
;wcs
--)
406 if (*wcs
=='\\') backslashcount
--;
410 wcscpy(wcs
,filenameinfo
->FileName
);
412 size
=((wcs
-buf
)+wcslen(filenameinfo
->FileName
))*sizeof(WCHAR
);
415 RtlFreeHeap (RtlGetProcessHeap (),
419 /* append backslash if missing */
420 wcs
= buf
+ size
/ sizeof(WCHAR
) - 1;
425 size
+= sizeof(WCHAR
);
428 memmove(cd
->DosPath
.Buffer
,
430 size
+ sizeof(WCHAR
));
431 cd
->DosPath
.Length
= size
;
437 if (cd
->DosPath
.Buffer
[1]==':')
439 envvar
.Length
= 2 * swprintf (var
, L
"=%c:", cd
->DosPath
.Buffer
[0]);
440 envvar
.MaximumLength
= 8;
443 RtlSetEnvironmentVariable(NULL
,
448 RtlFreeHeap (RtlGetProcessHeap (),
452 RtlFreeHeap (RtlGetProcessHeap (),
458 return STATUS_SUCCESS
;
463 RtlGetFullPathName_U(PWSTR DosName
,
468 WCHAR
*wcs
, var
[4], drive
;
471 DWORD offs
, sz
, type
;
472 UNICODE_STRING usvar
, pfx
;
476 DPRINT("RtlGetFullPathName_U %S %ld %p %p\n",
477 DosName
, size
, buf
, FilePart
);
479 if (!DosName
|| !*DosName
)
482 len
= wcslen (DosName
);
484 /* strip trailing spaces */
485 while (len
&& DosName
[len
- 1] == L
' ')
491 /* strip trailing path separator (but don't change '\') */
493 IS_PATH_SEPARATOR(DosName
[len
- 1]))
497 memset (buf
, 0, size
);
500 /* check for DOS device name */
501 sz
= RtlIsDosDeviceName_U (DosName
);
508 wcscpy (buf
, L
"\\\\.\\");
509 wcsncat (buf
, DosName
+ offs
, sz
/ sizeof(WCHAR
));
514 type
= RtlDetermineDosPathNameType_U (DosName
);
518 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
519 DPRINT("type %ld\n", type
);
522 case 1: /* \\xxx or \\.xxx */
523 case 6: /* \\.\xxx */
527 *DosName
= towupper (*DosName
);
531 drive
= towupper (*DosName
);
535 if (drive
== towupper (cd
->DosPath
.Buffer
[0]))
538 wcscpy (buf
, cd
->DosPath
.Buffer
);
543 usvar
.Length
= 2 * swprintf (var
, L
"=%c:", drive
);
544 usvar
.MaximumLength
= 8;
547 pfx
.MaximumLength
= size
;
549 Status
= RtlQueryEnvironmentVariable_U (NULL
,
553 if (!NT_SUCCESS(Status
))
556 if (Status
== STATUS_BUFFER_TOO_SMALL
)
557 return pfx
.Length
+ len
* 2 + 2;
558 swprintf (buf
, L
"%c:\\", drive
);
564 wcsncpy (buf
, cd
->DosPath
.Buffer
, 2);
568 wcscpy (buf
, cd
->DosPath
.Buffer
);
572 wcscpy (buf
, L
"\\\\.\\");
580 DPRINT("buf \'%S\' DosName \'%S\' len %ld\n", buf
, DosName
, len
);
581 /* add dosname to prefix */
582 wcsncat (buf
, DosName
, len
);
585 /* replace slashes */
586 for (wcs
= buf
; *wcs
; wcs
++)
591 if (len
< 3 && buf
[len
-1] == L
':')
594 DPRINT("buf \'%S\'\n", buf
);
596 DPRINT("buf \'%S\'\n", buf
);
603 for (wcs
= buf
+ len
- 1; wcs
>= buf
; wcs
--)
615 return len
* sizeof(WCHAR
);
620 RtlDosPathNameToNtPathName_U(PWSTR dosname
,
621 PUNICODE_STRING ntname
,
631 WCHAR fullname
[2*MAX_PATH
];
634 RtlAcquirePebLock ();
636 RtlInitUnicodeString (&us
, dosname
);
640 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
641 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
642 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
644 // if( f_77F68606( &us, ntname, shortname, nah ) )
646 // RtlReleasePebLock ();
650 RtlReleasePebLock ();
655 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
657 sizeof( fullname
) + MAX_PFX_SIZE
);
660 RtlReleasePebLock ();
664 Size
= RtlGetFullPathName_U (dosname
,
668 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
670 RtlFreeHeap (RtlGetProcessHeap (),
673 RtlReleasePebLock ();
679 wcscpy (Buffer
, L
"\\??\\");
681 Type
= RtlDetermineDosPathNameType_U (fullname
);
685 wcscat (Buffer
, L
"UNC\\");
693 wcscat (Buffer
, fullname
+ Offset
);
694 Length
= wcslen (Buffer
);
696 /* set NT filename */
697 ntname
->Length
= Length
* sizeof(WCHAR
);
698 ntname
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
699 ntname
->Buffer
= Buffer
;
701 /* set pointer to file part if possible */
702 if (FilePart
&& *FilePart
)
703 *FilePart
= Buffer
+ Length
- wcslen (*FilePart
);
705 /* Set name and handle structure if possible */
708 memset (nah
, 0, sizeof(CURDIR
));
709 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
710 if (Type
== 5 && cd
->Handle
&&
711 !_wcsnicmp (cd
->DosPath
.Buffer
, fullname
, cd
->DosPath
.Length
/ 2))
713 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
714 nah
->DosPath
.Buffer
= Buffer
+ Length
;
715 nah
->DosPath
.Length
= ntname
->Length
- (Length
* sizeof(WCHAR
));
716 nah
->DosPath
.MaximumLength
= nah
->DosPath
.Length
;
717 nah
->Handle
= cd
->Handle
;
744 Type
= RtlDetermineDosPathNameType_U (name
);
748 Length
= wcslen (sp
);
749 Length
+= wcslen (name
);
750 if (wcschr (name
, L
'.'))
753 Length
+= wcslen (ext
);
755 full_name
= (WCHAR
*)RtlAllocateHeap (RtlGetProcessHeap (),
757 (Length
+ 1) * sizeof(WCHAR
));
759 if (full_name
!= NULL
)
765 while (*path
&& *path
!= L
';')
769 if (wcs
!= full_name
&& *(wcs
- 1) != L
'\\')
774 if (RtlDoesFileExists_U (full_name
))
776 Length
= RtlGetFullPathName_U (full_name
,
784 RtlFreeHeap (RtlGetProcessHeap (),
789 else if (RtlDoesFileExists_U (name
))
791 Length
= RtlGetFullPathName_U (name
,
802 RtlDoesFileExists_U(IN PWSTR FileName
)
804 UNICODE_STRING NtFileName
;
805 OBJECT_ATTRIBUTES Attr
;
810 /* only used by replacement code */
812 IO_STATUS_BLOCK StatusBlock
;
814 if (!RtlDosPathNameToNtPathName_U (FileName
,
820 /* don't forget to free it! */
821 Buffer
= NtFileName
.Buffer
;
823 if (CurDir
.DosPath
.Length
)
824 NtFileName
= CurDir
.DosPath
;
828 InitializeObjectAttributes (&Attr
,
830 OBJ_CASE_INSENSITIVE
,
834 /* FIXME: not implemented yet */
835 // Status = NtQueryAttributesFile (&Attr, NULL);
837 /* REPLACEMENT start */
838 Status
= NtOpenFile (&FileHandle
,
843 FILE_SYNCHRONOUS_IO_NONALERT
);
844 if (NT_SUCCESS(Status
))
845 NtClose (FileHandle
);
846 /* REPLACEMENT end */
848 RtlFreeHeap (RtlGetProcessHeap (),
852 if (NT_SUCCESS(Status
) ||
853 Status
== STATUS_SHARING_VIOLATION
||
854 Status
== STATUS_ACCESS_DENIED
)