Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpath.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS system libraries 00004 * FILE: lib/rtl/path.c 00005 * PURPOSE: Path and current directory functions 00006 * PROGRAMMERS: Wine team 00007 * Thomas Weidenmueller 00008 * Gunnar Dalsnes 00009 * Alex Ionescu (alex.ionescu@reactos.org) 00010 * Pierre Schweitzer (pierre@reactos.org) 00011 */ 00012 00013 /* INCLUDES *****************************************************************/ 00014 00015 #include <rtl.h> 00016 00017 #define NDEBUG 00018 #include <debug.h> 00019 00020 /* DEFINITONS and MACROS ******************************************************/ 00021 00022 #define MAX_PFX_SIZE 16 00023 00024 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/')) 00025 00026 #define RTL_CURDIR_IS_REMOVABLE 0x1 00027 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2 00028 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3 00029 C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS); 00030 00031 00032 /* GLOBALS ********************************************************************/ 00033 00034 static const WCHAR DeviceRootW[] = L"\\\\.\\"; 00035 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\"); 00036 00037 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\"); 00038 const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\"); 00039 const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON"); 00040 const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\"); 00041 00042 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT"); 00043 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM"); 00044 const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN"); 00045 const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX"); 00046 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON"); 00047 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL"); 00048 00049 PRTLP_CURDIR_REF RtlpCurDirRef; 00050 00051 /* PRIVATE FUNCTIONS **********************************************************/ 00052 00053 RTL_PATH_TYPE 00054 NTAPI 00055 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString) 00056 { 00057 PWCHAR Path; 00058 ULONG Chars; 00059 00060 Path = PathString->Buffer; 00061 Chars = PathString->Length / sizeof(WCHAR); 00062 00063 /* Return if there are no characters */ 00064 if (!Chars) return RtlPathTypeRelative; 00065 00066 /* 00067 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we 00068 * actually check for the path length before touching the characters 00069 */ 00070 if (IS_PATH_SEPARATOR(Path[0])) 00071 { 00072 if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */ 00073 if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */ 00074 if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ 00075 if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ 00076 return RtlPathTypeRootLocalDevice; /* \\. or \\? */ 00077 } 00078 else 00079 { 00080 if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ 00081 if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */ 00082 return RtlPathTypeDriveAbsolute; /* x:\ */ 00083 } 00084 } 00085 00086 ULONG 00087 NTAPI 00088 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString) 00089 { 00090 UNICODE_STRING PathCopy; 00091 PWCHAR Start, End; 00092 USHORT PathChars, ColonCount = 0; 00093 USHORT ReturnOffset = 0, ReturnLength, OriginalLength; 00094 WCHAR c; 00095 00096 /* Validate the input */ 00097 if (!PathString) return 0; 00098 00099 /* Check what type of path this is */ 00100 switch (RtlDetermineDosPathNameType_Ustr(PathString)) 00101 { 00102 /* Fail for UNC or unknown paths */ 00103 case RtlPathTypeUnknown: 00104 case RtlPathTypeUncAbsolute: 00105 return 0; 00106 00107 /* Make special check for the CON device */ 00108 case RtlPathTypeLocalDevice: 00109 if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE)) 00110 { 00111 /* This should return 0x80006 */ 00112 return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length); 00113 } 00114 return 0; 00115 00116 default: 00117 break; 00118 } 00119 00120 /* Make a copy of the string */ 00121 PathCopy = *PathString; 00122 OriginalLength = PathString->Length; 00123 00124 /* Return if there's no characters */ 00125 PathChars = PathCopy.Length / sizeof(WCHAR); 00126 if (!PathChars) return 0; 00127 00128 /* Check for drive path and truncate */ 00129 if (PathCopy.Buffer[PathChars - 1] == L':') 00130 { 00131 /* Fixup the lengths */ 00132 PathCopy.Length -= sizeof(WCHAR); 00133 if (!--PathChars) return 0; 00134 00135 /* Remember this for later */ 00136 ColonCount = 1; 00137 } 00138 00139 /* Check for extension or space, and truncate */ 00140 do 00141 { 00142 /* Stop if we hit something else than a space or period */ 00143 c = PathCopy.Buffer[PathChars - 1]; 00144 if ((c != '.') && (c != ' ')) break; 00145 00146 /* Fixup the lengths */ 00147 PathCopy.Length -= sizeof(WCHAR); 00148 00149 /* Remember this for later */ 00150 ColonCount++; 00151 } while (--PathChars); 00152 00153 /* Anything still left? */ 00154 if (PathChars) 00155 { 00156 /* Loop from the end */ 00157 for (End = &PathCopy.Buffer[PathChars - 1]; 00158 End >= PathCopy.Buffer; 00159 --End) 00160 { 00161 /* Check if the character is a path or drive separator */ 00162 c = *End; 00163 if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1))) 00164 { 00165 /* Get the next lower case character */ 00166 End++; 00167 c = *End | ' '; // ' ' == ('z' - 'Z') 00168 00169 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */ 00170 if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) && 00171 ((c == 'l') || (c == 'c') || (c == 'p') || (c == 'a') || (c == 'n'))) 00172 { 00173 /* Calculate the offset */ 00174 ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer); 00175 00176 /* Build the final string */ 00177 PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR)); 00178 PathCopy.Buffer = End; 00179 00180 /* Save new amount of chars in the path */ 00181 PathChars = PathCopy.Length / sizeof(WCHAR); 00182 00183 break; 00184 } 00185 else 00186 { 00187 return 0; 00188 } 00189 } 00190 } 00191 00192 /* Get the next lower case character and check if it's a DOS device */ 00193 c = *PathCopy.Buffer | ' '; // ' ' == ('z' - 'Z') 00194 if ((c != 'l') && (c != 'c') && (c != 'p') && (c != 'a') && (c != 'n')) 00195 { 00196 /* Not LPT, COM, PRN, AUX, or NUL */ 00197 return 0; 00198 } 00199 } 00200 00201 /* Now skip past any extra extension or drive letter characters */ 00202 Start = PathCopy.Buffer; 00203 End = &Start[PathChars]; 00204 while (Start < End) 00205 { 00206 c = *Start; 00207 if ((c == '.') || (c == ':')) break; 00208 Start++; 00209 } 00210 00211 /* And then go backwards to get rid of spaces */ 00212 while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start; 00213 00214 /* Finally see how many characters are left, and that's our size */ 00215 PathChars = (USHORT)(Start - PathCopy.Buffer); 00216 PathCopy.Length = PathChars * sizeof(WCHAR); 00217 00218 /* Check if this is a COM or LPT port, which has a digit after it */ 00219 if ((PathChars == 4) && 00220 (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != '0'))) 00221 { 00222 /* Don't compare the number part, just check for LPT or COM */ 00223 PathCopy.Length -= sizeof(WCHAR); 00224 if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) || 00225 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE))) 00226 { 00227 /* Found it */ 00228 ReturnLength = sizeof(L"COM1") - sizeof(WCHAR); 00229 return MAKELONG(ReturnLength, ReturnOffset); 00230 } 00231 } 00232 else if ((PathChars == 3) && 00233 ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) || 00234 (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) || 00235 (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) || 00236 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE)))) 00237 { 00238 /* Otherwise this was something like AUX, NUL, PRN, or CON */ 00239 ReturnLength = sizeof(L"AUX") - sizeof(WCHAR); 00240 return MAKELONG(ReturnLength, ReturnOffset); 00241 } 00242 00243 /* Otherwise, this is not a valid DOS device */ 00244 return 0; 00245 } 00246 00247 NTSTATUS 00248 NTAPI 00249 RtlpCheckDeviceName(IN PUNICODE_STRING FileName, 00250 IN ULONG Length, 00251 OUT PBOOLEAN NameInvalid) 00252 { 00253 PWCHAR Buffer; 00254 NTSTATUS Status; 00255 00256 /* Allocate a large enough buffer */ 00257 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length); 00258 if (Buffer) 00259 { 00260 /* Assume failure */ 00261 *NameInvalid = TRUE; 00262 00263 /* Copy the filename */ 00264 RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length); 00265 00266 /* And add a dot at the end */ 00267 Buffer[Length / sizeof(WCHAR)] = L'.'; 00268 Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; 00269 00270 /* Check if the file exists or not */ 00271 *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE; 00272 00273 /* Get rid of the buffer now */ 00274 Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 00275 } 00276 else 00277 { 00278 /* Assume the name is ok, but fail the call */ 00279 *NameInvalid = FALSE; 00280 Status = STATUS_NO_MEMORY; 00281 } 00282 00283 /* Return the status */ 00284 return Status; 00285 } 00286 00287 ULONG 00288 NTAPI 00289 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName, 00290 IN ULONG Size, 00291 IN PWSTR Buffer, 00292 OUT PCWSTR *ShortName, 00293 OUT PBOOLEAN InvalidName, 00294 OUT RTL_PATH_TYPE *PathType) 00295 { 00296 PWCHAR FileNameBuffer; 00297 ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength; 00298 WCHAR c; 00299 NTSTATUS Status; 00300 00301 /* For now, assume the name is valid */ 00302 DPRINT("Filename: %wZ\n", FileName); 00303 DPRINT("Size and buffer: %lx %S\n", Size, Buffer); 00304 if (InvalidName) *InvalidName = FALSE; 00305 00306 /* Handle initial path type and failure case */ 00307 *PathType = RtlPathTypeUnknown; 00308 if (!(FileName->Length) || (FileName->Buffer[0] == UNICODE_NULL)) return 0; 00309 00310 /* Break filename into component parts */ 00311 FileNameBuffer = FileName->Buffer; 00312 FileNameLength = FileName->Length; 00313 FileNameChars = FileNameLength / sizeof(WCHAR); 00314 00315 /* Kill trailing spaces */ 00316 c = FileNameBuffer[FileNameChars - 1]; 00317 while ((FileNameLength) && (c == L' ')) 00318 { 00319 /* Keep going, ignoring the spaces */ 00320 FileNameLength -= sizeof(WCHAR); 00321 if (FileNameLength) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1]; 00322 } 00323 00324 /* Check if anything is left */ 00325 if (!FileNameLength) return 0; 00326 00327 /* Check if this is a DOS name */ 00328 DosLength = RtlIsDosDeviceName_Ustr(FileName); 00329 DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName); 00330 if (DosLength) 00331 { 00332 /* Zero out the short name */ 00333 if (ShortName) *ShortName = NULL; 00334 00335 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */ 00336 DosLengthOffset = DosLength >> 16; 00337 DosLength = DosLength & 0xFFFF; 00338 00339 /* Do we have a DOS length, and does the caller want validity? */ 00340 if ((InvalidName) && (DosLengthOffset)) 00341 { 00342 /* Do the check */ 00343 Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName); 00344 00345 /* If the check failed, or the name is invalid, fail here */ 00346 if (!NT_SUCCESS(Status)) return 0; 00347 if (*InvalidName) return 0; 00348 } 00349 00350 /* Add the size of the device root and check if it fits in the size */ 00351 FullLength = DosLength + DeviceRootString.Length; 00352 if (FullLength < Size) 00353 { 00354 /* Add the device string */ 00355 RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length); 00356 00357 /* Now add the DOS device name */ 00358 RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length, 00359 (PCHAR)FileNameBuffer + DosLengthOffset, 00360 DosLength); 00361 00362 /* Null terminate */ 00363 *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL; 00364 return FullLength; 00365 } 00366 00367 /* Otherwise, there's no space, so return the buffer size needed */ 00368 if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0; 00369 return FullLength + sizeof(UNICODE_NULL); 00370 } 00371 00372 /* This should work well enough for our current needs */ 00373 *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer); 00374 DPRINT("Path type: %lx\n", *PathType); 00375 00376 /* This is disgusting... but avoids re-writing everything */ 00377 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer, Size, Buffer); 00378 return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName); 00379 } 00380 00381 NTSTATUS 00382 NTAPI 00383 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath, 00384 OUT PUNICODE_STRING NtPath, 00385 OUT PCWSTR *PartName, 00386 OUT PRTL_RELATIVE_NAME_U RelativeName) 00387 { 00388 ULONG DosLength; 00389 PWSTR NewBuffer, p; 00390 00391 /* Validate the input */ 00392 if (!DosPath) return STATUS_OBJECT_NAME_INVALID; 00393 00394 /* Validate the DOS length */ 00395 DosLength = DosPath->Length; 00396 if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG; 00397 00398 /* Make space for the new path */ 00399 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 00400 0, 00401 DosLength + sizeof(UNICODE_NULL)); 00402 if (!NewBuffer) return STATUS_NO_MEMORY; 00403 00404 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */ 00405 RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length); 00406 RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length, 00407 DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR), 00408 DosPath->Length - RtlpDosDevicesPrefix.Length); 00409 NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL; 00410 00411 /* Did the caller send a relative name? */ 00412 if (RelativeName) 00413 { 00414 /* Zero initialize it */ 00415 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); 00416 RelativeName->ContainingDirectory = NULL; 00417 RelativeName->CurDirRef = 0; 00418 } 00419 00420 /* Did the caller request a partial name? */ 00421 if (PartName) 00422 { 00423 /* Loop from the back until we find a path separator */ 00424 p = &NewBuffer[(DosLength - 1) / sizeof (WCHAR)]; 00425 while (p > NewBuffer) if (*p-- == '\\') break; 00426 00427 /* Was one found? */ 00428 if (p > NewBuffer) 00429 { 00430 /* Move past it -- anything left? */ 00431 p++; 00432 if (!*p) 00433 { 00434 /* The path ends with a path separator, no part name */ 00435 *PartName = NULL; 00436 } 00437 else 00438 { 00439 /* What follows the path separator is the part name */ 00440 *PartName = p; 00441 } 00442 } 00443 } 00444 00445 /* Build the final NT path string */ 00446 NtPath->Length = (USHORT)DosLength; 00447 NtPath->Buffer = NewBuffer; 00448 NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL); 00449 return STATUS_SUCCESS; 00450 } 00451 00452 NTSTATUS 00453 NTAPI 00454 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative, 00455 IN PCUNICODE_STRING DosName, 00456 OUT PUNICODE_STRING NtName, 00457 OUT PCWSTR *PartName, 00458 OUT PRTL_RELATIVE_NAME_U RelativeName) 00459 { 00460 WCHAR BigBuffer[MAX_PATH + 1]; 00461 PWCHAR PrefixBuffer, NewBuffer, Buffer; 00462 ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length; 00463 UNICODE_STRING CapturedDosName, PartNameString, FullPath; 00464 BOOLEAN QuickPath; 00465 RTL_PATH_TYPE InputPathType, BufferPathType; 00466 NTSTATUS Status; 00467 BOOLEAN NameInvalid; 00468 PCURDIR CurrentDirectory; 00469 00470 /* Assume MAX_PATH for now */ 00471 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n", 00472 HaveRelative, DosName, NtName, PartName, RelativeName); 00473 MaxLength = sizeof(BigBuffer); 00474 00475 /* Validate the input */ 00476 if (!DosName) return STATUS_OBJECT_NAME_INVALID; 00477 00478 /* Capture input string */ 00479 CapturedDosName = *DosName; 00480 00481 /* Check for \\?\\ form */ 00482 if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) || 00483 (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) || 00484 (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) || 00485 (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) || 00486 (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3])) 00487 { 00488 /* Quick path won't be used */ 00489 QuickPath = FALSE; 00490 00491 /* Use the static buffer */ 00492 Buffer = BigBuffer; 00493 MaxLength += RtlpDosDevicesUncPrefix.Length; 00494 00495 /* Allocate a buffer to hold the path */ 00496 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength); 00497 DPRINT("Length: %lx\n", MaxLength); 00498 if (!NewBuffer) return STATUS_NO_MEMORY; 00499 } 00500 else 00501 { 00502 /* Use the optimized path after acquiring the lock */ 00503 QuickPath = TRUE; 00504 NewBuffer = NULL; 00505 } 00506 00507 /* Lock the PEB and check if the quick path can be used */ 00508 RtlAcquirePebLock(); 00509 if (QuickPath) 00510 { 00511 /* Some simple fixups will get us the correct path */ 00512 DPRINT("Quick path\n"); 00513 Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName, 00514 NtName, 00515 PartName, 00516 RelativeName); 00517 00518 /* Release the lock, we're done here */ 00519 RtlReleasePebLock(); 00520 return Status; 00521 } 00522 00523 /* Call the main function to get the full path name and length */ 00524 PathLength = RtlGetFullPathName_Ustr(&CapturedDosName, 00525 MAX_PATH * sizeof(WCHAR), 00526 Buffer, 00527 PartName, 00528 &NameInvalid, 00529 &InputPathType); 00530 if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR)))) 00531 { 00532 /* Invalid name, fail */ 00533 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength); 00534 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 00535 RtlReleasePebLock(); 00536 return STATUS_OBJECT_NAME_INVALID; 00537 } 00538 00539 /* Start by assuming the path starts with \??\ (DOS Devices Path) */ 00540 PrefixLength = RtlpDosDevicesPrefix.Length; 00541 PrefixBuffer = RtlpDosDevicesPrefix.Buffer; 00542 PrefixCut = 0; 00543 00544 /* Check where it really is */ 00545 BufferPathType = RtlDetermineDosPathNameType_U(Buffer); 00546 DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType); 00547 switch (BufferPathType) 00548 { 00549 /* It's actually a UNC path in \??\UNC\ */ 00550 case RtlPathTypeUncAbsolute: 00551 PrefixLength = RtlpDosDevicesUncPrefix.Length; 00552 PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer; 00553 PrefixCut = 2; 00554 break; 00555 00556 case RtlPathTypeLocalDevice: 00557 /* We made a good guess, go with it but skip the \??\ */ 00558 PrefixCut = 4; 00559 break; 00560 00561 case RtlPathTypeDriveAbsolute: 00562 case RtlPathTypeDriveRelative: 00563 case RtlPathTypeRooted: 00564 case RtlPathTypeRelative: 00565 /* Our guess was good, roll with it */ 00566 break; 00567 00568 /* Nothing else is expected */ 00569 default: 00570 ASSERT(FALSE); 00571 00572 } 00573 00574 /* Now copy the prefix and the buffer */ 00575 RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength); 00576 RtlCopyMemory((PCHAR)NewBuffer + PrefixLength, 00577 &Buffer[PrefixCut], 00578 PathLength - (PrefixCut * sizeof(WCHAR))); 00579 00580 /* Compute the length */ 00581 Length = PathLength - PrefixCut * sizeof(WCHAR) + PrefixLength; 00582 LengthChars = Length / sizeof(WCHAR); 00583 00584 /* Setup the actual NT path string and terminate it */ 00585 NtName->Buffer = NewBuffer; 00586 NtName->Length = (USHORT)Length; 00587 NtName->MaximumLength = (USHORT)MaxLength; 00588 NewBuffer[LengthChars] = UNICODE_NULL; 00589 DPRINT("new buffer: %S\n", NewBuffer); 00590 DPRINT("NT Name: %wZ\n", NtName); 00591 00592 /* Check if a partial name was requested */ 00593 if ((PartName) && (*PartName)) 00594 { 00595 /* Convert to Unicode */ 00596 Status = RtlInitUnicodeStringEx(&PartNameString, *PartName); 00597 if (NT_SUCCESS(Status)) 00598 { 00599 /* Set the partial name */ 00600 *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))]; 00601 } 00602 else 00603 { 00604 /* Fail */ 00605 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 00606 RtlReleasePebLock(); 00607 return Status; 00608 } 00609 } 00610 00611 /* Check if a relative name was asked for */ 00612 if (RelativeName) 00613 { 00614 /* Setup the structure */ 00615 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); 00616 RelativeName->ContainingDirectory = NULL; 00617 RelativeName->CurDirRef = NULL; 00618 00619 /* Check if the input path itself was relative */ 00620 if (InputPathType == RtlPathTypeRelative) 00621 { 00622 /* Get current directory */ 00623 CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory); 00624 if (CurrentDirectory->Handle) 00625 { 00626 Status = RtlInitUnicodeStringEx(&FullPath, Buffer); 00627 if (!NT_SUCCESS(Status)) 00628 { 00629 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 00630 RtlReleasePebLock(); 00631 return Status; 00632 } 00633 00634 /* If current directory is bigger than full path, there's no way */ 00635 if (CurrentDirectory->DosPath.Length > FullPath.Length) 00636 { 00637 RtlReleasePebLock(); 00638 return Status; 00639 } 00640 00641 /* File is in current directory */ 00642 if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE)) 00643 { 00644 /* Make relative name string */ 00645 RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + FullPath.Length - PrefixCut); 00646 RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length); 00647 /* If relative name starts with \, skip it */ 00648 if (RelativeName->RelativeName.Buffer[0] == L'\\') 00649 { 00650 RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)RelativeName->RelativeName.Buffer + sizeof(WCHAR)); 00651 RelativeName->RelativeName.Length -= sizeof(WCHAR); 00652 } 00653 RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length; 00654 DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName)); 00655 00656 if (!HaveRelative) 00657 { 00658 RelativeName->ContainingDirectory = CurrentDirectory->Handle; 00659 return Status; 00660 } 00661 00662 /* Give back current directory data & reference counter */ 00663 RelativeName->CurDirRef = RtlpCurDirRef; 00664 if (RelativeName->CurDirRef) 00665 { 00666 InterlockedIncrement(&RtlpCurDirRef->RefCount); 00667 } 00668 00669 RelativeName->ContainingDirectory = CurrentDirectory->Handle; 00670 } 00671 } 00672 } 00673 } 00674 00675 /* Done */ 00676 RtlReleasePebLock(); 00677 return STATUS_SUCCESS; 00678 } 00679 00680 NTSTATUS 00681 NTAPI 00682 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative, 00683 IN PCWSTR DosName, 00684 OUT PUNICODE_STRING NtName, 00685 OUT PCWSTR *PartName, 00686 OUT PRTL_RELATIVE_NAME_U RelativeName) 00687 { 00688 NTSTATUS Status; 00689 UNICODE_STRING NameString; 00690 00691 /* Create the unicode name */ 00692 Status = RtlInitUnicodeStringEx(&NameString, DosName); 00693 if (NT_SUCCESS(Status)) 00694 { 00695 /* Call the unicode function */ 00696 Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative, 00697 &NameString, 00698 NtName, 00699 PartName, 00700 RelativeName); 00701 } 00702 00703 /* Return status */ 00704 return Status; 00705 } 00706 00707 BOOLEAN 00708 NTAPI 00709 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName, 00710 OUT PUNICODE_STRING NtName, 00711 OUT PCWSTR *PartName, 00712 OUT PRTL_RELATIVE_NAME_U RelativeName) 00713 { 00714 /* Call the internal function */ 00715 ASSERT(RelativeName); 00716 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE, 00717 DosName, 00718 NtName, 00719 PartName, 00720 RelativeName)); 00721 } 00722 00723 BOOLEAN 00724 NTAPI 00725 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName, 00726 IN BOOLEAN SucceedIfBusy) 00727 { 00728 BOOLEAN Result; 00729 RTL_RELATIVE_NAME_U RelativeName; 00730 UNICODE_STRING NtPathName; 00731 PVOID Buffer; 00732 OBJECT_ATTRIBUTES ObjectAttributes; 00733 NTSTATUS Status; 00734 FILE_BASIC_INFORMATION BasicInformation; 00735 00736 /* Get the NT Path */ 00737 Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName, 00738 &NtPathName, 00739 NULL, 00740 &RelativeName); 00741 if (!Result) return FALSE; 00742 00743 /* Save the buffer */ 00744 Buffer = NtPathName.Buffer; 00745 00746 /* Check if we have a relative name */ 00747 if (RelativeName.RelativeName.Length) 00748 { 00749 /* Use it */ 00750 NtPathName = RelativeName.RelativeName; 00751 } 00752 else 00753 { 00754 /* Otherwise ignore it */ 00755 RelativeName.ContainingDirectory = NULL; 00756 } 00757 00758 /* Initialize the object attributes */ 00759 InitializeObjectAttributes(&ObjectAttributes, 00760 &NtPathName, 00761 OBJ_CASE_INSENSITIVE, 00762 RelativeName.ContainingDirectory, 00763 NULL); 00764 00765 /* Query the attributes and free the buffer now */ 00766 Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation); 00767 RtlReleaseRelativeName(&RelativeName); 00768 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 00769 00770 /* Check if we failed */ 00771 if (!NT_SUCCESS(Status)) 00772 { 00773 /* Check if we failed because the file is in use */ 00774 if ((Status == STATUS_SHARING_VIOLATION) || 00775 (Status == STATUS_ACCESS_DENIED)) 00776 { 00777 /* Check if the caller wants this to be considered OK */ 00778 Result = SucceedIfBusy ? TRUE : FALSE; 00779 } 00780 else 00781 { 00782 /* A failure because the file didn't exist */ 00783 Result = FALSE; 00784 } 00785 } 00786 else 00787 { 00788 /* The file exists */ 00789 Result = TRUE; 00790 } 00791 00792 /* Return the result */ 00793 return Result; 00794 } 00795 00796 BOOLEAN 00797 NTAPI 00798 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName) 00799 { 00800 /* Call the updated API */ 00801 return RtlDoesFileExists_UstrEx(FileName, TRUE); 00802 } 00803 00804 BOOLEAN 00805 NTAPI 00806 RtlDoesFileExists_UEx(IN PCWSTR FileName, 00807 IN BOOLEAN SucceedIfBusy) 00808 { 00809 UNICODE_STRING NameString; 00810 00811 /* Create the unicode name*/ 00812 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName))) 00813 { 00814 /* Call the unicode function */ 00815 return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy); 00816 } 00817 00818 /* Fail */ 00819 return FALSE; 00820 } 00821 00822 /* PUBLIC FUNCTIONS ***********************************************************/ 00823 00824 /* 00825 * @implemented 00826 */ 00827 VOID 00828 NTAPI 00829 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName) 00830 { 00831 /* Check if a directory reference was grabbed */ 00832 if (RelativeName->CurDirRef) 00833 { 00834 /* Decrease reference count */ 00835 if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount)) 00836 { 00837 /* If no one uses it any longer, close handle & free */ 00838 NtClose(RelativeName->CurDirRef->Handle); 00839 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef); 00840 } 00841 RelativeName->CurDirRef = NULL; 00842 } 00843 } 00844 00845 /* 00846 * @implemented 00847 */ 00848 ULONG 00849 NTAPI 00850 RtlGetLongestNtPathLength(VOID) 00851 { 00852 /* 00853 * The longest NT path is a DOS path that actually sits on a UNC path (ie: 00854 * a mapped network drive), which is accessed through the DOS Global?? path. 00855 * This is, and has always been equal to, 269 characters, except in Wine 00856 * which claims this is 277. Go figure. 00857 */ 00858 return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL); 00859 } 00860 00861 /* 00862 * @implemented 00863 */ 00864 ULONG 00865 NTAPI 00866 RtlDetermineDosPathNameType_U(IN PCWSTR Path) 00867 { 00868 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path); 00869 00870 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */ 00871 if (IS_PATH_SEPARATOR(Path[0])) 00872 { 00873 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */ 00874 if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */ 00875 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ 00876 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ 00877 return RtlPathTypeRootLocalDevice; /* \\. or \\? */ 00878 } 00879 else 00880 { 00881 if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ 00882 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */ 00883 return RtlPathTypeDriveRelative; /* x: */ 00884 } 00885 } 00886 00887 /* 00888 * @implemented 00889 */ 00890 ULONG 00891 NTAPI 00892 RtlIsDosDeviceName_U(IN PCWSTR Path) 00893 { 00894 UNICODE_STRING PathString; 00895 NTSTATUS Status; 00896 00897 /* Build the string */ 00898 Status = RtlInitUnicodeStringEx(&PathString, Path); 00899 if (!NT_SUCCESS(Status)) return 0; 00900 00901 /* 00902 * Returns 0 if name is not valid DOS device name, or DWORD with 00903 * offset in bytes to DOS device name from beginning of buffer in high word 00904 * and size in bytes of DOS device name in low word 00905 */ 00906 return RtlIsDosDeviceName_Ustr(&PathString); 00907 } 00908 00909 /* 00910 * @implemented 00911 */ 00912 ULONG 00913 NTAPI 00914 RtlGetCurrentDirectory_U(IN ULONG MaximumLength, 00915 IN PWSTR Buffer) 00916 { 00917 ULONG Length, Bytes; 00918 PCURDIR CurDir; 00919 PWSTR CurDirName; 00920 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer); 00921 00922 /* Lock the PEB to get the current directory */ 00923 RtlAcquirePebLock(); 00924 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory; 00925 00926 /* Get the buffer and character length */ 00927 CurDirName = CurDir->DosPath.Buffer; 00928 Length = CurDir->DosPath.Length / sizeof(WCHAR); 00929 ASSERT((CurDirName != NULL) && (Length > 0)); 00930 00931 /* 00932 * DosPath.Buffer should always have a trailing slash. There is an assert 00933 * below which checks for this. 00934 * 00935 * This function either returns x:\ for a root (keeping the original buffer) 00936 * or it returns x:\path\foo for a directory (replacing the trailing slash 00937 * with a NULL. 00938 */ 00939 Bytes = Length * sizeof(WCHAR); 00940 if ((Length <= 1) || (CurDirName[Length - 2] == L':')) 00941 { 00942 /* Check if caller does not have enough space */ 00943 if (MaximumLength <= Bytes) 00944 { 00945 /* Call has no space for it, fail, add the trailing slash */ 00946 RtlReleasePebLock(); 00947 return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR); 00948 } 00949 } 00950 else 00951 { 00952 /* Check if caller does not have enough space */ 00953 if (MaximumLength < Bytes) 00954 { 00955 /* Call has no space for it, fail */ 00956 RtlReleasePebLock(); 00957 return Bytes; 00958 } 00959 } 00960 00961 /* Copy the buffer since we seem to have space */ 00962 RtlCopyMemory(Buffer, CurDirName, Bytes); 00963 00964 /* The buffer should end with a path separator */ 00965 ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR); 00966 00967 /* Again check for our two cases (drive root vs path) */ 00968 if ((Length <= 1) || (Buffer[Length - 2] != L':')) 00969 { 00970 /* Replace the trailing slash with a null */ 00971 Buffer[Length - 1] = UNICODE_NULL; 00972 --Length; 00973 } 00974 else 00975 { 00976 /* Append the null char since there's no trailing slash */ 00977 Buffer[Length] = UNICODE_NULL; 00978 } 00979 00980 /* Release PEB lock */ 00981 RtlReleasePebLock(); 00982 DPRINT("CurrentDirectory %S\n", Buffer); 00983 return Length * sizeof(WCHAR); 00984 } 00985 00986 /* 00987 * @implemented 00988 */ 00989 NTSTATUS 00990 NTAPI 00991 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path) 00992 { 00993 PCURDIR CurDir; 00994 NTSTATUS Status; 00995 RTL_PATH_TYPE PathType; 00996 IO_STATUS_BLOCK IoStatusBlock; 00997 UNICODE_STRING FullPath, NtName; 00998 PRTLP_CURDIR_REF OldCurDir = NULL; 00999 OBJECT_ATTRIBUTES ObjectAttributes; 01000 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo; 01001 ULONG SavedLength, CharLength, FullPathLength; 01002 HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL; 01003 01004 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path); 01005 01006 /* Initialize for failure case */ 01007 RtlInitEmptyUnicodeString(&NtName, NULL, 0); 01008 01009 /* Can't set current directory on DOS device */ 01010 if (RtlIsDosDeviceName_Ustr(Path)) 01011 { 01012 return STATUS_NOT_A_DIRECTORY; 01013 } 01014 01015 /* Get current directory */ 01016 RtlAcquirePebLock(); 01017 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory; 01018 01019 /* Check if we have to drop current handle */ 01020 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE) 01021 { 01022 OldHandle = CurDir->Handle; 01023 CurDir->Handle = NULL; 01024 } 01025 01026 /* Allocate a buffer for full path (using max possible length */ 01027 FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength); 01028 if (!FullPath.Buffer) 01029 { 01030 Status = STATUS_NO_MEMORY; 01031 goto Leave; 01032 } 01033 01034 /* Init string */ 01035 FullPath.Length = 0; 01036 FullPath.MaximumLength = CurDir->DosPath.MaximumLength; 01037 01038 /* Get new directory full path */ 01039 FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType); 01040 if (!FullPathLength) 01041 { 01042 Status = STATUS_OBJECT_NAME_INVALID; 01043 goto Leave; 01044 } 01045 01046 SavedLength = FullPath.MaximumLength; 01047 CharLength = FullPathLength / sizeof(WCHAR); 01048 01049 if (FullPathLength > FullPath.MaximumLength) 01050 { 01051 Status = STATUS_NAME_TOO_LONG; 01052 goto Leave; 01053 } 01054 01055 /* Translate it to NT name */ 01056 if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL)) 01057 { 01058 Status = STATUS_OBJECT_NAME_INVALID; 01059 goto Leave; 01060 } 01061 01062 InitializeObjectAttributes(&ObjectAttributes, &NtName, 01063 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, 01064 NULL, NULL); 01065 01066 /* If previous current directory was removable, then check it for dropping */ 01067 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS) 01068 { 01069 /* Get back normal handle */ 01070 CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS); 01071 CurDir->Handle = NULL; 01072 01073 /* Get device information */ 01074 Status = NtQueryVolumeInformationFile(CurDirHandle, 01075 &IoStatusBlock, 01076 &FileFsDeviceInfo, 01077 sizeof(FileFsDeviceInfo), 01078 FileFsDeviceInformation); 01079 /* Retry without taking care of removable device */ 01080 if (!NT_SUCCESS(Status)) 01081 { 01082 Status = RtlSetCurrentDirectory_U(Path); 01083 goto Leave; 01084 } 01085 } 01086 else 01087 { 01088 /* Open directory */ 01089 Status = NtOpenFile(&CurDirHandle, 01090 SYNCHRONIZE | FILE_TRAVERSE, 01091 &ObjectAttributes, 01092 &IoStatusBlock, 01093 FILE_SHARE_READ | FILE_SHARE_WRITE, 01094 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); 01095 if (!NT_SUCCESS(Status)) goto Leave; 01096 01097 /* Get device information */ 01098 Status = NtQueryVolumeInformationFile(CurDirHandle, 01099 &IoStatusBlock, 01100 &FileFsDeviceInfo, 01101 sizeof(FileFsDeviceInfo), 01102 FileFsDeviceInformation); 01103 if (!NT_SUCCESS(Status)) goto Leave; 01104 } 01105 01106 /* If device is removable, mark handle */ 01107 if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) 01108 { 01109 CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE); 01110 } 01111 01112 FullPath.Length = (USHORT)FullPathLength; 01113 01114 /* If full path isn't \ terminated, do it */ 01115 if (FullPath.Buffer[CharLength - 1] != L'\\') 01116 { 01117 if ((CharLength + 1) * sizeof(WCHAR) > SavedLength) 01118 { 01119 Status = STATUS_NAME_TOO_LONG; 01120 goto Leave; 01121 } 01122 01123 FullPath.Buffer[CharLength] = L'\\'; 01124 FullPath.Buffer[CharLength + 1] = UNICODE_NULL; 01125 FullPath.Length += sizeof(WCHAR); 01126 } 01127 01128 /* If we have previous current directory with only us as reference, save it */ 01129 if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1) 01130 { 01131 OldCurDirHandle = RtlpCurDirRef->Handle; 01132 } 01133 else 01134 { 01135 /* Allocate new current directory struct saving previous one */ 01136 OldCurDir = RtlpCurDirRef; 01137 RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF)); 01138 if (!RtlpCurDirRef) 01139 { 01140 RtlpCurDirRef = OldCurDir; 01141 OldCurDir = NULL; 01142 Status = STATUS_NO_MEMORY; 01143 goto Leave; 01144 } 01145 01146 /* Set reference to 1 (us) */ 01147 RtlpCurDirRef->RefCount = 1; 01148 } 01149 01150 /* Save new data */ 01151 CurDir->Handle = CurDirHandle; 01152 RtlpCurDirRef->Handle = CurDirHandle; 01153 CurDirHandle = NULL; 01154 01155 /* Copy full path */ 01156 RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR)); 01157 CurDir->DosPath.Length = FullPath.Length; 01158 01159 Status = STATUS_SUCCESS; 01160 01161 Leave: 01162 RtlReleasePebLock(); 01163 01164 if (FullPath.Buffer) 01165 { 01166 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer); 01167 } 01168 01169 if (NtName.Buffer) 01170 { 01171 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer); 01172 } 01173 01174 if (CurDirHandle) NtClose(CurDirHandle); 01175 01176 if (OldHandle) NtClose(OldHandle); 01177 01178 if (OldCurDirHandle) NtClose(OldCurDirHandle); 01179 01180 if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0) 01181 { 01182 NtClose(OldCurDir->Handle); 01183 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir); 01184 } 01185 01186 return Status; 01187 } 01188 01189 01190 /****************************************************************** 01191 * collapse_path 01192 * 01193 * Helper for RtlGetFullPathName_U. 01194 * Get rid of . and .. components in the path. 01195 */ 01196 void FORCEINLINE collapse_path( WCHAR *path, UINT mark ) 01197 { 01198 WCHAR *p, *next; 01199 01200 /* convert every / into a \ */ 01201 for (p = path; *p; p++) if (*p == '/') *p = '\\'; 01202 01203 /* collapse duplicate backslashes */ 01204 next = path + max( 1, mark ); 01205 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p; 01206 *next = 0; 01207 01208 p = path + mark; 01209 while (*p) 01210 { 01211 if (*p == '.') 01212 { 01213 switch(p[1]) 01214 { 01215 case '\\': /* .\ component */ 01216 next = p + 2; 01217 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); 01218 continue; 01219 case 0: /* final . */ 01220 if (p > path + mark) p--; 01221 *p = 0; 01222 continue; 01223 case '.': 01224 if (p[2] == '\\') /* ..\ component */ 01225 { 01226 next = p + 3; 01227 if (p > path + mark) 01228 { 01229 p--; 01230 while (p > path + mark && p[-1] != '\\') p--; 01231 } 01232 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); 01233 continue; 01234 } 01235 else if (!p[2]) /* final .. */ 01236 { 01237 if (p > path + mark) 01238 { 01239 p--; 01240 while (p > path + mark && p[-1] != '\\') p--; 01241 if (p > path + mark) p--; 01242 } 01243 *p = 0; 01244 continue; 01245 } 01246 break; 01247 } 01248 } 01249 /* skip to the next component */ 01250 while (*p && *p != '\\') p++; 01251 if (*p == '\\') 01252 { 01253 /* remove last dot in previous dir name */ 01254 if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) ); 01255 else p++; 01256 } 01257 } 01258 01259 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */ 01260 while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--; 01261 *p = 0; 01262 } 01263 01264 01265 01266 /****************************************************************** 01267 * skip_unc_prefix 01268 * 01269 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U. 01270 */ 01271 static const WCHAR *skip_unc_prefix( const WCHAR *ptr ) 01272 { 01273 ptr += 2; 01274 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */ 01275 while (IS_PATH_SEPARATOR(*ptr)) ptr++; 01276 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */ 01277 while (IS_PATH_SEPARATOR(*ptr)) ptr++; 01278 return ptr; 01279 } 01280 01281 01282 /****************************************************************** 01283 * get_full_path_helper 01284 * 01285 * Helper for RtlGetFullPathName_U 01286 * Note: name and buffer are allowed to point to the same memory spot 01287 */ 01288 static ULONG get_full_path_helper( 01289 LPCWSTR name, 01290 LPWSTR buffer, 01291 ULONG size) 01292 { 01293 SIZE_T reqsize = 0, mark = 0, dep = 0, deplen; 01294 LPWSTR ins_str = NULL; 01295 LPCWSTR ptr; 01296 const UNICODE_STRING* cd; 01297 WCHAR tmp[4]; 01298 01299 /* return error if name only consists of spaces */ 01300 for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break; 01301 if (!*ptr) return 0; 01302 01303 RtlAcquirePebLock(); 01304 01305 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath; 01306 cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath; 01307 01308 switch (RtlDetermineDosPathNameType_U(name)) 01309 { 01310 case RtlPathTypeUncAbsolute: /* \\foo */ 01311 ptr = skip_unc_prefix( name ); 01312 mark = (ptr - name); 01313 break; 01314 01315 case RtlPathTypeLocalDevice: /* \\.\foo */ 01316 mark = 4; 01317 break; 01318 01319 case RtlPathTypeDriveAbsolute: /* c:\foo */ 01320 reqsize = sizeof(WCHAR); 01321 tmp[0] = towupper(name[0]); 01322 ins_str = tmp; 01323 dep = 1; 01324 mark = 3; 01325 break; 01326 01327 case RtlPathTypeDriveRelative: /* c:foo */ 01328 dep = 2; 01329 if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':') 01330 { 01331 UNICODE_STRING var, val; 01332 01333 tmp[0] = '='; 01334 tmp[1] = name[0]; 01335 tmp[2] = ':'; 01336 tmp[3] = '\0'; 01337 var.Length = 3 * sizeof(WCHAR); 01338 var.MaximumLength = 4 * sizeof(WCHAR); 01339 var.Buffer = tmp; 01340 val.Length = 0; 01341 val.MaximumLength = (USHORT)size; 01342 val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size); 01343 if (val.Buffer == NULL) 01344 { 01345 reqsize = 0; 01346 goto done; 01347 } 01348 01349 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val)) 01350 { 01351 case STATUS_SUCCESS: 01352 /* FIXME: Win2k seems to check that the environment variable actually points 01353 * to an existing directory. If not, root of the drive is used 01354 * (this seems also to be the only spot in RtlGetFullPathName that the 01355 * existence of a part of a path is checked) 01356 */ 01357 /* fall thru */ 01358 case STATUS_BUFFER_TOO_SMALL: 01359 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */ 01360 val.Buffer[val.Length / sizeof(WCHAR)] = '\\'; 01361 ins_str = val.Buffer; 01362 break; 01363 case STATUS_VARIABLE_NOT_FOUND: 01364 reqsize = 3 * sizeof(WCHAR); 01365 tmp[0] = name[0]; 01366 tmp[1] = ':'; 01367 tmp[2] = '\\'; 01368 ins_str = tmp; 01369 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); 01370 break; 01371 default: 01372 DPRINT1("Unsupported status code\n"); 01373 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); 01374 break; 01375 } 01376 mark = 3; 01377 break; 01378 } 01379 /* fall through */ 01380 01381 case RtlPathTypeRelative: /* foo */ 01382 reqsize = cd->Length; 01383 ins_str = cd->Buffer; 01384 if (cd->Buffer[1] != ':') 01385 { 01386 ptr = skip_unc_prefix( cd->Buffer ); 01387 mark = ptr - cd->Buffer; 01388 } 01389 else mark = 3; 01390 break; 01391 01392 case RtlPathTypeRooted: /* \xxx */ 01393 #ifdef __WINE__ 01394 if (name[0] == '/') /* may be a Unix path */ 01395 { 01396 const WCHAR *ptr = name; 01397 int drive = find_drive_root( &ptr ); 01398 if (drive != -1) 01399 { 01400 reqsize = 3 * sizeof(WCHAR); 01401 tmp[0] = 'A' + drive; 01402 tmp[1] = ':'; 01403 tmp[2] = '\\'; 01404 ins_str = tmp; 01405 mark = 3; 01406 dep = ptr - name; 01407 break; 01408 } 01409 } 01410 #endif 01411 if (cd->Buffer[1] == ':') 01412 { 01413 reqsize = 2 * sizeof(WCHAR); 01414 tmp[0] = cd->Buffer[0]; 01415 tmp[1] = ':'; 01416 ins_str = tmp; 01417 mark = 3; 01418 } 01419 else 01420 { 01421 ptr = skip_unc_prefix( cd->Buffer ); 01422 reqsize = (ptr - cd->Buffer) * sizeof(WCHAR); 01423 mark = reqsize / sizeof(WCHAR); 01424 ins_str = cd->Buffer; 01425 } 01426 break; 01427 01428 case RtlPathTypeRootLocalDevice: /* \\. */ 01429 reqsize = 4 * sizeof(WCHAR); 01430 dep = 3; 01431 tmp[0] = '\\'; 01432 tmp[1] = '\\'; 01433 tmp[2] = '.'; 01434 tmp[3] = '\\'; 01435 ins_str = tmp; 01436 mark = 4; 01437 break; 01438 01439 case RtlPathTypeUnknown: 01440 goto done; 01441 } 01442 01443 /* enough space ? */ 01444 deplen = wcslen(name + dep) * sizeof(WCHAR); 01445 if (reqsize + deplen + sizeof(WCHAR) > size) 01446 { 01447 /* not enough space, return need size (including terminating '\0') */ 01448 reqsize += deplen + sizeof(WCHAR); 01449 goto done; 01450 } 01451 01452 memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR)); 01453 if (reqsize) memcpy(buffer, ins_str, reqsize); 01454 reqsize += deplen; 01455 01456 if (ins_str != tmp && ins_str != cd->Buffer) 01457 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str); 01458 01459 collapse_path( buffer, (ULONG)mark ); 01460 reqsize = wcslen(buffer) * sizeof(WCHAR); 01461 01462 done: 01463 RtlReleasePebLock(); 01464 return (ULONG)reqsize; 01465 } 01466 01467 01468 /****************************************************************** 01469 * RtlGetFullPathName_U (NTDLL.@) 01470 * 01471 * Returns the number of bytes written to buffer (not including the 01472 * terminating NULL) if the function succeeds, or the required number of bytes 01473 * (including the terminating NULL) if the buffer is too small. 01474 * 01475 * file_part will point to the filename part inside buffer (except if we use 01476 * DOS device name, in which case file_in_buf is NULL) 01477 * 01478 * @implemented 01479 */ 01480 ULONG NTAPI RtlGetFullPathName_U( 01481 const WCHAR* name, 01482 ULONG size, 01483 WCHAR* buffer, 01484 WCHAR** file_part) 01485 { 01486 WCHAR* ptr; 01487 ULONG dosdev; 01488 ULONG reqsize; 01489 01490 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part); 01491 01492 if (!name || !*name) return 0; 01493 01494 if (file_part) *file_part = NULL; 01495 01496 /* check for DOS device name */ 01497 dosdev = RtlIsDosDeviceName_U((WCHAR*)name); 01498 if (dosdev) 01499 { 01500 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */ 01501 DWORD sz = LOWORD(dosdev); /* in bytes */ 01502 01503 if (8 + sz + 2 > size) return sz + 10; 01504 wcscpy(buffer, DeviceRootW); 01505 memmove(buffer + 4, name + offset, sz); 01506 buffer[4 + sz / sizeof(WCHAR)] = '\0'; 01507 /* file_part isn't set in this case */ 01508 return sz + 8; 01509 } 01510 01511 reqsize = get_full_path_helper(name, buffer, size); 01512 if (!reqsize) return 0; 01513 if (reqsize > size) 01514 { 01515 LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize); 01516 if (tmp == NULL) return 0; 01517 reqsize = get_full_path_helper(name, tmp, reqsize); 01518 if (reqsize + sizeof(WCHAR) > size) /* it may have worked the second time */ 01519 { 01520 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); 01521 return reqsize + sizeof(WCHAR); 01522 } 01523 memcpy( buffer, tmp, reqsize + sizeof(WCHAR) ); 01524 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); 01525 } 01526 01527 /* find file part */ 01528 if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr) 01529 *file_part = ptr; 01530 return reqsize; 01531 } 01532 01533 /* 01534 * @implemented 01535 */ 01536 BOOLEAN 01537 NTAPI 01538 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName, 01539 OUT PUNICODE_STRING NtName, 01540 OUT PCWSTR *PartName, 01541 OUT PRTL_RELATIVE_NAME_U RelativeName) 01542 { 01543 /* Call the internal function */ 01544 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE, 01545 DosName, 01546 NtName, 01547 PartName, 01548 RelativeName)); 01549 } 01550 01551 /* 01552 * @implemented 01553 */ 01554 NTSTATUS 01555 NTAPI 01556 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName, 01557 OUT PUNICODE_STRING NtName, 01558 OUT PCWSTR *PartName, 01559 OUT PRTL_RELATIVE_NAME_U RelativeName) 01560 { 01561 /* Call the internal function */ 01562 return RtlpDosPathNameToRelativeNtPathName_U(FALSE, 01563 DosName, 01564 NtName, 01565 PartName, 01566 RelativeName); 01567 } 01568 01569 /* 01570 * @implemented 01571 */ 01572 BOOLEAN 01573 NTAPI 01574 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName, 01575 OUT PUNICODE_STRING NtName, 01576 OUT PCWSTR *PartName, 01577 OUT PRTL_RELATIVE_NAME_U RelativeName) 01578 { 01579 /* Call the internal function */ 01580 ASSERT(RelativeName); 01581 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE, 01582 DosName, 01583 NtName, 01584 PartName, 01585 RelativeName)); 01586 } 01587 01588 /* 01589 * @implemented 01590 */ 01591 NTSTATUS 01592 NTAPI 01593 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName, 01594 OUT PUNICODE_STRING NtName, 01595 OUT PCWSTR *PartName, 01596 OUT PRTL_RELATIVE_NAME_U RelativeName) 01597 { 01598 /* Call the internal function */ 01599 ASSERT(RelativeName); 01600 return RtlpDosPathNameToRelativeNtPathName_U(TRUE, 01601 DosName, 01602 NtName, 01603 PartName, 01604 RelativeName); 01605 } 01606 01607 /* 01608 * @unimplemented 01609 */ 01610 NTSTATUS NTAPI 01611 RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4) 01612 { 01613 DPRINT1("RtlNtPathNameToDosPathName: stub\n"); 01614 return STATUS_NOT_IMPLEMENTED; 01615 } 01616 01617 /* 01618 * @implemented 01619 */ 01620 ULONG 01621 NTAPI 01622 RtlDosSearchPath_U(IN PCWSTR Path, 01623 IN PCWSTR FileName, 01624 IN PCWSTR Extension, 01625 IN ULONG Size, 01626 IN PWSTR Buffer, 01627 OUT PWSTR *PartName) 01628 { 01629 NTSTATUS Status; 01630 ULONG ExtensionLength, Length, FileNameLength, PathLength; 01631 UNICODE_STRING TempString; 01632 PWCHAR NewBuffer, BufferStart; 01633 PCWSTR p; 01634 01635 /* Validate the input */ 01636 if (!(Path) || !(FileName)) return 0; 01637 01638 /* Check if this is an absolute path */ 01639 if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative) 01640 { 01641 /* Check if the file exists */ 01642 if (RtlDoesFileExists_UEx(FileName, TRUE)) 01643 { 01644 /* Get the full name, which does the DOS lookup */ 01645 return RtlGetFullPathName_U(FileName, Size, Buffer, PartName); 01646 } 01647 01648 /* Doesn't exist, so fail */ 01649 return 0; 01650 } 01651 01652 /* Scan the filename */ 01653 p = FileName; 01654 while (*p) 01655 { 01656 /* Looking for an extension */ 01657 if (*p == '.') 01658 { 01659 /* No extension string needed -- it's part of the filename */ 01660 Extension = NULL; 01661 break; 01662 } 01663 01664 /* Next character */ 01665 p++; 01666 } 01667 01668 /* Do we have an extension? */ 01669 if (!Extension) 01670 { 01671 /* Nope, don't worry about one */ 01672 ExtensionLength = 0; 01673 } 01674 else 01675 { 01676 /* Build a temporary string to get the extension length */ 01677 Status = RtlInitUnicodeStringEx(&TempString, Extension); 01678 if (!NT_SUCCESS(Status)) return 0; 01679 ExtensionLength = TempString.Length; 01680 } 01681 01682 /* Build a temporary string to get the path length */ 01683 Status = RtlInitUnicodeStringEx(&TempString, Path); 01684 if (!NT_SUCCESS(Status)) return 0; 01685 PathLength = TempString.Length; 01686 01687 /* Build a temporary string to get the filename length */ 01688 Status = RtlInitUnicodeStringEx(&TempString, FileName); 01689 if (!NT_SUCCESS(Status)) return 0; 01690 FileNameLength = TempString.Length; 01691 01692 /* Allocate the buffer for the new string name */ 01693 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 01694 0, 01695 FileNameLength + 01696 ExtensionLength + 01697 PathLength + 01698 3 * sizeof(WCHAR)); 01699 if (!NewBuffer) 01700 { 01701 /* Fail the call */ 01702 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n", 01703 __FUNCTION__); 01704 return 0; 01705 } 01706 01707 /* Final loop to build the path */ 01708 while (TRUE) 01709 { 01710 /* Check if we have a valid character */ 01711 BufferStart = NewBuffer; 01712 if (*Path) 01713 { 01714 /* Loop as long as there's no semicolon */ 01715 while (*Path != ';') 01716 { 01717 /* Copy the next character */ 01718 *BufferStart++ = *Path++; 01719 if (!*Path) break; 01720 } 01721 01722 /* We found a semi-colon, to stop path processing on this loop */ 01723 if (*Path == ';') ++Path; 01724 } 01725 01726 /* Add a terminating slash if needed */ 01727 if ((BufferStart != NewBuffer) && (BufferStart[-1] != '\\')) 01728 { 01729 *BufferStart++ = '\\'; 01730 } 01731 01732 /* Bail out if we reached the end */ 01733 if (!*Path) Path = NULL; 01734 01735 /* Copy the file name and check if an extension is needed */ 01736 RtlCopyMemory(BufferStart, FileName, FileNameLength); 01737 if (ExtensionLength) 01738 { 01739 /* Copy the extension too */ 01740 RtlCopyMemory((PCHAR)BufferStart + FileNameLength, 01741 Extension, 01742 ExtensionLength + sizeof(WCHAR)); 01743 } 01744 else 01745 { 01746 /* Just NULL-terminate */ 01747 *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL; 01748 } 01749 01750 /* Now, does this file exist? */ 01751 if (RtlDoesFileExists_UEx(NewBuffer, FALSE)) 01752 { 01753 /* Call the full-path API to get the length */ 01754 Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName); 01755 break; 01756 } 01757 01758 /* If we got here, path doesn't exist, so fail the call */ 01759 Length = 0; 01760 if (!Path) break; 01761 } 01762 01763 /* Free the allocation and return the length */ 01764 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 01765 return Length; 01766 } 01767 01768 /* 01769 * @implemented 01770 */ 01771 NTSTATUS 01772 NTAPI 01773 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName, 01774 IN PUNICODE_STRING StaticString, 01775 IN PUNICODE_STRING DynamicString, 01776 IN PUNICODE_STRING *StringUsed, 01777 IN PSIZE_T FilePartSize, 01778 OUT PBOOLEAN NameInvalid, 01779 OUT RTL_PATH_TYPE* PathType, 01780 OUT PSIZE_T LengthNeeded) 01781 { 01782 NTSTATUS Status; 01783 PWCHAR StaticBuffer; 01784 PCWCH ShortName; 01785 ULONG Length; 01786 USHORT StaticLength; 01787 UNICODE_STRING TempDynamicString; 01788 01789 /* Initialize all our locals */ 01790 ShortName = NULL; 01791 StaticBuffer = NULL; 01792 TempDynamicString.Buffer = NULL; 01793 01794 /* Initialize the input parameters */ 01795 if (StringUsed) *StringUsed = NULL; 01796 if (LengthNeeded) *LengthNeeded = 0; 01797 if (FilePartSize) *FilePartSize = 0; 01798 01799 /* Check for invalid parameters */ 01800 if ((DynamicString) && !(StringUsed) && (StaticString)) 01801 { 01802 return STATUS_INVALID_PARAMETER; 01803 } 01804 01805 /* Check if we did not get an input string */ 01806 if (!StaticString) 01807 { 01808 /* Allocate one */ 01809 StaticLength = MAX_PATH * sizeof(WCHAR); 01810 StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR); 01811 if (!StaticBuffer) return STATUS_NO_MEMORY; 01812 } 01813 else 01814 { 01815 /* Use the one we received */ 01816 StaticBuffer = StaticString->Buffer; 01817 StaticLength = StaticString->MaximumLength; 01818 } 01819 01820 /* Call the lower-level function */ 01821 Length = RtlGetFullPathName_Ustr(FileName, 01822 StaticLength, 01823 StaticBuffer, 01824 &ShortName, 01825 NameInvalid, 01826 PathType); 01827 DPRINT("Length: %d StaticBuffer: %S\n", Length, StaticBuffer); 01828 if (!Length) 01829 { 01830 /* Fail if it failed */ 01831 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n", 01832 __FUNCTION__, 01833 __LINE__); 01834 Status = STATUS_OBJECT_NAME_INVALID; 01835 goto Quickie; 01836 } 01837 01838 /* Check if it fits inside our static string */ 01839 if ((StaticString) && (Length < StaticLength)) 01840 { 01841 /* Set the final length */ 01842 StaticString->Length = (USHORT)Length; 01843 01844 /* Set the file part size */ 01845 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 01846 01847 /* Return the static string if requested */ 01848 if (StringUsed) *StringUsed = StaticString; 01849 01850 /* We are done with success */ 01851 Status = STATUS_SUCCESS; 01852 goto Quickie; 01853 } 01854 01855 /* Did we not have an input dynamic string ?*/ 01856 if (!DynamicString) 01857 { 01858 /* Return the length we need */ 01859 if (LengthNeeded) *LengthNeeded = Length; 01860 01861 /* And fail such that the caller can try again */ 01862 Status = STATUS_BUFFER_TOO_SMALL; 01863 goto Quickie; 01864 } 01865 01866 /* Check if it fits in our static buffer */ 01867 if ((StaticBuffer) && (Length < StaticLength)) 01868 { 01869 /* NULL-terminate it */ 01870 StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 01871 01872 /* Set the settings for the dynamic string the caller sent */ 01873 DynamicString->MaximumLength = StaticLength; 01874 DynamicString->Length = (USHORT)Length; 01875 DynamicString->Buffer = StaticBuffer; 01876 01877 /* Set the part size */ 01878 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0; 01879 01880 /* Return the dynamic string if requested */ 01881 if (StringUsed) *StringUsed = DynamicString; 01882 01883 /* Do not free the static buffer on exit, and return success */ 01884 StaticBuffer = NULL; 01885 Status = STATUS_SUCCESS; 01886 goto Quickie; 01887 } 01888 01889 /* Now try again under the PEB lock */ 01890 RtlAcquirePebLock(); 01891 Length = RtlGetFullPathName_Ustr(FileName, 01892 StaticLength, 01893 StaticBuffer, 01894 &ShortName, 01895 NameInvalid, 01896 PathType); 01897 if (!Length) 01898 { 01899 /* It failed */ 01900 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 01901 __FUNCTION__, __LINE__); 01902 Status = STATUS_OBJECT_NAME_INVALID; 01903 goto Release; 01904 } 01905 01906 /* Check if it fits inside our static string now */ 01907 if ((StaticString) && (Length < StaticLength)) 01908 { 01909 /* Set the final length */ 01910 StaticString->Length = (USHORT)Length; 01911 01912 /* Set the file part size */ 01913 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 01914 01915 /* Return the static string if requested */ 01916 if (StringUsed) *StringUsed = StaticString; 01917 01918 /* We are done with success */ 01919 Status = STATUS_SUCCESS; 01920 goto Release; 01921 } 01922 01923 /* Check if the path won't even fit in a real string */ 01924 if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) 01925 { 01926 /* Name is way too long, fail */ 01927 Status = STATUS_NAME_TOO_LONG; 01928 goto Release; 01929 } 01930 01931 /* Allocate the string to hold the path name now */ 01932 TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR), 01933 TAG_USTR); 01934 if (!TempDynamicString.Buffer) 01935 { 01936 /* Out of memory, fail */ 01937 Status = STATUS_NO_MEMORY; 01938 goto Release; 01939 } 01940 01941 /* Add space for a NULL terminator, and now check the full path */ 01942 TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL); 01943 Length = RtlGetFullPathName_Ustr(FileName, 01944 Length, 01945 TempDynamicString.Buffer, 01946 &ShortName, 01947 NameInvalid, 01948 PathType); 01949 if (!Length) 01950 { 01951 /* Some path error, so fail out */ 01952 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 01953 __FUNCTION__, __LINE__); 01954 Status = STATUS_OBJECT_NAME_INVALID; 01955 goto Release; 01956 } 01957 01958 /* It should fit in the string we just allocated */ 01959 ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR))); 01960 if (Length > TempDynamicString.MaximumLength) 01961 { 01962 /* This is really weird and would mean some kind of race */ 01963 Status = STATUS_INTERNAL_ERROR; 01964 goto Release; 01965 } 01966 01967 /* Return the file part size */ 01968 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0; 01969 01970 /* Terminate the whole string now */ 01971 TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 01972 01973 /* Finalize the string and return it to the user */ 01974 DynamicString->Buffer = TempDynamicString.Buffer; 01975 DynamicString->Length = (USHORT)Length; 01976 DynamicString->MaximumLength = TempDynamicString.MaximumLength; 01977 if (StringUsed) *StringUsed = DynamicString; 01978 01979 /* Return success and make sure we don't free the buffer on exit */ 01980 TempDynamicString.Buffer = NULL; 01981 Status = STATUS_SUCCESS; 01982 01983 Release: 01984 /* Release the PEB lock */ 01985 RtlReleasePebLock(); 01986 01987 Quickie: 01988 /* Free any buffers we should be freeing */ 01989 DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer); 01990 if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer)) 01991 { 01992 RtlpFreeMemory(StaticBuffer, TAG_USTR); 01993 } 01994 if (TempDynamicString.Buffer) 01995 { 01996 RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR); 01997 } 01998 01999 /* Print out any unusual errors */ 02000 if ((NT_ERROR(Status)) && 02001 (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) 02002 { 02003 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n", 02004 __FUNCTION__, FileName, Status); 02005 } 02006 02007 /* Return, we're all done */ 02008 return Status; 02009 } 02010 02011 /* 02012 * @implemented 02013 */ 02014 NTSTATUS 02015 NTAPI 02016 RtlDosSearchPath_Ustr(IN ULONG Flags, 02017 IN PUNICODE_STRING PathString, 02018 IN PUNICODE_STRING FileNameString, 02019 IN PUNICODE_STRING ExtensionString, 02020 IN PUNICODE_STRING CallerBuffer, 02021 IN OUT PUNICODE_STRING DynamicString OPTIONAL, 02022 OUT PUNICODE_STRING* FullNameOut OPTIONAL, 02023 OUT PSIZE_T FilePartSize OPTIONAL, 02024 OUT PSIZE_T LengthNeeded OPTIONAL) 02025 { 02026 WCHAR StaticCandidateBuffer[MAX_PATH]; 02027 UNICODE_STRING StaticCandidateString; 02028 NTSTATUS Status; 02029 RTL_PATH_TYPE PathType; 02030 PWCHAR p, End, CandidateEnd, SegmentEnd; 02031 SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0; 02032 USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0; 02033 PUNICODE_STRING FullIsolatedPath; 02034 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n", 02035 Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString); 02036 02037 /* Initialize the input string */ 02038 RtlInitEmptyUnicodeString(&StaticCandidateString, 02039 StaticCandidateBuffer, 02040 sizeof(StaticCandidateBuffer)); 02041 02042 /* Initialize optional arguments */ 02043 if (FullNameOut) *FullNameOut = NULL; 02044 if (FilePartSize) *FilePartSize = 0; 02045 if (DynamicString) 02046 { 02047 DynamicString->Length = DynamicString->MaximumLength = 0; 02048 DynamicString->Buffer = NULL; 02049 } 02050 02051 /* Check for invalid parameters */ 02052 if ((Flags & ~7) || 02053 !(PathString) || 02054 !(FileNameString) || 02055 ((CallerBuffer) && (DynamicString) && !(FullNameOut))) 02056 { 02057 /* Fail */ 02058 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__); 02059 Status = STATUS_INVALID_PARAMETER; 02060 goto Quickie; 02061 } 02062 02063 /* First check what kind of path this is */ 02064 PathType = RtlDetermineDosPathNameType_Ustr(FileNameString); 02065 02066 /* Check if the caller wants to prevent relative .\ and ..\ paths */ 02067 if ((Flags & 2) && 02068 (PathType == RtlPathTypeRelative) && 02069 (FileNameString->Length >= (2 * sizeof(WCHAR))) && 02070 (FileNameString->Buffer[0] == L'.') && 02071 ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) || 02072 ((FileNameString->Buffer[1] == L'.') && 02073 ((FileNameString->Length >= (3 * sizeof(WCHAR))) && 02074 (IS_PATH_SEPARATOR(FileNameString->Buffer[2])))))) 02075 { 02076 /* Yes, and this path is like that, so make it seem unknown */ 02077 PathType = RtlPathTypeUnknown; 02078 } 02079 02080 /* Now check relative vs non-relative paths */ 02081 if (PathType == RtlPathTypeRelative) 02082 { 02083 /* Does the caller want SxS? */ 02084 if (Flags & 1) 02085 { 02086 /* Apply the SxS magic */ 02087 FullIsolatedPath = NULL; 02088 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE, 02089 FileNameString, 02090 ExtensionString, 02091 CallerBuffer, 02092 DynamicString, 02093 &FullIsolatedPath, 02094 NULL, 02095 FilePartSize, 02096 LengthNeeded); 02097 if (NT_SUCCESS(Status)) 02098 { 02099 /* We found the SxS path, return it */ 02100 if (FullNameOut) *FullNameOut = FullIsolatedPath; 02101 goto Quickie; 02102 } 02103 else if (Status != STATUS_SXS_KEY_NOT_FOUND) 02104 { 02105 /* Critical SxS error, fail */ 02106 DbgPrint("%s: Failing because call to " 02107 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with " 02108 "status 0x%08lx\n", 02109 __FUNCTION__, 02110 FileNameString, 02111 Status); 02112 goto Quickie; 02113 } 02114 } 02115 02116 /* No SxS key found, or not requested, check if there's an extension */ 02117 if (ExtensionString) 02118 { 02119 /* Save the extension length, and check if there's a file name */ 02120 ExtensionLength = ExtensionString->Length; 02121 if (FileNameString->Length) 02122 { 02123 /* Start parsing the file name */ 02124 End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)]; 02125 while (End > FileNameString->Buffer) 02126 { 02127 /* If we find a path separator, there's no extension */ 02128 if (IS_PATH_SEPARATOR(*--End)) break; 02129 02130 /* Otherwise, did we find an extension dot? */ 02131 if (*End == L'.') 02132 { 02133 /* Ignore what the caller sent it, use the filename's */ 02134 ExtensionString = NULL; 02135 ExtensionLength = 0; 02136 break; 02137 } 02138 } 02139 } 02140 } 02141 02142 /* Check if we got a path */ 02143 if (PathString->Length) 02144 { 02145 /* Start parsing the path name, looking for path separators */ 02146 End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)]; 02147 p = End; 02148 while ((p > PathString->Buffer) && (*--p == L';')) 02149 { 02150 /* This is the size of the path -- handle a trailing slash */ 02151 PathSize = End - p - 1; 02152 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 02153 02154 /* Check if we found a bigger path than before */ 02155 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 02156 02157 /* Keep going with the path after this path separator */ 02158 End = p; 02159 } 02160 02161 /* This is the trailing path, run the same code as above */ 02162 PathSize = End - p; 02163 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 02164 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 02165 02166 /* Finally, convert the largest path size into WCHAR */ 02167 MaxPathSize *= sizeof(WCHAR); 02168 } 02169 02170 /* Use the extension, the file name, and the largest path as the size */ 02171 WorstCaseLength = ExtensionLength + 02172 FileNameString->Length + 02173 (USHORT)MaxPathSize + 02174 sizeof(UNICODE_NULL); 02175 if (WorstCaseLength > UNICODE_STRING_MAX_BYTES) 02176 { 02177 /* It has to fit in a registry string, if not, fail here */ 02178 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed " 02179 "worst case file name length is %Iu bytes\n", 02180 __FUNCTION__, 02181 WorstCaseLength); 02182 Status = STATUS_NAME_TOO_LONG; 02183 goto Quickie; 02184 } 02185 02186 /* Scan the path now, to see if we can find the file */ 02187 p = PathString->Buffer; 02188 End = &p[PathString->Length / sizeof(WCHAR)]; 02189 while (p < End) 02190 { 02191 /* Find out where this path ends */ 02192 for (SegmentEnd = p; 02193 ((SegmentEnd != End) && (*SegmentEnd != L';')); 02194 SegmentEnd++); 02195 02196 /* Compute the size of this path */ 02197 ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR); 02198 02199 /* Handle trailing slash if there isn't one */ 02200 if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1)))) 02201 { 02202 /* Add space for one */ 02203 SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR); 02204 } 02205 02206 /* Now check if our initial static buffer is too small */ 02207 if (StaticCandidateString.MaximumLength < 02208 (SegmentSize + ExtensionLength + FileNameString->Length)) 02209 { 02210 /* At this point we should've been using our static buffer */ 02211 ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer); 02212 if (StaticCandidateString.Buffer != StaticCandidateBuffer) 02213 { 02214 /* Something is really messed up if this was the dynamic string */ 02215 DbgPrint("%s: internal error #1; " 02216 "CandidateString.Buffer = %p; " 02217 "StaticCandidateBuffer = %p\n", 02218 __FUNCTION__, 02219 StaticCandidateString.Buffer, 02220 StaticCandidateBuffer); 02221 Status = STATUS_INTERNAL_ERROR; 02222 goto Quickie; 02223 } 02224 02225 /* We checked before that the maximum possible size shoudl fit! */ 02226 ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) < 02227 UNICODE_STRING_MAX_BYTES); 02228 if ((SegmentSize + ExtensionLength + FileNameString->Length) > 02229 (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR))) 02230 { 02231 /* For some reason it's not fitting anymore. Something messed up */ 02232 DbgPrint("%s: internal error #2; SegmentSize = %u, " 02233 "FileName->Length = %u, DefaultExtensionLength = %u\n", 02234 __FUNCTION__, 02235 SegmentSize, 02236 FileNameString->Length, 02237 ExtensionLength); 02238 Status = STATUS_INTERNAL_ERROR; 02239 goto Quickie; 02240 } 02241 02242 /* Now allocate the dynamic string */ 02243 StaticCandidateString.MaximumLength = FileNameString->Length + 02244 WorstCaseLength; 02245 StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength, 02246 TAG_USTR); 02247 if (!StaticCandidateString.Buffer) 02248 { 02249 /* Out of memory, fail */ 02250 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n", 02251 __FUNCTION__, 02252 StaticCandidateString.MaximumLength); 02253 Status = STATUS_NO_MEMORY; 02254 goto Quickie; 02255 } 02256 } 02257 02258 /* Copy the path in the string */ 02259 RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount); 02260 02261 /* Get to the end of the string, and add the trailing slash if missing */ 02262 CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)]; 02263 if ((SegmentSize) && (SegmentSize != ByteCount)) 02264 { 02265 *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR; 02266 } 02267 02268 /* Copy the filename now */ 02269 RtlCopyMemory(CandidateEnd, 02270 FileNameString->Buffer, 02271 FileNameString->Length); 02272 CandidateEnd += (FileNameString->Length / sizeof(WCHAR)); 02273 02274 /* Check if there was an extension */ 02275 if (ExtensionString) 02276 { 02277 /* Copy the extension too */ 02278 RtlCopyMemory(CandidateEnd, 02279 ExtensionString->Buffer, 02280 ExtensionString->Length); 02281 CandidateEnd += (ExtensionString->Length / sizeof(WCHAR)); 02282 } 02283 02284 /* We are done, terminate it */ 02285 *CandidateEnd = UNICODE_NULL; 02286 02287 /* Now set the final length of the string so it becomes valid */ 02288 StaticCandidateString.Length = (USHORT)(CandidateEnd - 02289 StaticCandidateString.Buffer) * 02290 sizeof(WCHAR); 02291 02292 /* Check if this file exists */ 02293 DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer); 02294 if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE)) 02295 { 02296 /* Awesome, it does, now get the full path */ 02297 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 02298 CallerBuffer, 02299 DynamicString, 02300 (PUNICODE_STRING*)FullNameOut, 02301 FilePartSize, 02302 NULL, 02303 &PathType, 02304 LengthNeeded); 02305 if (!(NT_SUCCESS(Status)) && 02306 ((Status != STATUS_NO_SUCH_FILE) && 02307 (Status != STATUS_BUFFER_TOO_SMALL))) 02308 { 02309 DbgPrint("%s: Failing because we thought we found %wZ on " 02310 "the search path, but RtlGetfullPathNameUStrEx() " 02311 "returned %08lx\n", 02312 __FUNCTION__, 02313 Status); 02314 } 02315 DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 02316 goto Quickie; 02317 } 02318 else 02319 { 02320 /* Otherwise, move to the next path */ 02321 if (SegmentEnd != End) 02322 { 02323 /* Handle the case of the path separator trailing */ 02324 p = SegmentEnd + 1; 02325 } 02326 else 02327 { 02328 p = SegmentEnd; 02329 } 02330 } 02331 } 02332 02333 /* Loop finished and we didn't break out -- fail */ 02334 Status = STATUS_NO_SUCH_FILE; 02335 } 02336 else 02337 { 02338 /* We have a full path, so check if it does exist */ 02339 DPRINT("%wZ\n", FileNameString); 02340 if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE)) 02341 { 02342 /* It doesn't exist, did we have an extension? */ 02343 if (!(ExtensionString) || !(ExtensionString->Length)) 02344 { 02345 /* No extension, so just fail */ 02346 Status = STATUS_NO_SUCH_FILE; 02347 goto Quickie; 02348 } 02349 02350 /* There was an extension, check if the filename already had one */ 02351 if (!(Flags & 4) && (FileNameString->Length)) 02352 { 02353 /* Parse the filename */ 02354 p = FileNameString->Buffer; 02355 End = &p[FileNameString->Length / sizeof(WCHAR)]; 02356 while (End > p) 02357 { 02358 /* If there's a path separator, there's no extension */ 02359 if (IS_PATH_SEPARATOR(*--End)) break; 02360 02361 /* Othwerwise, did we find an extension dot? */ 02362 if (*End == L'.') 02363 { 02364 /* File already had an extension, so fail */ 02365 Status = STATUS_NO_SUCH_FILE; 02366 goto Quickie; 02367 } 02368 } 02369 } 02370 02371 /* So there is an extension, we'll try again by adding it */ 02372 NamePlusExtLength = FileNameString->Length + 02373 ExtensionString->Length + 02374 sizeof(UNICODE_NULL); 02375 if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES) 02376 { 02377 /* It won't fit in any kind of valid string, so fail */ 02378 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n", 02379 __FUNCTION__, 02380 NamePlusExtLength); 02381 Status = STATUS_NAME_TOO_LONG; 02382 goto Quickie; 02383 } 02384 02385 /* Fill it fit in our temporary string? */ 02386 if (NamePlusExtLength > StaticCandidateString.MaximumLength) 02387 { 02388 /* It won't fit anymore, allocate a dynamic string for it */ 02389 StaticCandidateString.MaximumLength = NamePlusExtLength; 02390 StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength, 02391 TAG_USTR); 02392 if (!StaticCandidateString.Buffer) 02393 { 02394 /* Ran out of memory, so fail */ 02395 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n", 02396 __FUNCTION__); 02397 Status = STATUS_NO_MEMORY; 02398 goto Quickie; 02399 } 02400 } 02401 02402 /* Copy the filename */ 02403 RtlCopyUnicodeString(&StaticCandidateString, FileNameString); 02404 02405 /* Copy the extension */ 02406 RtlAppendUnicodeStringToString(&StaticCandidateString, 02407 ExtensionString); 02408 02409 DPRINT("SB: %wZ\n", &StaticCandidateString); 02410 02411 /* And check if this file now exists */ 02412 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE)) 02413 { 02414 /* Still no joy, fail out */ 02415 Status = STATUS_NO_SUCH_FILE; 02416 goto Quickie; 02417 } 02418 02419 /* File was found, get the final full path */ 02420 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 02421 CallerBuffer, 02422 DynamicString, 02423 (PUNICODE_STRING*)FullNameOut, 02424 FilePartSize, 02425 NULL, 02426 &PathType, 02427 LengthNeeded); 02428 if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE)) 02429 { 02430 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() " 02431 "failed with status %08lx\n", 02432 __FUNCTION__, 02433 &StaticCandidateString, 02434 Status); 02435 } 02436 DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 02437 } 02438 else 02439 { 02440 /* File was found on the first try, get the final full path */ 02441 Status = RtlGetFullPathName_UstrEx(FileNameString, 02442 CallerBuffer, 02443 DynamicString, 02444 (PUNICODE_STRING*)FullNameOut, 02445 FilePartSize, 02446 NULL, 02447 &PathType, 02448 LengthNeeded); 02449 if (!(NT_SUCCESS(Status)) && 02450 ((Status != STATUS_NO_SUCH_FILE) && 02451 (Status != STATUS_BUFFER_TOO_SMALL))) 02452 { 02453 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ " 02454 "failed with status %08lx\n", 02455 __FUNCTION__, 02456 FileNameString, 02457 Status); 02458 } 02459 DPRINT("STatus: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 02460 } 02461 } 02462 02463 Quickie: 02464 /* Anything that was not an error, turn into STATUS_SUCCESS */ 02465 if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS; 02466 02467 /* Check if we had a dynamic string */ 02468 if ((StaticCandidateString.Buffer) && 02469 (StaticCandidateString.Buffer != StaticCandidateBuffer)) 02470 { 02471 /* Free it */ 02472 RtlFreeUnicodeString(&StaticCandidateString); 02473 } 02474 02475 /* Return the status */ 02476 return Status; 02477 } 02478 02479 /* 02480 * @implemented 02481 */ 02482 BOOLEAN 02483 NTAPI 02484 RtlDoesFileExists_U(IN PCWSTR FileName) 02485 { 02486 /* Call the new function */ 02487 return RtlDoesFileExists_UEx(FileName, TRUE); 02488 } 02489 02490 /* EOF */ Generated on Sun May 27 2012 04:18:14 for ReactOS by
1.7.6.1
|