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
;
398 LPWSTR ins_str
= NULL
;
400 const UNICODE_STRING
* cd
;
403 /* return error if name only consists of spaces */
404 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
409 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
410 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
412 switch (type
= RtlDetermineDosPathNameType_U(name
))
414 case RtlPathTypeUncAbsolute
: /* \\foo */
415 ptr
= skip_unc_prefix( name
);
419 case RtlPathTypeLocalDevice
: /* \\.\foo */
423 case RtlPathTypeDriveAbsolute
: /* c:\foo */
424 reqsize
= sizeof(WCHAR
);
425 tmp
[0] = towupper(name
[0]);
431 case RtlPathTypeDriveRelative
: /* c:foo */
433 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
435 UNICODE_STRING var
, val
;
441 var
.Length
= 3 * sizeof(WCHAR
);
442 var
.MaximumLength
= 4 * sizeof(WCHAR
);
445 val
.MaximumLength
= size
;
446 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
447 if (val
.Buffer
== NULL
)
453 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
456 /* FIXME: Win2k seems to check that the environment variable actually points
457 * to an existing directory. If not, root of the drive is used
458 * (this seems also to be the only spot in RtlGetFullPathName that the
459 * existence of a part of a path is checked)
462 case STATUS_BUFFER_TOO_SMALL
:
463 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
464 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
465 ins_str
= val
.Buffer
;
467 case STATUS_VARIABLE_NOT_FOUND
:
468 reqsize
= 3 * sizeof(WCHAR
);
475 DPRINT1("Unsupported status code\n");
483 case RtlPathTypeRelative
: /* foo */
484 reqsize
= cd
->Length
;
485 ins_str
= cd
->Buffer
;
486 if (cd
->Buffer
[1] != ':')
488 ptr
= skip_unc_prefix( cd
->Buffer
);
489 mark
= ptr
- cd
->Buffer
;
494 case RtlPathTypeRooted
: /* \xxx */
496 if (name
[0] == '/') /* may be a Unix path */
498 const WCHAR
*ptr
= name
;
499 int drive
= find_drive_root( &ptr
);
502 reqsize
= 3 * sizeof(WCHAR
);
503 tmp
[0] = 'A' + drive
;
513 if (cd
->Buffer
[1] == ':')
515 reqsize
= 2 * sizeof(WCHAR
);
516 tmp
[0] = cd
->Buffer
[0];
523 ptr
= skip_unc_prefix( cd
->Buffer
);
524 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
525 mark
= reqsize
/ sizeof(WCHAR
);
526 ins_str
= cd
->Buffer
;
530 case RtlPathTypeRootLocalDevice
: /* \\. */
531 reqsize
= 4 * sizeof(WCHAR
);
541 case RtlPathTypeUnknown
:
546 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
547 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
549 /* not enough space, return need size (including terminating '\0') */
550 reqsize
+= deplen
+ sizeof(WCHAR
);
554 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
555 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
558 if (ins_str
&& ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
559 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
561 collapse_path( buffer
, mark
);
562 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
570 /******************************************************************
571 * RtlGetFullPathName_U (NTDLL.@)
573 * Returns the number of bytes written to buffer (not including the
574 * terminating NULL) if the function succeeds, or the required number of bytes
575 * (including the terminating NULL) if the buffer is too small.
577 * file_part will point to the filename part inside buffer (except if we use
578 * DOS device name, in which case file_in_buf is NULL)
582 ULONG NTAPI
RtlGetFullPathName_U(
592 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
594 if (!name
|| !*name
) return 0;
596 if (file_part
) *file_part
= NULL
;
598 /* check for DOS device name */
599 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
602 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
603 DWORD sz
= LOWORD(dosdev
); /* in bytes */
605 if (8 + sz
+ 2 > size
) return sz
+ 10;
606 wcscpy(buffer
, DeviceRootW
);
607 memmove(buffer
+ 4, name
+ offset
, sz
);
608 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
609 /* file_part isn't set in this case */
613 reqsize
= get_full_path_helper(name
, buffer
, size
);
614 if (!reqsize
) return 0;
617 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
620 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
621 if (reqsize
> size
) /* it may have worked the second time */
623 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
624 return reqsize
+ sizeof(WCHAR
);
626 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
627 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
631 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
641 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName
,
642 OUT PUNICODE_STRING NtPathName
,
643 OUT PCWSTR
*NtFileNamePart
,
644 OUT CURDIR
*DirectoryInfo
)
653 WCHAR fullname
[MAX_PATH
+ 1];
656 RtlInitUnicodeString (&us
, DosPathName
);
660 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
661 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
662 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
664 /* allocate the new string and simply copy it */
665 NtPathName
->Length
= us
.Length
;
666 NtPathName
->MaximumLength
= us
.Length
+ sizeof(WCHAR
);
667 NtPathName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
669 NtPathName
->MaximumLength
);
670 if (NtPathName
->Buffer
== NULL
)
675 /* copy the string */
676 RtlCopyMemory(NtPathName
->Buffer
,
679 NtPathName
->Buffer
[us
.Length
/ sizeof(WCHAR
)] = L
'\0';
681 /* change the \\?\ prefix to \??\ */
682 NtPathName
->Buffer
[1] = L
'?';
684 if (NtFileNamePart
!= NULL
)
686 PWSTR FilePart
= NULL
;
689 /* try to find the last separator */
690 s
= NtPathName
->Buffer
+ (NtPathName
->Length
/ sizeof(WCHAR
));
691 while (s
!= NtPathName
->Buffer
)
701 *NtFileNamePart
= FilePart
;
704 if (DirectoryInfo
!= NULL
)
706 DirectoryInfo
->DosPath
.Length
= 0;
707 DirectoryInfo
->DosPath
.MaximumLength
= 0;
708 DirectoryInfo
->DosPath
.Buffer
= NULL
;
709 DirectoryInfo
->Handle
= NULL
;
716 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
718 sizeof( fullname
) + MAX_PFX_SIZE
);
724 RtlAcquirePebLock ();
726 Size
= RtlGetFullPathName_U (DosPathName
,
729 (PWSTR
*)NtFileNamePart
);
730 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
732 RtlFreeHeap (RtlGetProcessHeap (),
735 RtlReleasePebLock ();
741 memcpy (Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
744 Type
= RtlDetermineDosPathNameType_U (fullname
);
748 memcpy (Buffer
+ tmpLength
, L
"UNC\\", 4 * sizeof(WCHAR
));
757 Length
= wcslen(fullname
+ Offset
);
758 memcpy (Buffer
+ tmpLength
, fullname
+ Offset
, (Length
+ 1) * sizeof(WCHAR
));
760 if (Type
== RtlPathTypeDriveAbsolute
||
761 Type
== RtlPathTypeDriveRelative
)
763 /* make the drive letter to uppercase */
764 Buffer
[tmpLength
] = towupper(Buffer
[tmpLength
]);
767 /* set NT filename */
768 NtPathName
->Length
= Length
* sizeof(WCHAR
);
769 NtPathName
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
770 NtPathName
->Buffer
= Buffer
;
772 /* set pointer to file part if possible */
773 if (NtFileNamePart
&& *NtFileNamePart
)
774 *NtFileNamePart
= Buffer
+ Length
- wcslen (*NtFileNamePart
);
776 /* Set name and handle structure if possible */
779 memset (DirectoryInfo
, 0, sizeof(CURDIR
));
780 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
781 if (Type
== 5 && cd
->Handle
)
783 RtlInitUnicodeString(&us
, fullname
);
784 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
786 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
787 DirectoryInfo
->DosPath
.Buffer
= Buffer
+ Length
;
788 DirectoryInfo
->DosPath
.Length
= NtPathName
->Length
- (Length
* sizeof(WCHAR
));
789 DirectoryInfo
->DosPath
.MaximumLength
= DirectoryInfo
->DosPath
.Length
;
790 DirectoryInfo
->Handle
= cd
->Handle
;
820 Type
= RtlDetermineDosPathNameType_U (name
);
824 Length
= wcslen (sp
);
825 Length
+= wcslen (name
);
826 if (wcschr (name
, L
'.'))
829 Length
+= wcslen (ext
);
831 full_name
= (WCHAR
*)RtlAllocateHeap (RtlGetProcessHeap (),
833 (Length
+ 1) * sizeof(WCHAR
));
835 if (full_name
!= NULL
)
841 while (*path
&& *path
!= L
';')
845 if (wcs
!= full_name
&& *(wcs
- 1) != L
'\\')
850 if (RtlDoesFileExists_U (full_name
))
852 Length
= RtlGetFullPathName_U (full_name
,
860 RtlFreeHeap (RtlGetProcessHeap (),
865 else if (RtlDoesFileExists_U (name
))
867 Length
= RtlGetFullPathName_U (name
,
881 RtlDoesFileExists_U(IN PCWSTR FileName
)
883 UNICODE_STRING NtFileName
;
884 OBJECT_ATTRIBUTES Attr
;
885 FILE_BASIC_INFORMATION Info
;
890 if (!RtlDosPathNameToNtPathName_U (FileName
,
896 if (CurDir
.DosPath
.Length
)
897 NtFileName
= CurDir
.DosPath
;
901 InitializeObjectAttributes (&Attr
,
903 OBJ_CASE_INSENSITIVE
,
907 Status
= ZwQueryAttributesFile (&Attr
, &Info
);
909 RtlFreeUnicodeString(&NtFileName
);
912 if (NT_SUCCESS(Status
) ||
913 Status
== STATUS_SHARING_VIOLATION
||
914 Status
== STATUS_ACCESS_DENIED
)
925 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1
,
930 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
931 Unknown1
, Unknown2
, Unknown3
, Unknown4
);
940 RtlReleaseRelativeName(PVOID Unknown
)
942 DPRINT1("RtlReleaseRelativeName(0x%p) UNIMPLEMENTED\n", Unknown
);
946 RtlpEnsureBufferSize(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
)
948 DPRINT1("RtlpEnsureBufferSize: stub\n");
949 return STATUS_NOT_IMPLEMENTED
;
953 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
955 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
956 return STATUS_NOT_IMPLEMENTED
;