1 /* $Id: path.c,v 1.7 2001/03/07 22:29:09 cnettel 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>
20 #include <ntdll/ntdll.h>
22 /* DEFINITONS and MACROS ******************************************************/
24 #define MAX_PFX_SIZE 16
26 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
29 /* FUNCTIONS *****************************************************************/
32 static ULONG
RtlpGetDotSequence (PWSTR p
)
40 else if ((*p
== '\\' || *p
== '\0') && Count
)
50 static VOID
RtlpEatPath (PWSTR Path
)
57 while ((*p
) != 0 || ((*p
) == L
'\\' && (*(p
+1)) == 0))
61 DotLen
= RtlpGetDotSequence (p
+1);
62 DPRINT("DotSequenceLength %u\n", DotLen
);
63 DPRINT("prev %S p %S\n",prev
,p
);
72 while ((*p
) != 0 && (*p
) != L
'\\');
84 while (n
> 0 && prev
> (Path
+ 2))
92 if (*(p
+ DotLen
+ 1) == 0)
95 wcscpy (prev
, p
+ DotLen
+ 1);
97 if (prev
> (Path
+ 2))
100 while ((*prev
) != L
'\\')
110 ULONG STDCALL
RtlGetLongestNtPathLength (VOID
)
112 return (MAX_PATH
+ 9);
118 RtlDetermineDosPathNameType_U (
122 // DPRINT ("RtlDetermineDosPathNameType_U %S\n", Path);
127 if (IS_PATH_SEPARATOR(Path
[0]))
129 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 (
170 if (DeviceName
== NULL
)
173 while (DeviceName
[Length
])
176 Type
= RtlDetermineDosPathNameType_U (DeviceName
);
182 !_wcsnicmp (DeviceName
, L
"\\\\.\\CON", 7))
187 /* name can end with ':' */
188 if (Length
&& DeviceName
[Length
- 1 ] == L
':')
191 /* there can be spaces or points at the end of name */
192 wc
= DeviceName
+ Length
- 1;
193 while (Length
&& (*wc
== L
'.' || *wc
== L
' '))
199 /* let's find a beginning of name */
200 wc
= DeviceName
+ Length
- 1;
201 while (wc
> DeviceName
&& !IS_PATH_SEPARATOR(*(wc
- 1)))
203 Offset
= wc
- DeviceName
;
206 /* check for LPTx or COMx */
207 if (Length
== 4 && wc
[3] >= L
'0' && wc
[3] <= L
'9')
211 if (!_wcsnicmp (wc
, L
"LPT", 3) ||
212 !_wcsnicmp (wc
, L
"COM", 3))
214 return ((Offset
* 2) << 16 ) | 8;
219 /* check for PRN,AUX,NUL or CON */
221 (!_wcsnicmp (wc
, L
"PRN", 3) ||
222 !_wcsnicmp (wc
, L
"AUX", 3) ||
223 !_wcsnicmp (wc
, L
"NUL", 3) ||
224 !_wcsnicmp (wc
, L
"CON", 3)))
226 return ((Offset
* 2) << 16) | 6;
235 RtlGetCurrentDirectory_U (
243 // DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
245 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
248 Length
= cd
->DosPath
.Length
/ sizeof(WCHAR
);
249 if (cd
->DosPath
.Buffer
[Length
- 1] == L
'\\' &&
250 cd
->DosPath
.Buffer
[Length
- 2] != L
':')
253 // DPRINT ("cd->DosPath.Buffer %S Length %d\n",
254 // cd->DosPath.Buffer, Length);
256 if (MaximumLength
/ sizeof(WCHAR
) > Length
)
260 Length
* sizeof(WCHAR
));
268 RtlReleasePebLock ();
270 DPRINT ("CurrentDirectory %S\n", Buffer
);
272 return (Length
* sizeof(WCHAR
));
276 NTSTATUS STDCALL
RtlSetCurrentDirectory_U (PUNICODE_STRING name
)
279 OBJECT_ATTRIBUTES Attr
;
280 IO_STATUS_BLOCK iosb
;
284 HANDLE handle
= NULL
;
286 PWSTR devpathstart
, loopvar
;
288 PFILE_NAME_INFORMATION objnameinfo
;
291 DPRINT ("RtlSetCurrentDirectory %wZ\n", name
);
293 RtlAcquirePebLock ();
294 cd
= &NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
;
295 size
= cd
->DosPath
.MaximumLength
;
297 buf
= RtlAllocateHeap (RtlGetProcessHeap(),
302 RtlReleasePebLock ();
303 return STATUS_NO_MEMORY
;
306 size
= RtlGetFullPathName_U (name
->Buffer
, size
, buf
, 0);
309 RtlFreeHeap (RtlGetProcessHeap (),
312 RtlReleasePebLock ();
313 return STATUS_OBJECT_NAME_INVALID
;
316 if (!RtlDosPathNameToNtPathName_U (buf
, &full
, 0, 0))
318 RtlFreeHeap (RtlGetProcessHeap (),
321 RtlFreeHeap (RtlGetProcessHeap (),
324 RtlReleasePebLock ();
325 return STATUS_OBJECT_NAME_INVALID
;
328 InitializeObjectAttributes (&Attr
,
330 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
334 Status
= NtOpenFile (&handle
,
335 SYNCHRONIZE
| FILE_TRAVERSE
,
338 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
339 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
341 objnameinfo
= RtlAllocateHeap (RtlGetProcessHeap(),
343 cd
->DosPath
.MaximumLength
*sizeof(WCHAR
)+4); // A little margin, for Length pre-fix
345 NtQueryInformationFile(handle
,NULL
,objnameinfo
,sizeof(OBJECT_NAME_INFORMATION
),FileNameInformation
);
347 devpathstart
=buf
+wcslen(buf
);
349 for (loopvar
=objnameinfo
->FileName
;(*loopvar
);loopvar
++)
350 if ((*loopvar
)=='\\')
353 for (;devpathstart
>buf
&& (*devpathstart
)!='\\';devpathstart
--) {}
356 wcscpy(devpathstart
, objnameinfo
->FileName
);
359 RtlFreeHeap (RtlGetProcessHeap (),
362 if (!NT_SUCCESS(Status
))
364 RtlFreeHeap (RtlGetProcessHeap (),
367 RtlFreeHeap (RtlGetProcessHeap (),
370 RtlReleasePebLock ();
374 /* append backslash if missing */
375 wcs
= buf
+ size
/ sizeof(WCHAR
) - 1;
380 size
+= sizeof(WCHAR
);
383 memmove (cd
->DosPath
.Buffer
,
385 size
+ sizeof(WCHAR
));
386 cd
->DosPath
.Length
= size
;
389 NtClose (cd
->Handle
);
392 RtlFreeHeap (RtlGetProcessHeap (),
396 RtlFreeHeap (RtlGetProcessHeap (),
402 return STATUS_SUCCESS
;
408 RtlGetFullPathName_U (
415 WCHAR
*wcs
, var
[4], drive
;
417 DWORD offs
, sz
, type
;
418 UNICODE_STRING usvar
, pfx
;
422 // DPRINT("RtlGetFullPathName_U %S %ld %p %p\n",
423 // DosName, size, buf, FilePart);
425 if (!DosName
|| !*DosName
)
428 len
= wcslen (DosName
);
430 /* strip trailing spaces */
431 while (len
&& DosName
[len
- 1] == L
' ')
436 /* strip trailing path separator (but don't change '\') */
438 IS_PATH_SEPARATOR(DosName
[len
- 1]))
442 memset (buf
, 0, size
);
445 /* check for DOS device name */
446 sz
= RtlIsDosDeviceName_U (DosName
);
453 wcscpy (buf
, L
"\\\\.\\");
454 wcsncat (buf
, DosName
+ offs
, sz
/ sizeof(WCHAR
));
459 type
= RtlDetermineDosPathNameType_U (DosName
);
463 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
464 //DPRINT("type %ld\n", type);
467 case 1: /* \\xxx or \\.xxx */
468 case 6: /* \\.\xxx */
472 *DosName
= towupper (*DosName
);
476 drive
= towupper (*DosName
);
480 if (drive
== towupper (cd
->DosPath
.Buffer
[0]))
483 wcscpy (buf
, cd
->DosPath
.Buffer
);
488 usvar
.Length
= 2 * swprintf (var
, L
"=%c:", drive
);
489 usvar
.MaximumLength
= 8;
492 pfx
.MaximumLength
= size
;
494 Status
= RtlQueryEnvironmentVariable_U (NULL
,
498 if (!NT_SUCCESS(Status
))
501 if (Status
== STATUS_BUFFER_TOO_SMALL
)
502 return pfx
.Length
+ len
* 2 + 2;
503 swprintf (buf
, L
"%c:\\", drive
);
511 buf
[pfx
.Length
/ 2] = L
'\\';
519 wcsncpy (buf
, cd
->DosPath
.Buffer
, 2);
523 wcscpy (buf
, cd
->DosPath
.Buffer
);
527 wcscpy (buf
, L
"\\\\.\\");
535 // DPRINT("buf \'%S\' DosName \'%S\' len %ld\n", buf, DosName, len);
536 /* add dosname to prefix */
537 wcsncat (buf
, DosName
, len
);
540 /* replace slashes */
541 for (wcs
= buf
; *wcs
; wcs
++)
546 if (len
< 3 && buf
[len
-1] == L
':')
549 // DPRINT("buf \'%S\'\n", buf);
551 // DPRINT("buf \'%S\'\n", buf);
558 for (wcs
= buf
+ len
- 1; wcs
>= buf
; wcs
--)
570 return len
* sizeof(WCHAR
);
576 RtlDosPathNameToNtPathName_U (
578 PUNICODE_STRING ntname
,
589 WCHAR fullname
[2*MAX_PATH
];
592 RtlAcquirePebLock ();
594 RtlInitUnicodeString (&us
, dosname
);
598 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
599 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
600 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
602 // if( f_77F68606( &us, ntname, shortname, nah ) )
604 // RtlReleasePebLock ();
608 RtlReleasePebLock ();
613 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
615 sizeof( fullname
) + MAX_PFX_SIZE
);
618 RtlReleasePebLock ();
622 Size
= RtlGetFullPathName_U (dosname
,
626 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
628 RtlFreeHeap (RtlGetProcessHeap (),
631 RtlReleasePebLock ();
637 wcscpy (Buffer
, L
"\\??\\");
639 Type
= RtlDetermineDosPathNameType_U (fullname
);
643 wcscat (Buffer
, L
"UNC\\");
651 wcscat (Buffer
, fullname
+ Offset
);
652 Length
= wcslen (Buffer
);
654 /* set NT filename */
655 ntname
->Length
= Length
* sizeof(WCHAR
);
656 ntname
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
657 ntname
->Buffer
= Buffer
;
659 /* set pointer to file part if possible */
660 if (FilePart
&& *FilePart
)
661 *FilePart
= Buffer
+ Length
- wcslen (*FilePart
);
663 /* Set name and handle structure if possible */
666 memset (nah
, 0, sizeof(CURDIR
));
667 cd
= &(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
);
668 if (Type
== 5 && cd
->Handle
&&
669 !_wcsnicmp (cd
->DosPath
.Buffer
, fullname
, cd
->DosPath
.Length
/ 2))
671 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
672 nah
->DosPath
.Buffer
= Buffer
+ Length
;
673 nah
->DosPath
.Length
= ntname
->Length
- (Length
* sizeof(WCHAR
));
674 nah
->DosPath
.MaximumLength
= nah
->DosPath
.Length
;
675 nah
->Handle
= cd
->Handle
;
702 Type
= RtlDetermineDosPathNameType_U (name
);
706 Length
= wcslen (sp
);
707 Length
+= wcslen (name
);
708 if (wcschr (name
, L
'.'))
711 Length
+= wcslen (ext
);
713 full_name
= (WCHAR
*)RtlAllocateHeap (RtlGetProcessHeap (),
715 (Length
+ 1) * sizeof(WCHAR
));
717 if (full_name
!= NULL
)
723 while (*path
&& *path
!= L
';')
727 if (wcs
!= full_name
&& *(wcs
- 1) != L
'\\')
732 if (RtlDoesFileExists_U (full_name
))
734 Length
= RtlGetFullPathName_U (full_name
,
742 RtlFreeHeap (RtlGetProcessHeap (),
747 else if (RtlDoesFileExists_U (name
))
749 Length
= RtlGetFullPathName_U (name
,
761 RtlIsNameLegalDOS8Dot3 (
762 PUNICODE_STRING UnicodeName
,
763 PANSI_STRING AnsiName
,
767 PANSI_STRING name
= AnsiName
;
768 ANSI_STRING DummyString
;
774 BOOLEAN HasSpace
= FALSE
;
775 BOOLEAN HasDot
= FALSE
;
777 if (UnicodeName
->Length
> 24)
778 return FALSE
; /* name too long */
784 name
->MaximumLength
= 12;
785 name
->Buffer
= Buffer
;
788 Status
= RtlUpcaseUnicodeStringToCountedOemString (name
,
791 if (!NT_SUCCESS(Status
))
794 Length
= name
->Length
;
797 if (!(Length
== 1 && *str
== '.') &&
798 !(Length
== 2 && *str
== '.' && *(str
+ 1) == '.'))
800 for (i
= 0; i
< Length
; i
++, str
++)
809 if ((HasDot
) || /* two points */
810 (!i
) || /* point is first char */
811 (i
+ 1 == Length
) ||/* point is last char */
812 (Length
- i
> 4)) /* more than 3 chars of extension */
821 *SpacesFound
= HasSpace
;
829 RtlDoesFileExists_U (
833 UNICODE_STRING NtFileName
;
834 OBJECT_ATTRIBUTES Attr
;
839 /* only used by replacement code */
841 IO_STATUS_BLOCK StatusBlock
;
843 if (!RtlDosPathNameToNtPathName_U (FileName
,
849 /* don't forget to free it! */
850 Buffer
= NtFileName
.Buffer
;
852 if (CurDir
.DosPath
.Length
)
853 NtFileName
= CurDir
.DosPath
;
857 InitializeObjectAttributes (&Attr
,
859 OBJ_CASE_INSENSITIVE
,
863 /* FIXME: not implemented yet */
864 // Status = NtQueryAttributesFile (&Attr, NULL);
866 /* REPLACEMENT start */
867 Status
= NtOpenFile (&FileHandle
,
872 FILE_SYNCHRONOUS_IO_NONALERT
);
873 if (NT_SUCCESS(Status
))
874 NtClose (FileHandle
);
875 /* REPLACEMENT end */
877 RtlFreeHeap (RtlGetProcessHeap (),
881 if (NT_SUCCESS(Status
) ||
882 Status
== STATUS_SHARING_VIOLATION
||
883 Status
== STATUS_ACCESS_DENIED
)