2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: Path and current directory functions
9 /* INCLUDES *****************************************************************/
16 /* DEFINITONS and MACROS ******************************************************/
18 #define MAX_PFX_SIZE 16
20 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
23 /* GLOBALS ********************************************************************/
25 static const WCHAR DeviceRootW
[] = L
"\\\\.\\";
27 static const UNICODE_STRING _condev
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
29 static const UNICODE_STRING _lpt
= RTL_CONSTANT_STRING(L
"LPT");
31 static const UNICODE_STRING _com
= RTL_CONSTANT_STRING(L
"COM");
33 static const UNICODE_STRING _prn
= RTL_CONSTANT_STRING(L
"PRN");
35 static const UNICODE_STRING _aux
= RTL_CONSTANT_STRING(L
"AUX");
37 static const UNICODE_STRING _con
= RTL_CONSTANT_STRING(L
"CON");
39 static const UNICODE_STRING _nul
= RTL_CONSTANT_STRING(L
"NUL");
41 /* FUNCTIONS *****************************************************************/
47 ULONG NTAPI
RtlGetLongestNtPathLength (VOID
)
49 return (MAX_PATH
+ 9);
58 RtlDetermineDosPathNameType_U(PCWSTR Path
)
60 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
64 return RtlPathTypeUnknown
;
67 if (IS_PATH_SEPARATOR(Path
[0]))
69 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \xxx */
70 if (Path
[2] != L
'.') return RtlPathTypeUncAbsolute
; /* \\xxx */
71 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\xxx */
72 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.xxxx */
74 return RtlPathTypeRootLocalDevice
; /* \\. */
78 if (!Path
[0] || Path
[1] != L
':') return RtlPathTypeRelative
; /* xxx */
79 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\xxx */
81 return RtlPathTypeDriveRelative
; /* x:xxx */
86 /* returns 0 if name is not valid DOS device name, or DWORD with
87 * offset in bytes to DOS device name from beginning of buffer in high word
88 * and size in bytes of DOS device name in low word */
94 RtlIsDosDeviceName_U(PWSTR dos_name
)
96 static const WCHAR consoleW
[] = {'\\','\\','.','\\','C','O','N',0};
97 static const WCHAR auxW
[3] = {'A','U','X'};
98 static const WCHAR comW
[3] = {'C','O','M'};
99 static const WCHAR conW
[3] = {'C','O','N'};
100 static const WCHAR lptW
[3] = {'L','P','T'};
101 static const WCHAR nulW
[3] = {'N','U','L'};
102 static const WCHAR prnW
[3] = {'P','R','N'};
104 const WCHAR
*start
, *end
, *p
;
106 switch(RtlDetermineDosPathNameType_U( dos_name
))
108 case RtlPathTypeUnknown
:
109 case RtlPathTypeUncAbsolute
:
111 case RtlPathTypeLocalDevice
:
112 if (!_wcsicmp( dos_name
, consoleW
))
113 return MAKELONG( sizeof(conW
), 4 * sizeof(WCHAR
) ); /* 4 is length of \\.\ prefix */
119 end
= dos_name
+ wcslen(dos_name
) - 1;
120 while (end
>= dos_name
&& *end
== ':') end
--; /* remove all trailing ':' */
122 /* find start of file name */
123 for (start
= end
; start
>= dos_name
; start
--)
125 if (IS_PATH_SEPARATOR(start
[0])) break;
126 /* check for ':' but ignore if before extension (for things like NUL:.txt) */
127 if (start
[0] == ':' && start
[1] != '.') break;
131 /* remove extension */
132 if ((p
= wcschr( start
, '.' )))
135 if (end
>= dos_name
&& *end
== ':') end
--; /* remove trailing ':' before extension */
137 /* remove trailing spaces */
138 while (end
>= dos_name
&& *end
== ' ') end
--;
140 /* now we have a potential device name between start and end, check it */
141 switch(end
- start
+ 1)
144 if (_wcsnicmp( start
, auxW
, 3 ) &&
145 _wcsnicmp( start
, conW
, 3 ) &&
146 _wcsnicmp( start
, nulW
, 3 ) &&
147 _wcsnicmp( start
, prnW
, 3 )) break;
148 return MAKELONG( 3 * sizeof(WCHAR
), (start
- dos_name
) * sizeof(WCHAR
) );
150 if (_wcsnicmp( start
, comW
, 3 ) && _wcsnicmp( start
, lptW
, 3 )) break;
151 if (*end
<= '0' || *end
> '9') break;
152 return MAKELONG( 4 * sizeof(WCHAR
), (start
- dos_name
) * sizeof(WCHAR
) );
153 default: /* can't match anything */
164 RtlGetCurrentDirectory_U(ULONG MaximumLength
,
170 DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
174 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
175 Length
= cd
->DosPath
.Length
/ sizeof(WCHAR
);
176 if (cd
->DosPath
.Buffer
[Length
- 1] == L
'\\' &&
177 cd
->DosPath
.Buffer
[Length
- 2] != L
':')
180 DPRINT ("cd->DosPath.Buffer %S Length %lu\n",
181 cd
->DosPath
.Buffer
, Length
);
183 if (MaximumLength
/ sizeof(WCHAR
) > Length
)
187 Length
* sizeof(WCHAR
));
195 RtlReleasePebLock ();
197 DPRINT ("CurrentDirectory %S\n", Buffer
);
199 return (Length
* sizeof(WCHAR
));
207 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
210 FILE_FS_DEVICE_INFORMATION device_info
;
211 OBJECT_ATTRIBUTES Attr
;
212 IO_STATUS_BLOCK iosb
;
216 HANDLE handle
= NULL
;
219 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
221 RtlAcquirePebLock ();
223 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
225 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
227 RtlReleasePebLock ();
228 return STATUS_OBJECT_NAME_INVALID
;
231 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
233 InitializeObjectAttributes (&Attr
,
235 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
239 Status
= ZwOpenFile (&handle
,
240 SYNCHRONIZE
| FILE_TRAVERSE
,
243 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
244 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
246 if (!NT_SUCCESS(Status
))
248 RtlFreeUnicodeString( &full
);
249 RtlReleasePebLock ();
253 /* don't keep the directory handle open on removable media */
254 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
255 sizeof(device_info
), FileFsDeviceInformation
)) &&
256 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
258 DPRINT1("don't keep the directory handle open on removable media\n");
267 /* append trailing \ if missing */
268 size
= full
.Length
/ sizeof(WCHAR
);
270 ptr
+= 4; /* skip \??\ prefix */
273 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
274 * So the nullterm is replaced with \
277 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
279 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
280 cd
->DosPath
.Buffer
[size
] = 0;
281 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
283 RtlFreeUnicodeString( &full
);
286 return STATUS_SUCCESS
;
291 /******************************************************************
294 * Helper for RtlGetFullPathName_U.
295 * 1) Convert slashes into backslashes
296 * 2) Get rid of duplicate backslashes
297 * 3) Get rid of . and .. components in the path.
299 static __inline
void collapse_path( WCHAR
*path
, UINT mark
)
303 /* convert every / into a \ */
304 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
306 /* collapse duplicate backslashes */
307 next
= path
+ max( 1, mark
);
308 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
318 case '\\': /* .\ component */
320 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
322 case 0: /* final . */
323 if (p
> path
+ mark
) p
--;
327 if (p
[2] == '\\') /* ..\ component */
333 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
335 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
338 else if (!p
[2]) /* final .. */
343 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
344 if (p
> path
+ mark
) p
--;
352 /* skip to the next component */
353 while (*p
&& *p
!= '\\') p
++;
356 /* remove last dot in previous dir name */
357 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
362 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
363 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
369 /******************************************************************
372 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
374 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
377 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
378 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
379 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
380 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
385 /******************************************************************
386 * get_full_path_helper
388 * Helper for RtlGetFullPathName_U
389 * Note: name and buffer are allowed to point to the same memory spot
391 static ULONG
get_full_path_helper(
396 ULONG reqsize
= 0, mark
= 0, dep
= 0, deplen
;
397 LPWSTR ins_str
= NULL
;
399 const UNICODE_STRING
* cd
;
402 /* return error if name only consists of spaces */
403 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
408 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
409 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
411 switch (RtlDetermineDosPathNameType_U(name
))
413 case RtlPathTypeUncAbsolute
: /* \\foo */
414 ptr
= skip_unc_prefix( name
);
418 case RtlPathTypeLocalDevice
: /* \\.\foo */
422 case RtlPathTypeDriveAbsolute
: /* c:\foo */
423 reqsize
= sizeof(WCHAR
);
424 tmp
[0] = towupper(name
[0]);
430 case RtlPathTypeDriveRelative
: /* c:foo */
432 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
434 UNICODE_STRING var
, val
;
440 var
.Length
= 3 * sizeof(WCHAR
);
441 var
.MaximumLength
= 4 * sizeof(WCHAR
);
444 val
.MaximumLength
= size
;
445 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
446 if (val
.Buffer
== NULL
)
452 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
455 /* FIXME: Win2k seems to check that the environment variable actually points
456 * to an existing directory. If not, root of the drive is used
457 * (this seems also to be the only spot in RtlGetFullPathName that the
458 * existence of a part of a path is checked)
461 case STATUS_BUFFER_TOO_SMALL
:
462 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
463 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
464 ins_str
= val
.Buffer
;
466 case STATUS_VARIABLE_NOT_FOUND
:
467 reqsize
= 3 * sizeof(WCHAR
);
474 DPRINT1("Unsupported status code\n");
482 case RtlPathTypeRelative
: /* foo */
483 reqsize
= cd
->Length
;
484 ins_str
= cd
->Buffer
;
485 if (cd
->Buffer
[1] != ':')
487 ptr
= skip_unc_prefix( cd
->Buffer
);
488 mark
= ptr
- cd
->Buffer
;
493 case RtlPathTypeRooted
: /* \xxx */
495 if (name
[0] == '/') /* may be a Unix path */
497 const WCHAR
*ptr
= name
;
498 int drive
= find_drive_root( &ptr
);
501 reqsize
= 3 * sizeof(WCHAR
);
502 tmp
[0] = 'A' + drive
;
512 if (cd
->Buffer
[1] == ':')
514 reqsize
= 2 * sizeof(WCHAR
);
515 tmp
[0] = cd
->Buffer
[0];
522 ptr
= skip_unc_prefix( cd
->Buffer
);
523 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
524 mark
= reqsize
/ sizeof(WCHAR
);
525 ins_str
= cd
->Buffer
;
529 case RtlPathTypeRootLocalDevice
: /* \\. */
530 reqsize
= 4 * sizeof(WCHAR
);
540 case RtlPathTypeUnknown
:
545 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
546 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
548 /* not enough space, return need size (including terminating '\0') */
549 reqsize
+= deplen
+ sizeof(WCHAR
);
553 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
554 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
557 if (ins_str
&& ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
558 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
560 collapse_path( buffer
, mark
);
561 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
569 /******************************************************************
570 * RtlGetFullPathName_U (NTDLL.@)
572 * Returns the number of bytes written to buffer (not including the
573 * terminating NULL) if the function succeeds, or the required number of bytes
574 * (including the terminating NULL) if the buffer is too small.
576 * file_part will point to the filename part inside buffer (except if we use
577 * DOS device name, in which case file_in_buf is NULL)
581 ULONG NTAPI
RtlGetFullPathName_U(
591 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
593 if (!name
|| !*name
) return 0;
595 if (file_part
) *file_part
= NULL
;
597 /* check for DOS device name */
598 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
601 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
602 DWORD sz
= LOWORD(dosdev
); /* in bytes */
604 if (8 + sz
+ 2 > size
) return sz
+ 10;
605 wcscpy(buffer
, DeviceRootW
);
606 memmove(buffer
+ 4, name
+ offset
, sz
);
607 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
608 /* file_part isn't set in this case */
612 reqsize
= get_full_path_helper(name
, buffer
, size
);
613 if (!reqsize
) return 0;
616 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
619 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
620 if (reqsize
> size
) /* it may have worked the second time */
622 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
623 return reqsize
+ sizeof(WCHAR
);
625 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
626 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
630 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
640 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName
,
641 OUT PUNICODE_STRING NtPathName
,
642 OUT PCWSTR
*NtFileNamePart
,
643 OUT CURDIR
*DirectoryInfo
)
652 WCHAR fullname
[MAX_PATH
+ 1];
655 RtlInitUnicodeString (&us
, DosPathName
);
659 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
660 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
661 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
663 /* allocate the new string and simply copy it */
664 NtPathName
->Length
= us
.Length
;
665 NtPathName
->MaximumLength
= us
.Length
+ sizeof(WCHAR
);
666 NtPathName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
668 NtPathName
->MaximumLength
);
669 if (NtPathName
->Buffer
== NULL
)
674 /* copy the string */
675 RtlCopyMemory(NtPathName
->Buffer
,
678 NtPathName
->Buffer
[us
.Length
/ sizeof(WCHAR
)] = L
'\0';
680 /* change the \\?\ prefix to \??\ */
681 NtPathName
->Buffer
[1] = L
'?';
683 if (NtFileNamePart
!= NULL
)
685 PWSTR FilePart
= NULL
;
688 /* try to find the last separator */
689 s
= NtPathName
->Buffer
+ (NtPathName
->Length
/ sizeof(WCHAR
));
690 while (s
!= NtPathName
->Buffer
)
700 *NtFileNamePart
= FilePart
;
703 if (DirectoryInfo
!= NULL
)
705 DirectoryInfo
->DosPath
.Length
= 0;
706 DirectoryInfo
->DosPath
.MaximumLength
= 0;
707 DirectoryInfo
->DosPath
.Buffer
= NULL
;
708 DirectoryInfo
->Handle
= NULL
;
715 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
717 sizeof( fullname
) + MAX_PFX_SIZE
);
723 RtlAcquirePebLock ();
725 Size
= RtlGetFullPathName_U (DosPathName
,
728 (PWSTR
*)NtFileNamePart
);
729 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
731 RtlFreeHeap (RtlGetProcessHeap (),
734 RtlReleasePebLock ();
740 memcpy (Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
743 Type
= RtlDetermineDosPathNameType_U (fullname
);
747 memcpy (Buffer
+ tmpLength
, L
"UNC\\", 4 * sizeof(WCHAR
));
756 Length
= wcslen(fullname
+ Offset
);
757 memcpy (Buffer
+ tmpLength
, fullname
+ Offset
, (Length
+ 1) * sizeof(WCHAR
));
759 if (Type
== RtlPathTypeDriveAbsolute
||
760 Type
== RtlPathTypeDriveRelative
)
762 /* make the drive letter to uppercase */
763 Buffer
[tmpLength
] = towupper(Buffer
[tmpLength
]);
766 /* set NT filename */
767 NtPathName
->Length
= Length
* sizeof(WCHAR
);
768 NtPathName
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
769 NtPathName
->Buffer
= Buffer
;
771 /* set pointer to file part if possible */
772 if (NtFileNamePart
&& *NtFileNamePart
)
773 *NtFileNamePart
= Buffer
+ Length
- wcslen (*NtFileNamePart
);
775 /* Set name and handle structure if possible */
778 memset (DirectoryInfo
, 0, sizeof(CURDIR
));
779 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
780 if (Type
== 5 && cd
->Handle
)
782 RtlInitUnicodeString(&us
, fullname
);
783 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
785 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
786 DirectoryInfo
->DosPath
.Buffer
= Buffer
+ Length
;
787 DirectoryInfo
->DosPath
.Length
= NtPathName
->Length
- (Length
* sizeof(WCHAR
));
788 DirectoryInfo
->DosPath
.MaximumLength
= DirectoryInfo
->DosPath
.Length
;
789 DirectoryInfo
->Handle
= cd
->Handle
;
819 Type
= RtlDetermineDosPathNameType_U (name
);
823 Length
= wcslen (sp
);
824 Length
+= wcslen (name
);
825 if (wcschr (name
, L
'.'))
828 Length
+= wcslen (ext
);
830 full_name
= (WCHAR
*)RtlAllocateHeap (RtlGetProcessHeap (),
832 (Length
+ 1) * sizeof(WCHAR
));
834 if (full_name
!= NULL
)
840 while (*path
&& *path
!= L
';')
844 if (wcs
!= full_name
&& *(wcs
- 1) != L
'\\')
849 if (RtlDoesFileExists_U (full_name
))
851 Length
= RtlGetFullPathName_U (full_name
,
859 RtlFreeHeap (RtlGetProcessHeap (),
864 else if (RtlDoesFileExists_U (name
))
866 Length
= RtlGetFullPathName_U (name
,
880 RtlDoesFileExists_U(IN PCWSTR FileName
)
882 UNICODE_STRING NtFileName
;
883 OBJECT_ATTRIBUTES Attr
;
884 FILE_BASIC_INFORMATION Info
;
889 if (!RtlDosPathNameToNtPathName_U (FileName
,
895 if (CurDir
.DosPath
.Length
)
896 NtFileName
= CurDir
.DosPath
;
900 InitializeObjectAttributes (&Attr
,
902 OBJ_CASE_INSENSITIVE
,
906 Status
= ZwQueryAttributesFile (&Attr
, &Info
);
908 RtlFreeUnicodeString(&NtFileName
);
911 if (NT_SUCCESS(Status
) ||
912 Status
== STATUS_SHARING_VIOLATION
||
913 Status
== STATUS_ACCESS_DENIED
)
924 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1
,
929 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
930 Unknown1
, Unknown2
, Unknown3
, Unknown4
);
939 RtlReleaseRelativeName(PVOID Unknown
)
941 DPRINT1("RtlReleaseRelativeName(0x%p) UNIMPLEMENTED\n", Unknown
);
945 RtlpEnsureBufferSize(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
)
947 DPRINT1("RtlpEnsureBufferSize: stub\n");
948 return STATUS_NOT_IMPLEMENTED
;
952 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
954 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
955 return STATUS_NOT_IMPLEMENTED
;