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
"\\\\.\\";
29 static const UNICODE_STRING _condev
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
31 static const UNICODE_STRING _unc
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
33 static const UNICODE_STRING _lpt
= RTL_CONSTANT_STRING(L
"LPT");
35 static const UNICODE_STRING _com
= RTL_CONSTANT_STRING(L
"COM");
37 static const UNICODE_STRING _prn
= RTL_CONSTANT_STRING(L
"PRN");
39 static const UNICODE_STRING _aux
= RTL_CONSTANT_STRING(L
"AUX");
41 static const UNICODE_STRING _con
= RTL_CONSTANT_STRING(L
"CON");
43 static const UNICODE_STRING _nul
= RTL_CONSTANT_STRING(L
"NUL");
45 /* FUNCTIONS *****************************************************************/
52 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
54 /* Check if a directory reference was grabbed */
55 if (RelativeName
->CurDirRef
)
57 /* FIXME: Not yet supported */
59 RelativeName
->CurDirRef
= NULL
;
68 RtlGetLongestNtPathLength(VOID
)
71 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
72 * a mapped network drive), which is accessed through the DOS Global?? path.
73 * This is, and has always been equal to, 269 characters, except in Wine
74 * which claims this is 277. Go figure.
76 return (MAX_PATH
+ _unc
.Length
+ sizeof(ANSI_NULL
));
86 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
88 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
91 if (IS_PATH_SEPARATOR(Path
[0]))
93 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \xxx */
94 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\xxx */
95 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\xxx */
96 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.xxxx */
98 return RtlPathTypeRootLocalDevice
; /* \\. */
102 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* xxx */
103 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\xxx */
105 return RtlPathTypeDriveRelative
; /* x:xxx */
110 /* returns 0 if name is not valid DOS device name, or DWORD with
111 * offset in bytes to DOS device name from beginning of buffer in high word
112 * and size in bytes of DOS device name in low word */
118 RtlIsDosDeviceName_U(PWSTR dos_name
)
120 static const WCHAR consoleW
[] = {'\\','\\','.','\\','C','O','N',0};
121 static const WCHAR auxW
[3] = {'A','U','X'};
122 static const WCHAR comW
[3] = {'C','O','M'};
123 static const WCHAR conW
[3] = {'C','O','N'};
124 static const WCHAR lptW
[3] = {'L','P','T'};
125 static const WCHAR nulW
[3] = {'N','U','L'};
126 static const WCHAR prnW
[3] = {'P','R','N'};
128 const WCHAR
*start
, *end
, *p
;
130 switch(RtlDetermineDosPathNameType_U( dos_name
))
132 case RtlPathTypeUnknown
:
133 case RtlPathTypeUncAbsolute
:
135 case RtlPathTypeLocalDevice
:
136 if (!_wcsicmp( dos_name
, consoleW
))
137 return MAKELONG( sizeof(conW
), 4 * sizeof(WCHAR
) ); /* 4 is length of \\.\ prefix */
139 case RtlPathTypeDriveAbsolute
:
140 case RtlPathTypeDriveRelative
:
141 start
= dos_name
+ 2; /* skip drive letter */
148 /* find start of file name */
149 for (p
= start
; *p
; p
++) if (IS_PATH_SEPARATOR(*p
)) start
= p
+ 1;
151 /* truncate at extension and ':' */
152 for (end
= start
; *end
; end
++) if (*end
== '.' || *end
== ':') break;
155 /* remove trailing spaces */
156 while (end
>= start
&& *end
== ' ') end
--;
158 /* now we have a potential device name between start and end, check it */
159 switch(end
- start
+ 1)
162 if (_wcsnicmp( start
, auxW
, 3 ) &&
163 _wcsnicmp( start
, conW
, 3 ) &&
164 _wcsnicmp( start
, nulW
, 3 ) &&
165 _wcsnicmp( start
, prnW
, 3 )) break;
166 return MAKELONG( 3 * sizeof(WCHAR
), (start
- dos_name
) * sizeof(WCHAR
) );
168 if (_wcsnicmp( start
, comW
, 3 ) && _wcsnicmp( start
, lptW
, 3 )) break;
169 if (*end
<= '0' || *end
> '9') break;
170 return MAKELONG( 4 * sizeof(WCHAR
), (start
- dos_name
) * sizeof(WCHAR
) );
171 default: /* can't match anything */
182 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
188 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
190 /* Lock the PEB to get the current directory */
192 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
194 /* Get the buffer and character length */
195 CurDirName
= CurDir
->DosPath
.Buffer
;
196 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
197 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
199 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
200 Bytes
= Length
* sizeof(WCHAR
);
201 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
203 /* Check if caller does not have enough space */
204 if (MaximumLength
<= Bytes
)
206 /* Call has no space for it, fail, add the trailing slash */
208 return Bytes
+ sizeof(L
'\\');
213 /* Check if caller does not have enough space */
214 if (MaximumLength
<= Bytes
)
216 /* Call has no space for it, fail */
222 /* Copy the buffer since we seem to have space */
223 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
225 /* The buffer should end with a path separator */
226 ASSERT(Buffer
[Length
- 1] == L
'\\');
228 /* Again check for our two cases (drive root vs path) */
229 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
231 /* Replace the trailing slash with a null */
232 Buffer
[Length
- 1] = UNICODE_NULL
;
237 /* Append the null char since there's no trailing slash */
238 Buffer
[Length
] = UNICODE_NULL
;
241 /* Release PEB lock */
243 DPRINT("CurrentDirectory %S\n", Buffer
);
244 return Length
* sizeof(WCHAR
);
251 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
254 FILE_FS_DEVICE_INFORMATION device_info
;
255 OBJECT_ATTRIBUTES Attr
;
256 IO_STATUS_BLOCK iosb
;
260 HANDLE handle
= NULL
;
263 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
267 RtlAcquirePebLock ();
269 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
271 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
273 RtlReleasePebLock ();
274 return STATUS_OBJECT_NAME_INVALID
;
277 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
279 InitializeObjectAttributes (&Attr
,
281 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
285 Status
= ZwOpenFile (&handle
,
286 SYNCHRONIZE
| FILE_TRAVERSE
,
289 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
290 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
292 if (!NT_SUCCESS(Status
))
294 RtlFreeUnicodeString( &full
);
295 RtlReleasePebLock ();
299 /* don't keep the directory handle open on removable media */
300 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
301 sizeof(device_info
), FileFsDeviceInformation
)) &&
302 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
304 DPRINT1("don't keep the directory handle open on removable media\n");
313 /* append trailing \ if missing */
314 size
= full
.Length
/ sizeof(WCHAR
);
316 ptr
+= 4; /* skip \??\ prefix */
319 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
320 * So the nullterm is replaced with \
323 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
325 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
326 cd
->DosPath
.Buffer
[size
] = 0;
327 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
329 RtlFreeUnicodeString( &full
);
332 return STATUS_SUCCESS
;
336 /******************************************************************
339 * Helper for RtlGetFullPathName_U.
340 * Get rid of . and .. components in the path.
342 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
346 /* convert every / into a \ */
347 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
349 /* collapse duplicate backslashes */
350 next
= path
+ max( 1, mark
);
351 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
361 case '\\': /* .\ component */
363 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
365 case 0: /* final . */
366 if (p
> path
+ mark
) p
--;
370 if (p
[2] == '\\') /* ..\ component */
376 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
378 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
381 else if (!p
[2]) /* final .. */
386 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
387 if (p
> path
+ mark
) p
--;
395 /* skip to the next component */
396 while (*p
&& *p
!= '\\') p
++;
399 /* remove last dot in previous dir name */
400 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
405 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
406 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
412 /******************************************************************
415 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
417 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
420 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
421 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
422 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
423 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
428 /******************************************************************
429 * get_full_path_helper
431 * Helper for RtlGetFullPathName_U
432 * Note: name and buffer are allowed to point to the same memory spot
434 static ULONG
get_full_path_helper(
439 ULONG reqsize
= 0, mark
= 0, dep
= 0, deplen
;
440 LPWSTR ins_str
= NULL
;
442 const UNICODE_STRING
* cd
;
445 /* return error if name only consists of spaces */
446 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
451 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
452 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
454 switch (RtlDetermineDosPathNameType_U(name
))
456 case RtlPathTypeUncAbsolute
: /* \\foo */
457 ptr
= skip_unc_prefix( name
);
461 case RtlPathTypeLocalDevice
: /* \\.\foo */
465 case RtlPathTypeDriveAbsolute
: /* c:\foo */
466 reqsize
= sizeof(WCHAR
);
467 tmp
[0] = towupper(name
[0]);
473 case RtlPathTypeDriveRelative
: /* c:foo */
475 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
477 UNICODE_STRING var
, val
;
483 var
.Length
= 3 * sizeof(WCHAR
);
484 var
.MaximumLength
= 4 * sizeof(WCHAR
);
487 val
.MaximumLength
= size
;
488 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
489 if (val
.Buffer
== NULL
)
495 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
498 /* FIXME: Win2k seems to check that the environment variable actually points
499 * to an existing directory. If not, root of the drive is used
500 * (this seems also to be the only spot in RtlGetFullPathName that the
501 * existence of a part of a path is checked)
504 case STATUS_BUFFER_TOO_SMALL
:
505 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
506 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
507 ins_str
= val
.Buffer
;
509 case STATUS_VARIABLE_NOT_FOUND
:
510 reqsize
= 3 * sizeof(WCHAR
);
515 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
518 DPRINT1("Unsupported status code\n");
519 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
527 case RtlPathTypeRelative
: /* foo */
528 reqsize
= cd
->Length
;
529 ins_str
= cd
->Buffer
;
530 if (cd
->Buffer
[1] != ':')
532 ptr
= skip_unc_prefix( cd
->Buffer
);
533 mark
= ptr
- cd
->Buffer
;
538 case RtlPathTypeRooted
: /* \xxx */
540 if (name
[0] == '/') /* may be a Unix path */
542 const WCHAR
*ptr
= name
;
543 int drive
= find_drive_root( &ptr
);
546 reqsize
= 3 * sizeof(WCHAR
);
547 tmp
[0] = 'A' + drive
;
557 if (cd
->Buffer
[1] == ':')
559 reqsize
= 2 * sizeof(WCHAR
);
560 tmp
[0] = cd
->Buffer
[0];
567 ptr
= skip_unc_prefix( cd
->Buffer
);
568 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
569 mark
= reqsize
/ sizeof(WCHAR
);
570 ins_str
= cd
->Buffer
;
574 case RtlPathTypeRootLocalDevice
: /* \\. */
575 reqsize
= 4 * sizeof(WCHAR
);
585 case RtlPathTypeUnknown
:
590 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
591 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
593 /* not enough space, return need size (including terminating '\0') */
594 reqsize
+= deplen
+ sizeof(WCHAR
);
598 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
599 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
602 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
603 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
605 collapse_path( buffer
, mark
);
606 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
614 /******************************************************************
615 * RtlGetFullPathName_U (NTDLL.@)
617 * Returns the number of bytes written to buffer (not including the
618 * terminating NULL) if the function succeeds, or the required number of bytes
619 * (including the terminating NULL) if the buffer is too small.
621 * file_part will point to the filename part inside buffer (except if we use
622 * DOS device name, in which case file_in_buf is NULL)
626 ULONG NTAPI
RtlGetFullPathName_U(
636 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
638 if (!name
|| !*name
) return 0;
640 if (file_part
) *file_part
= NULL
;
642 /* check for DOS device name */
643 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
646 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
647 DWORD sz
= LOWORD(dosdev
); /* in bytes */
649 if (8 + sz
+ 2 > size
) return sz
+ 10;
650 wcscpy(buffer
, DeviceRootW
);
651 memmove(buffer
+ 4, name
+ offset
, sz
);
652 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
653 /* file_part isn't set in this case */
657 reqsize
= get_full_path_helper(name
, buffer
, size
);
658 if (!reqsize
) return 0;
661 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
662 if (tmp
== NULL
) return 0;
663 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
664 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
666 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
667 return reqsize
+ sizeof(WCHAR
);
669 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
670 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
674 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
684 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName
,
685 OUT PUNICODE_STRING NtPathName
,
686 OUT PCWSTR
*NtFileNamePart
,
687 OUT PRTL_RELATIVE_NAME_U DirectoryInfo
)
696 WCHAR fullname
[MAX_PATH
+ 1];
699 RtlInitUnicodeString (&us
, DosPathName
);
703 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
704 if (Buffer
[0] == L
'\\' && Buffer
[1] == L
'\\' &&
705 Buffer
[2] == L
'?' && Buffer
[3] == L
'\\')
707 /* allocate the new string and simply copy it */
708 NtPathName
->Length
= us
.Length
;
709 NtPathName
->MaximumLength
= us
.Length
+ sizeof(WCHAR
);
710 NtPathName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
712 NtPathName
->MaximumLength
);
713 if (NtPathName
->Buffer
== NULL
)
718 /* copy the string */
719 RtlCopyMemory(NtPathName
->Buffer
,
722 NtPathName
->Buffer
[us
.Length
/ sizeof(WCHAR
)] = L
'\0';
724 /* change the \\?\ prefix to \??\ */
725 NtPathName
->Buffer
[1] = L
'?';
727 if (NtFileNamePart
!= NULL
)
729 PWSTR FilePart
= NULL
;
732 /* try to find the last separator */
733 s
= NtPathName
->Buffer
+ (NtPathName
->Length
/ sizeof(WCHAR
));
734 while (s
!= NtPathName
->Buffer
)
744 *NtFileNamePart
= FilePart
;
747 if (DirectoryInfo
!= NULL
)
749 DirectoryInfo
->RelativeName
.Length
= 0;
750 DirectoryInfo
->RelativeName
.MaximumLength
= 0;
751 DirectoryInfo
->RelativeName
.Buffer
= NULL
;
752 DirectoryInfo
->ContainingDirectory
= NULL
;
759 Buffer
= RtlAllocateHeap (RtlGetProcessHeap (),
761 sizeof( fullname
) + MAX_PFX_SIZE
);
767 RtlAcquirePebLock ();
769 Size
= RtlGetFullPathName_U (DosPathName
,
772 (PWSTR
*)NtFileNamePart
);
773 if (Size
== 0 || Size
> MAX_PATH
* sizeof(WCHAR
))
775 RtlFreeHeap (RtlGetProcessHeap (),
778 RtlReleasePebLock ();
784 memcpy (Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
787 Type
= RtlDetermineDosPathNameType_U (fullname
);
791 memcpy (Buffer
+ tmpLength
, L
"UNC\\", 4 * sizeof(WCHAR
));
800 Length
= wcslen(fullname
+ Offset
);
801 memcpy (Buffer
+ tmpLength
, fullname
+ Offset
, (Length
+ 1) * sizeof(WCHAR
));
803 if (Type
== RtlPathTypeDriveAbsolute
||
804 Type
== RtlPathTypeDriveRelative
)
806 /* make the drive letter to uppercase */
807 Buffer
[tmpLength
] = towupper(Buffer
[tmpLength
]);
810 /* set NT filename */
811 NtPathName
->Length
= Length
* sizeof(WCHAR
);
812 NtPathName
->MaximumLength
= sizeof(fullname
) + MAX_PFX_SIZE
;
813 NtPathName
->Buffer
= Buffer
;
815 /* set pointer to file part if possible */
816 if (NtFileNamePart
&& *NtFileNamePart
)
817 *NtFileNamePart
= Buffer
+ Length
- wcslen (*NtFileNamePart
);
819 /* Set name and handle structure if possible */
822 memset (DirectoryInfo
, 0, sizeof(RTL_RELATIVE_NAME_U
));
823 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
824 if (Type
== 5 && cd
->Handle
)
826 RtlInitUnicodeString(&us
, fullname
);
827 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
829 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - Offset
) + ((Type
== 1) ? 8 : 4);
830 DirectoryInfo
->RelativeName
.Buffer
= Buffer
+ Length
;
831 DirectoryInfo
->RelativeName
.Length
= NtPathName
->Length
- (Length
* sizeof(WCHAR
));
832 DirectoryInfo
->RelativeName
.MaximumLength
= DirectoryInfo
->RelativeName
.Length
;
833 DirectoryInfo
->ContainingDirectory
= cd
->Handle
;
846 /******************************************************************
849 * Searches a file of name 'name' into a ';' separated list of paths
851 * Doesn't seem to search elsewhere than the paths list
852 * Stores the result in buffer (file_part will point to the position
853 * of the file name in the buffer)
855 * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
859 RtlDosSearchPath_U(PCWSTR paths
,
866 RTL_PATH_TYPE type
= RtlDetermineDosPathNameType_U(search
);
869 if (type
== RtlPathTypeRelative
)
871 ULONG allocated
= 0, needed
, filelen
;
874 filelen
= 1 /* for \ */ + wcslen(search
) + 1 /* \0 */;
876 /* Windows only checks for '.' without worrying about path components */
877 if (wcschr( search
, '.' )) ext
= NULL
;
878 if (ext
!= NULL
) filelen
+= wcslen(ext
);
884 for (needed
= 0, ptr
= paths
; *ptr
!= 0 && *ptr
++ != ';'; needed
++);
885 if (needed
+ filelen
> allocated
)
887 if (!name
) name
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
888 (needed
+ filelen
) * sizeof(WCHAR
));
891 WCHAR
*newname
= RtlReAllocateHeap(RtlGetProcessHeap(), 0, name
,
892 (needed
+ filelen
) * sizeof(WCHAR
));
893 if (!newname
) RtlFreeHeap(RtlGetProcessHeap(), 0, name
);
897 allocated
= needed
+ filelen
;
899 memmove(name
, paths
, needed
* sizeof(WCHAR
));
900 /* append '\\' if none is present */
901 if (needed
> 0 && name
[needed
- 1] != '\\') name
[needed
++] = '\\';
902 wcscpy(&name
[needed
], search
);
903 if (ext
) wcscat(&name
[needed
], ext
);
904 if (RtlDoesFileExists_U(name
))
906 len
= RtlGetFullPathName_U(name
, buffer_size
, buffer
, file_part
);
911 RtlFreeHeap(RtlGetProcessHeap(), 0, name
);
913 else if (RtlDoesFileExists_U(search
))
915 len
= RtlGetFullPathName_U(search
, buffer_size
, buffer
, file_part
);
926 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
927 IN BOOLEAN SucceedIfBusy
)
930 RTL_RELATIVE_NAME_U RelativeName
;
931 UNICODE_STRING NtPathName
;
933 OBJECT_ATTRIBUTES ObjectAttributes
;
935 FILE_BASIC_INFORMATION BasicInformation
;
938 /* Get the NT Path */
939 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
944 /* FIXME: Use the old API for now */
945 Result
= RtlDosPathNameToNtPathName_U(FileName
->Buffer
,
950 if (!Result
) return FALSE
;
952 /* Save the buffer */
953 Buffer
= NtPathName
.Buffer
;
955 /* Check if we have a relative name */
956 if (RelativeName
.RelativeName
.Length
)
959 NtPathName
= RelativeName
.RelativeName
;
963 /* Otherwise ignore it */
964 RelativeName
.ContainingDirectory
= NULL
;
967 /* Initialize the object attributes */
968 InitializeObjectAttributes(&ObjectAttributes
,
970 OBJ_CASE_INSENSITIVE
,
971 RelativeName
.ContainingDirectory
,
974 /* Query the attributes and free the buffer now */
975 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
976 RtlReleaseRelativeName(&RelativeName
);
977 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
979 /* Check if we failed */
980 if (!NT_SUCCESS(Status
))
982 /* Check if we failed because the file is in use */
983 if ((Status
== STATUS_SHARING_VIOLATION
) ||
984 (Status
== STATUS_ACCESS_DENIED
))
986 /* Check if the caller wants this to be considered OK */
987 Result
= SucceedIfBusy
? TRUE
: FALSE
;
991 /* A failure because the file didn't exist */
997 /* The file exists */
1001 /* Return the result */
1007 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1009 /* Call the updated API */
1010 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1019 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1020 IN BOOLEAN SucceedIfBusy
)
1022 UNICODE_STRING NameString
;
1024 /* Create the unicode name*/
1025 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1027 /* Call the unicode function */
1028 return NT_SUCCESS(RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
));
1040 RtlDoesFileExists_U(IN PCWSTR FileName
)
1042 /* Call the new function */
1043 return RtlDoesFileExists_UEx(FileName
, TRUE
);
1050 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1
,
1055 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
1056 Unknown1
, Unknown2
, Unknown3
, Unknown4
);
1061 RtlpEnsureBufferSize(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
)
1063 DPRINT1("RtlpEnsureBufferSize: stub\n");
1064 return STATUS_NOT_IMPLEMENTED
;
1068 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1070 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1071 return STATUS_NOT_IMPLEMENTED
;