2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: Path and current directory functions
6 * PROGRAMMERS: Wine team
11 /* INCLUDES *****************************************************************/
18 /* DEFINITONS and MACROS ******************************************************/
20 #define MAX_PFX_SIZE 16
22 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
25 /* GLOBALS ********************************************************************/
27 static const WCHAR DeviceRootW
[] = L
"\\\\.\\";
28 static const UNICODE_STRING _condev
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
29 static const UNICODE_STRING _unc
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
31 const UNICODE_STRING DeviceRootString
= RTL_CONSTANT_STRING(L
"\\\\.\\");
33 const UNICODE_STRING RtlpDosLPTDevice
= RTL_CONSTANT_STRING(L
"LPT");
34 const UNICODE_STRING RtlpDosCOMDevice
= RTL_CONSTANT_STRING(L
"COM");
35 const UNICODE_STRING RtlpDosPRNDevice
= RTL_CONSTANT_STRING(L
"PRN");
36 const UNICODE_STRING RtlpDosAUXDevice
= RTL_CONSTANT_STRING(L
"AUX");
37 const UNICODE_STRING RtlpDosCONDevice
= RTL_CONSTANT_STRING(L
"CON");
38 const UNICODE_STRING RtlpDosSlashCONDevice
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
39 const UNICODE_STRING RtlpDosNULDevice
= RTL_CONSTANT_STRING(L
"NUL");
41 /* FUNCTIONS *****************************************************************/
48 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
50 /* Check if a directory reference was grabbed */
51 if (RelativeName
->CurDirRef
)
53 /* FIXME: Not yet supported */
55 RelativeName
->CurDirRef
= NULL
;
64 RtlGetLongestNtPathLength(VOID
)
67 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
68 * a mapped network drive), which is accessed through the DOS Global?? path.
69 * This is, and has always been equal to, 269 characters, except in Wine
70 * which claims this is 277. Go figure.
72 return (MAX_PATH
+ _unc
.Length
+ sizeof(ANSI_NULL
));
81 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
83 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
86 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
87 if (IS_PATH_SEPARATOR(Path
[0]))
89 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
90 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
91 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
92 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
93 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
97 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
98 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
99 return RtlPathTypeDriveRelative
; /* x: */
108 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
110 PWCHAR Path
= PathString
->Buffer
;
111 ULONG Chars
= PathString
->Length
/ sizeof(WCHAR
);
114 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
115 * actually check for the path length before touching the characters
117 if ((Chars
< 1) || (IS_PATH_SEPARATOR(Path
[0])))
119 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
120 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
121 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
122 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
123 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
127 if ((Chars
< 2) || (!(Path
[0]) || (Path
[1] != L
':'))) return RtlPathTypeRelative
; /* x */
128 if ((Chars
< 3) || (IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveAbsolute
; /* x:\ */
129 return RtlPathTypeDriveRelative
; /* x: */
138 RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString
)
140 UNICODE_STRING PathCopy
;
142 ULONG PathChars
, ColonCount
= 0;
143 USHORT ReturnOffset
= 0, ReturnLength
;
146 /* Check what type of path this is */
147 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
149 /* Fail for UNC or unknown paths */
150 case RtlPathTypeUnknown
:
151 case RtlPathTypeUncAbsolute
:
154 /* Make special check for the CON device */
155 case RtlPathTypeLocalDevice
:
156 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
158 /* This should return 0x80006 */
159 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
167 /* Make a copy of the string */
168 PathCopy
= *PathString
;
170 /* Return if there's no characters */
171 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
172 if (!PathChars
) return 0;
174 /* Check for drive path and truncate */
175 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
177 /* Fixup the lengths */
178 PathCopy
.Length
-= sizeof(WCHAR
);
179 if (!--PathChars
) return 0;
181 /* Remember this for later */
185 /* Check for extension or space, and truncate */
186 c
= PathCopy
.Buffer
[PathChars
- 1];
189 /* Stop if we hit a space or period */
190 if ((c
!= '.') && (c
!= ' ')) break;
192 /* Fixup the lengths and get the next character */
193 PathCopy
.Length
-= sizeof(WCHAR
);
194 if (!--PathChars
) c
= PathCopy
.Buffer
[PathChars
- 1];
196 /* Remember this for later */
200 /* Anything still left? */
203 /* Loop from the end */
204 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
205 End
>= PathCopy
.Buffer
;
208 /* Check if the character is a path or drive separator */
210 if ((c
== '\\') || (c
== '/') || ((c
== ':') && (End
== PathCopy
.Buffer
+ 1)))
212 /* Get the next lower case character */
214 c
= *End
| ' '; // ' ' == ('z' - 'Z')
216 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
217 if ((End
< &PathCopy
.Buffer
[PathCopy
.Length
/ sizeof(WCHAR
)]) &&
218 ((c
== 'l') || (c
== 'c') || (c
== 'p') || (c
== 'a') || (c
== 'n')))
220 /* Calculate the offset */
221 ReturnOffset
= (PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
;
223 /* Build the final string */
224 PathCopy
.Length
-= ReturnOffset
;
225 PathCopy
.Length
-= (ColonCount
* sizeof(WCHAR
));
226 PathCopy
.Buffer
= End
;
234 /* Get the next lower case character and check if it's a DOS device */
235 c
= *PathCopy
.Buffer
| ' '; // ' ' == ('z' - 'Z')
236 if ((c
!= 'l') && (c
!= 'c') && (c
!= 'p') && (c
!= 'a') && (c
!= 'n'))
238 /* Not LPT, COM, PRN, AUX, or NUL */
243 /* Now skip past any extra extension or drive letter characters */
244 Start
= PathCopy
.Buffer
;
245 End
= &Start
[PathChars
];
249 if ((c
== '.') || (c
== ':')) break;
253 /* And then go backwards to get rid of spaces */
254 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == ' ')) --Start
;
256 /* Finally see how many characters are left, and that's our size */
257 PathChars
= Start
- PathCopy
.Buffer
;
258 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
260 /* Check if this is a COM or LPT port, which has a digit after it */
261 if ((PathChars
== 4) &&
262 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != '0')))
264 /* Don't compare the number part, just check for LPT or COM */
265 PathCopy
.Length
-= sizeof(WCHAR
);
266 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
267 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
270 ReturnLength
= sizeof(L
"COM1");
271 return MAKELONG(ReturnOffset
, ReturnLength
);
274 else if ((PathChars
== 3) &&
275 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
276 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
277 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
278 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
280 /* Otherwise this was something like AUX, NUL, PRN, or CON */
281 ReturnLength
= sizeof(L
"AUX");
282 return MAKELONG(ReturnOffset
, ReturnLength
);
285 /* Otherwise, this isn't a valid DOS device */
294 RtlIsDosDeviceName_U(IN PWSTR Path
)
296 UNICODE_STRING PathString
;
299 /* Build the string */
300 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
301 if (!NT_SUCCESS(Status
)) return 0;
304 * Returns 0 if name is not valid DOS device name, or DWORD with
305 * offset in bytes to DOS device name from beginning of buffer in high word
306 * and size in bytes of DOS device name in low word
308 return RtlIsDosDeviceName_Ustr(&PathString
);
316 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
322 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
324 /* Lock the PEB to get the current directory */
326 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
328 /* Get the buffer and character length */
329 CurDirName
= CurDir
->DosPath
.Buffer
;
330 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
331 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
333 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
334 Bytes
= Length
* sizeof(WCHAR
);
335 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
337 /* Check if caller does not have enough space */
338 if (MaximumLength
<= Bytes
)
340 /* Call has no space for it, fail, add the trailing slash */
342 return Bytes
+ sizeof(L
'\\');
347 /* Check if caller does not have enough space */
348 if (MaximumLength
<= Bytes
)
350 /* Call has no space for it, fail */
356 /* Copy the buffer since we seem to have space */
357 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
359 /* The buffer should end with a path separator */
360 ASSERT(Buffer
[Length
- 1] == L
'\\');
362 /* Again check for our two cases (drive root vs path) */
363 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
365 /* Replace the trailing slash with a null */
366 Buffer
[Length
- 1] = UNICODE_NULL
;
371 /* Append the null char since there's no trailing slash */
372 Buffer
[Length
] = UNICODE_NULL
;
375 /* Release PEB lock */
377 DPRINT("CurrentDirectory %S\n", Buffer
);
378 return Length
* sizeof(WCHAR
);
385 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
388 FILE_FS_DEVICE_INFORMATION device_info
;
389 OBJECT_ATTRIBUTES Attr
;
390 IO_STATUS_BLOCK iosb
;
394 HANDLE handle
= NULL
;
397 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
401 RtlAcquirePebLock ();
403 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
405 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
407 RtlReleasePebLock ();
408 return STATUS_OBJECT_NAME_INVALID
;
411 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
413 InitializeObjectAttributes (&Attr
,
415 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
419 Status
= ZwOpenFile (&handle
,
420 SYNCHRONIZE
| FILE_TRAVERSE
,
423 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
424 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
426 if (!NT_SUCCESS(Status
))
428 RtlFreeUnicodeString( &full
);
429 RtlReleasePebLock ();
433 /* don't keep the directory handle open on removable media */
434 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
435 sizeof(device_info
), FileFsDeviceInformation
)) &&
436 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
438 DPRINT1("don't keep the directory handle open on removable media\n");
447 /* append trailing \ if missing */
448 size
= full
.Length
/ sizeof(WCHAR
);
450 ptr
+= 4; /* skip \??\ prefix */
453 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
454 * So the nullterm is replaced with \
457 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
459 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
460 cd
->DosPath
.Buffer
[size
] = 0;
461 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
463 RtlFreeUnicodeString( &full
);
466 return STATUS_SUCCESS
;
470 /******************************************************************
473 * Helper for RtlGetFullPathName_U.
474 * Get rid of . and .. components in the path.
476 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
480 /* convert every / into a \ */
481 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
483 /* collapse duplicate backslashes */
484 next
= path
+ max( 1, mark
);
485 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
495 case '\\': /* .\ component */
497 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
499 case 0: /* final . */
500 if (p
> path
+ mark
) p
--;
504 if (p
[2] == '\\') /* ..\ component */
510 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
512 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
515 else if (!p
[2]) /* final .. */
520 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
521 if (p
> path
+ mark
) p
--;
529 /* skip to the next component */
530 while (*p
&& *p
!= '\\') p
++;
533 /* remove last dot in previous dir name */
534 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
539 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
540 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
546 /******************************************************************
549 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
551 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
554 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
555 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
556 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
557 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
562 /******************************************************************
563 * get_full_path_helper
565 * Helper for RtlGetFullPathName_U
566 * Note: name and buffer are allowed to point to the same memory spot
568 static ULONG
get_full_path_helper(
573 ULONG reqsize
= 0, mark
= 0, dep
= 0, deplen
;
574 LPWSTR ins_str
= NULL
;
576 const UNICODE_STRING
* cd
;
579 /* return error if name only consists of spaces */
580 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
585 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
586 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
588 switch (RtlDetermineDosPathNameType_U(name
))
590 case RtlPathTypeUncAbsolute
: /* \\foo */
591 ptr
= skip_unc_prefix( name
);
595 case RtlPathTypeLocalDevice
: /* \\.\foo */
599 case RtlPathTypeDriveAbsolute
: /* c:\foo */
600 reqsize
= sizeof(WCHAR
);
601 tmp
[0] = towupper(name
[0]);
607 case RtlPathTypeDriveRelative
: /* c:foo */
609 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
611 UNICODE_STRING var
, val
;
617 var
.Length
= 3 * sizeof(WCHAR
);
618 var
.MaximumLength
= 4 * sizeof(WCHAR
);
621 val
.MaximumLength
= size
;
622 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
623 if (val
.Buffer
== NULL
)
629 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
632 /* FIXME: Win2k seems to check that the environment variable actually points
633 * to an existing directory. If not, root of the drive is used
634 * (this seems also to be the only spot in RtlGetFullPathName that the
635 * existence of a part of a path is checked)
638 case STATUS_BUFFER_TOO_SMALL
:
639 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
640 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
641 ins_str
= val
.Buffer
;
643 case STATUS_VARIABLE_NOT_FOUND
:
644 reqsize
= 3 * sizeof(WCHAR
);
649 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
652 DPRINT1("Unsupported status code\n");
653 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
661 case RtlPathTypeRelative
: /* foo */
662 reqsize
= cd
->Length
;
663 ins_str
= cd
->Buffer
;
664 if (cd
->Buffer
[1] != ':')
666 ptr
= skip_unc_prefix( cd
->Buffer
);
667 mark
= ptr
- cd
->Buffer
;
672 case RtlPathTypeRooted
: /* \xxx */
674 if (name
[0] == '/') /* may be a Unix path */
676 const WCHAR
*ptr
= name
;
677 int drive
= find_drive_root( &ptr
);
680 reqsize
= 3 * sizeof(WCHAR
);
681 tmp
[0] = 'A' + drive
;
691 if (cd
->Buffer
[1] == ':')
693 reqsize
= 2 * sizeof(WCHAR
);
694 tmp
[0] = cd
->Buffer
[0];
701 ptr
= skip_unc_prefix( cd
->Buffer
);
702 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
703 mark
= reqsize
/ sizeof(WCHAR
);
704 ins_str
= cd
->Buffer
;
708 case RtlPathTypeRootLocalDevice
: /* \\. */
709 reqsize
= 4 * sizeof(WCHAR
);
719 case RtlPathTypeUnknown
:
724 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
725 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
727 /* not enough space, return need size (including terminating '\0') */
728 reqsize
+= deplen
+ sizeof(WCHAR
);
732 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
733 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
736 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
737 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
739 collapse_path( buffer
, mark
);
740 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
748 /******************************************************************
749 * RtlGetFullPathName_U (NTDLL.@)
751 * Returns the number of bytes written to buffer (not including the
752 * terminating NULL) if the function succeeds, or the required number of bytes
753 * (including the terminating NULL) if the buffer is too small.
755 * file_part will point to the filename part inside buffer (except if we use
756 * DOS device name, in which case file_in_buf is NULL)
760 ULONG NTAPI
RtlGetFullPathName_U(
770 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
772 if (!name
|| !*name
) return 0;
774 if (file_part
) *file_part
= NULL
;
776 /* check for DOS device name */
777 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
780 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
781 DWORD sz
= LOWORD(dosdev
); /* in bytes */
783 if (8 + sz
+ 2 > size
) return sz
+ 10;
784 wcscpy(buffer
, DeviceRootW
);
785 memmove(buffer
+ 4, name
+ offset
, sz
);
786 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
787 /* file_part isn't set in this case */
791 reqsize
= get_full_path_helper(name
, buffer
, size
);
792 if (!reqsize
) return 0;
795 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
796 if (tmp
== NULL
) return 0;
797 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
798 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
800 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
801 return reqsize
+ sizeof(WCHAR
);
803 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
804 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
808 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
818 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName
,
819 OUT PUNICODE_STRING NtPathName
,
820 OUT PCWSTR
*NtFileNamePart
,
821 OUT PRTL_RELATIVE_NAME_U DirectoryInfo
)
830 WCHAR fullname
[MAX_PATH
+ 1];
833 RtlInitUnicodeString (&us
, DosPathName
);
837 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
838 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
839 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
841 /* allocate the new string and simply copy it */
842 NtPathName
->Length
= us
.Length
;
843 NtPathName
->MaximumLength
= us
.Length
+ sizeof(WCHAR
);
844 NtPathName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
846 NtPathName
->MaximumLength
);
847 if (NtPathName
->Buffer
== NULL
)
852 /* copy the string */
853 RtlCopyMemory(NtPathName
->Buffer
,
856 NtPathName
->Buffer
[us
.Length
/ sizeof(WCHAR
)] = L
'\0';
858 /* change the \\?\ prefix to \??\ */
859 NtPathName
->Buffer
[1] = L
'?';
861 if (NtFileNamePart
!= NULL
)
863 PWSTR FilePart
= NULL
;
866 /* try to find the last separator */
867 s
= NtPathName
->Buffer
+ (NtPathName
->Length
/ sizeof(WCHAR
));
868 while (s
!= NtPathName
->Buffer
)
878 *NtFileNamePart
= FilePart
;
881 if (DirectoryInfo
!= NULL
)
883 DirectoryInfo
->RelativeName
.Length
= 0;
884 DirectoryInfo
->RelativeName
.MaximumLength
= 0;
885 DirectoryInfo
->RelativeName
.Buffer
= NULL
;
886 DirectoryInfo
->ContainingDirectory
= NULL
;
893 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
895 sizeof( fullname
) + MAX_PFX_SIZE
);
901 RtlAcquirePebLock ();
903 Size
= RtlGetFullPathName_U (DosPathName
,
906 (PWSTR
*)NtFileNamePart
);
907 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
909 RtlFreeHeap (RtlGetProcessHeap (),
912 RtlReleasePebLock ();
918 memcpy (Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
921 Type
= RtlDetermineDosPathNameType_U (fullname
);
925 memcpy (Buffer
+ tmpLength
, L
"UNC\\", 4 * sizeof(WCHAR
));
934 Length
= wcslen(fullname
+ Offset
);
935 memcpy (Buffer
+ tmpLength
, fullname
+ Offset
, (Length
+ 1) * sizeof(WCHAR
));
937 if (Type
== RtlPathTypeDriveAbsolute
||
938 Type
== RtlPathTypeDriveRelative
)
940 /* make the drive letter to uppercase */
941 Buffer
[tmpLength
] = towupper(Buffer
[tmpLength
]);
944 /* set NT filename */
945 NtPathName
->Length
= Length
* sizeof(WCHAR
);
946 NtPathName
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
947 NtPathName
->Buffer
= Buffer
;
949 /* set pointer to file part if possible */
950 if (NtFileNamePart
&& *NtFileNamePart
)
951 *NtFileNamePart
= Buffer
+ Length
- wcslen (*NtFileNamePart
);
953 /* Set name and handle structure if possible */
956 memset (DirectoryInfo
, 0, sizeof(RTL_RELATIVE_NAME_U
));
957 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
958 if (Type
== 5 && cd
->Handle
)
960 RtlInitUnicodeString(&us
, fullname
);
961 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
963 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
964 DirectoryInfo
->RelativeName
.Buffer
= Buffer
+ Length
;
965 DirectoryInfo
->RelativeName
.Length
= NtPathName
->Length
- (Length
* sizeof(WCHAR
));
966 DirectoryInfo
->RelativeName
.MaximumLength
= DirectoryInfo
->RelativeName
.Length
;
967 DirectoryInfo
->ContainingDirectory
= cd
->Handle
;
980 /******************************************************************
983 * Searches a file of name 'name' into a ';' separated list of paths
985 * Doesn't seem to search elsewhere than the paths list
986 * Stores the result in buffer (file_part will point to the position
987 * of the file name in the buffer)
989 * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
993 RtlDosSearchPath_U(PCWSTR paths
,
1000 RTL_PATH_TYPE type
= RtlDetermineDosPathNameType_U(search
);
1003 if (type
== RtlPathTypeRelative
)
1005 ULONG allocated
= 0, needed
, filelen
;
1008 filelen
= 1 /* for \ */ + wcslen(search
) + 1 /* \0 */;
1010 /* Windows only checks for '.' without worrying about path components */
1011 if (wcschr( search
, '.' )) ext
= NULL
;
1012 if (ext
!= NULL
) filelen
+= wcslen(ext
);
1018 for (needed
= 0, ptr
= paths
; *ptr
!= 0 && *ptr
++ != ';'; needed
++);
1019 if (needed
+ filelen
> allocated
)
1021 if (!name
) name
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
1022 (needed
+ filelen
) * sizeof(WCHAR
));
1025 WCHAR
*newname
= RtlReAllocateHeap(RtlGetProcessHeap(), 0, name
,
1026 (needed
+ filelen
) * sizeof(WCHAR
));
1027 if (!newname
) RtlFreeHeap(RtlGetProcessHeap(), 0, name
);
1030 if (!name
) return 0;
1031 allocated
= needed
+ filelen
;
1033 memmove(name
, paths
, needed
* sizeof(WCHAR
));
1034 /* append '\\' if none is present */
1035 if (needed
> 0 && name
[needed
- 1] != '\\') name
[needed
++] = '\\';
1036 wcscpy(&name
[needed
], search
);
1037 if (ext
) wcscat(&name
[needed
], ext
);
1038 if (RtlDoesFileExists_U(name
))
1040 len
= RtlGetFullPathName_U(name
, buffer_size
, buffer
, file_part
);
1045 RtlFreeHeap(RtlGetProcessHeap(), 0, name
);
1047 else if (RtlDoesFileExists_U(search
))
1049 len
= RtlGetFullPathName_U(search
, buffer_size
, buffer
, file_part
);
1060 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1061 IN BOOLEAN SucceedIfBusy
)
1064 RTL_RELATIVE_NAME_U RelativeName
;
1065 UNICODE_STRING NtPathName
;
1067 OBJECT_ATTRIBUTES ObjectAttributes
;
1069 FILE_BASIC_INFORMATION BasicInformation
;
1072 /* Get the NT Path */
1073 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1078 /* FIXME: Use the old API for now */
1079 Result
= RtlDosPathNameToNtPathName_U(FileName
->Buffer
,
1084 if (!Result
) return FALSE
;
1086 /* Save the buffer */
1087 Buffer
= NtPathName
.Buffer
;
1089 /* Check if we have a relative name */
1090 if (RelativeName
.RelativeName
.Length
)
1093 NtPathName
= RelativeName
.RelativeName
;
1097 /* Otherwise ignore it */
1098 RelativeName
.ContainingDirectory
= NULL
;
1101 /* Initialize the object attributes */
1102 InitializeObjectAttributes(&ObjectAttributes
,
1104 OBJ_CASE_INSENSITIVE
,
1105 RelativeName
.ContainingDirectory
,
1108 /* Query the attributes and free the buffer now */
1109 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1110 RtlReleaseRelativeName(&RelativeName
);
1111 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1113 /* Check if we failed */
1114 if (!NT_SUCCESS(Status
))
1116 /* Check if we failed because the file is in use */
1117 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1118 (Status
== STATUS_ACCESS_DENIED
))
1120 /* Check if the caller wants this to be considered OK */
1121 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1125 /* A failure because the file didn't exist */
1131 /* The file exists */
1135 /* Return the result */
1141 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1143 /* Call the updated API */
1144 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1152 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1153 IN BOOLEAN SucceedIfBusy
)
1155 UNICODE_STRING NameString
;
1157 /* Create the unicode name*/
1158 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1160 /* Call the unicode function */
1161 return NT_SUCCESS(RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
));
1173 RtlDoesFileExists_U(IN PCWSTR FileName
)
1175 /* Call the new function */
1176 return RtlDoesFileExists_UEx(FileName
, TRUE
);
1183 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1
,
1188 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
1189 Unknown1
, Unknown2
, Unknown3
, Unknown4
);
1194 RtlpEnsureBufferSize(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
)
1196 DPRINT1("RtlpEnsureBufferSize: stub\n");
1197 return STATUS_NOT_IMPLEMENTED
;
1201 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1203 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1204 return STATUS_NOT_IMPLEMENTED
;