1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/path.c 5 * PURPOSE: Path and current directory functions 6 * PROGRAMMERS: Wine team 7 * Thomas Weidenmueller 8 * Gunnar Dalsnes 9 * Alex Ionescu (alex.ionescu@reactos.org) 10 * Pierre Schweitzer (pierre@reactos.org) 11 */ 12 13 /* INCLUDES *******************************************************************/ 14 15 #include <rtl.h> 16 17 #define NDEBUG 18 #include <debug.h> 19 20 /* DEFINITONS and MACROS ******************************************************/ 21 22 #define MAX_PFX_SIZE 16 23 24 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/')) 25 26 #define RTL_CURDIR_IS_REMOVABLE 0x1 27 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2 28 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3 29 C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS); 30 31 32 /* GLOBALS ********************************************************************/ 33 34 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\"); 35 36 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\"); 37 const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\"); 38 const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON"); 39 const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\"); 40 41 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT"); 42 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM"); 43 const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN"); 44 const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX"); 45 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON"); 46 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL"); 47 48 const UNICODE_STRING RtlpDoubleSlashPrefix = RTL_CONSTANT_STRING(L"\\\\"); 49 50 PRTLP_CURDIR_REF RtlpCurDirRef; 51 52 /* PRIVATE FUNCTIONS **********************************************************/ 53 54 RTL_PATH_TYPE 55 NTAPI 56 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString) 57 { 58 PWCHAR Path; 59 ULONG Chars; 60 61 Path = PathString->Buffer; 62 Chars = PathString->Length / sizeof(WCHAR); 63 64 /* Return if there are no characters */ 65 if (!Chars) return RtlPathTypeRelative; 66 67 /* 68 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we 69 * actually check for the path length before touching the characters 70 */ 71 if (IS_PATH_SEPARATOR(Path[0])) 72 { 73 if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */ 74 if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */ 75 if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ 76 if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ 77 return RtlPathTypeRootLocalDevice; /* \\. or \\? */ 78 } 79 else 80 { 81 if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ 82 if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */ 83 return RtlPathTypeDriveAbsolute; /* x:\ */ 84 } 85 } 86 87 ULONG 88 NTAPI 89 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString) 90 { 91 UNICODE_STRING PathCopy; 92 PWCHAR Start, End; 93 USHORT PathChars, ColonCount = 0; 94 USHORT ReturnOffset = 0, ReturnLength, OriginalLength; 95 WCHAR c; 96 97 /* Validate the input */ 98 if (!PathString) return 0; 99 100 /* Check what type of path this is */ 101 switch (RtlDetermineDosPathNameType_Ustr(PathString)) 102 { 103 /* Fail for UNC or unknown paths */ 104 case RtlPathTypeUnknown: 105 case RtlPathTypeUncAbsolute: 106 return 0; 107 108 /* Make special check for the CON device */ 109 case RtlPathTypeLocalDevice: 110 if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE)) 111 { 112 /* This should return 0x80006 */ 113 return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length); 114 } 115 return 0; 116 117 default: 118 break; 119 } 120 121 /* Make a copy of the string */ 122 PathCopy = *PathString; 123 OriginalLength = PathString->Length; 124 125 /* Return if there's no characters */ 126 PathChars = PathCopy.Length / sizeof(WCHAR); 127 if (!PathChars) return 0; 128 129 /* Check for drive path and truncate */ 130 if (PathCopy.Buffer[PathChars - 1] == L':') 131 { 132 /* Fixup the lengths */ 133 PathCopy.Length -= sizeof(WCHAR); 134 if (!--PathChars) return 0; 135 136 /* Remember this for later */ 137 ColonCount = 1; 138 } 139 140 /* Check for extension or space, and truncate */ 141 do 142 { 143 /* Stop if we hit something else than a space or period */ 144 c = PathCopy.Buffer[PathChars - 1]; 145 if ((c != L'.') && (c != L' ')) break; 146 147 /* Fixup the lengths */ 148 PathCopy.Length -= sizeof(WCHAR); 149 150 /* Remember this for later */ 151 ColonCount++; 152 } while (--PathChars); 153 154 /* Anything still left? */ 155 if (PathChars) 156 { 157 /* Loop from the end */ 158 for (End = &PathCopy.Buffer[PathChars - 1]; 159 End >= PathCopy.Buffer; 160 --End) 161 { 162 /* Check if the character is a path or drive separator */ 163 c = *End; 164 if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1))) 165 { 166 /* Get the next lower case character */ 167 End++; 168 c = RtlpDowncaseUnicodeChar(*End); 169 170 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */ 171 if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) && 172 ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n'))) 173 { 174 /* Calculate the offset */ 175 ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer); 176 177 /* Build the final string */ 178 PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR)); 179 PathCopy.Buffer = End; 180 181 /* Save new amount of chars in the path */ 182 PathChars = PathCopy.Length / sizeof(WCHAR); 183 184 break; 185 } 186 else 187 { 188 return 0; 189 } 190 } 191 } 192 193 /* Get the next lower case character and check if it's a DOS device */ 194 c = RtlpDowncaseUnicodeChar(*PathCopy.Buffer); 195 if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n')) 196 { 197 /* Not LPT, COM, PRN, AUX, or NUL */ 198 return 0; 199 } 200 } 201 202 /* Now skip past any extra extension or drive letter characters */ 203 Start = PathCopy.Buffer; 204 End = &Start[PathChars]; 205 while (Start < End) 206 { 207 c = *Start; 208 if ((c == L'.') || (c == L':')) break; 209 Start++; 210 } 211 212 /* And then go backwards to get rid of spaces */ 213 while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start; 214 215 /* Finally see how many characters are left, and that's our size */ 216 PathChars = (USHORT)(Start - PathCopy.Buffer); 217 PathCopy.Length = PathChars * sizeof(WCHAR); 218 219 /* Check if this is a COM or LPT port, which has a digit after it */ 220 if ((PathChars == 4) && 221 (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0'))) 222 { 223 /* Don't compare the number part, just check for LPT or COM */ 224 PathCopy.Length -= sizeof(WCHAR); 225 if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) || 226 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE))) 227 { 228 /* Found it */ 229 ReturnLength = sizeof(L"COM1") - sizeof(WCHAR); 230 return MAKELONG(ReturnLength, ReturnOffset); 231 } 232 } 233 else if ((PathChars == 3) && 234 ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) || 235 (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) || 236 (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) || 237 (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE)))) 238 { 239 /* Otherwise this was something like AUX, NUL, PRN, or CON */ 240 ReturnLength = sizeof(L"AUX") - sizeof(WCHAR); 241 return MAKELONG(ReturnLength, ReturnOffset); 242 } 243 244 /* Otherwise, this is not a valid DOS device */ 245 return 0; 246 } 247 248 NTSTATUS 249 NTAPI 250 RtlpCheckDeviceName(IN PUNICODE_STRING FileName, 251 IN ULONG Length, 252 OUT PBOOLEAN NameInvalid) 253 { 254 PWCHAR Buffer; 255 NTSTATUS Status; 256 257 /* Allocate a large enough buffer */ 258 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length); 259 if (Buffer) 260 { 261 /* Assume failure */ 262 *NameInvalid = TRUE; 263 264 /* Copy the filename */ 265 RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length); 266 267 /* And add a dot at the end */ 268 Buffer[Length / sizeof(WCHAR)] = L'.'; 269 Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; 270 271 /* Check if the file exists or not */ 272 *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE; 273 274 /* Get rid of the buffer now */ 275 Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 276 } 277 else 278 { 279 /* Assume the name is ok, but fail the call */ 280 *NameInvalid = FALSE; 281 Status = STATUS_NO_MEMORY; 282 } 283 284 /* Return the status */ 285 return Status; 286 } 287 288 289 290 /****************************************************************** 291 * RtlpCollapsePath (from WINE) 292 * 293 * Helper for RtlGetFullPathName_U 294 * 295 * 1) Converts slashes into backslashes and gets rid of duplicated ones; 296 * 2) Gets rid of . and .. components in the path. 297 * 298 * Returns the full path length without its terminating NULL character. 299 */ 300 static ULONG 301 RtlpCollapsePath(PWSTR Path, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark, BOOLEAN SkipTrailingPathSeparators) 302 { 303 PWSTR p, next; 304 305 // FIXME: Do not suppose NULL-terminated strings!! 306 307 ULONG PathLength = wcslen(Path); 308 PWSTR EndBuffer = Path + PathLength; // Path + PathBufferSize / sizeof(WCHAR); 309 PWSTR EndPath; 310 311 /* Convert slashes into backslashes */ 312 for (p = Path; *p; p++) 313 { 314 if (*p == L'/') *p = L'\\'; 315 } 316 317 /* Collapse duplicate backslashes */ 318 next = Path + max( 1, mark ); 319 for (p = next; *p; p++) 320 { 321 if (*p != L'\\' || next[-1] != L'\\') *next++ = *p; 322 } 323 *next = UNICODE_NULL; 324 EndPath = next; 325 326 p = Path + mark; 327 while (*p) 328 { 329 if (*p == L'.') 330 { 331 switch (p[1]) 332 { 333 case UNICODE_NULL: /* final . */ 334 if (p > Path + mark) p--; 335 *p = UNICODE_NULL; 336 EndPath = p; 337 continue; 338 339 case L'\\': /* .\ component */ 340 next = p + 2; 341 // ASSERT(EndPath - next == wcslen(next)); 342 RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR)); 343 EndPath -= (next - p); 344 continue; 345 346 case L'.': 347 if (p[2] == L'\\') /* ..\ component */ 348 { 349 next = p + 3; 350 if (p > Path + mark) 351 { 352 p--; 353 while (p > Path + mark && p[-1] != L'\\') p--; 354 } 355 // ASSERT(EndPath - next == wcslen(next)); 356 RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR)); 357 EndPath -= (next - p); 358 continue; 359 } 360 else if (p[2] == UNICODE_NULL) /* final .. */ 361 { 362 if (p > Path + mark) 363 { 364 p--; 365 while (p > Path + mark && p[-1] != L'\\') p--; 366 if (p > Path + mark) p--; 367 } 368 *p = UNICODE_NULL; 369 EndPath = p; 370 continue; 371 } 372 break; 373 } 374 } 375 376 /* Skip to the next component */ 377 while (*p && *p != L'\\') p++; 378 if (*p == L'\\') 379 { 380 /* Remove last dot in previous dir name */ 381 if (p > Path + mark && p[-1] == L'.') 382 { 383 // ASSERT(EndPath - p == wcslen(p)); 384 RtlMoveMemory(p - 1, p, (EndPath - p + 1) * sizeof(WCHAR)); 385 EndPath--; 386 } 387 else 388 { 389 p++; 390 } 391 } 392 } 393 394 /* Remove trailing backslashes if needed (after the UNC part if it exists) */ 395 if (SkipTrailingPathSeparators) 396 { 397 while (p > Path + mark && IS_PATH_SEPARATOR(p[-1])) p--; 398 } 399 400 /* Remove trailing spaces and dots (for all the path) */ 401 while (p > Path && (p[-1] == L' ' || p[-1] == L'.')) p--; 402 403 /* 404 * Zero-out the discarded buffer zone, starting just after 405 * the path string and going up to the end of the buffer. 406 * It also NULL-terminate the path string. 407 */ 408 ASSERT(EndBuffer >= p); 409 RtlZeroMemory(p, (EndBuffer - p + 1) * sizeof(WCHAR)); 410 411 /* Return the real path length */ 412 PathLength = (p - Path); 413 // ASSERT(PathLength == wcslen(Path)); 414 return (PathLength * sizeof(WCHAR)); 415 } 416 417 /****************************************************************** 418 * RtlpSkipUNCPrefix (from WINE) 419 * 420 * Helper for RtlGetFullPathName_U 421 * 422 * Skips the \\share\dir part of a file name and returns the new position 423 * (which can point on the last backslash of "dir\"). 424 */ 425 static SIZE_T 426 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer) 427 { 428 PCWSTR UncPath = FileNameBuffer + 2; 429 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer); 430 431 while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* share name */ 432 while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; 433 while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* dir name */ 434 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */ 435 436 return (UncPath - FileNameBuffer); 437 } 438 439 NTSTATUS 440 NTAPI 441 RtlpApplyLengthFunction(IN ULONG Flags, 442 IN ULONG Type, 443 IN PVOID UnicodeStringOrUnicodeStringBuffer, 444 IN PVOID LengthFunction) 445 { 446 UNIMPLEMENTED; 447 return STATUS_NOT_IMPLEMENTED; 448 } 449 450 NTSTATUS 451 NTAPI 452 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags, 453 IN PWCHAR Path, 454 OUT PULONG LengthOut) 455 { 456 UNIMPLEMENTED; 457 return STATUS_NOT_IMPLEMENTED; 458 } 459 460 NTSTATUS 461 NTAPI 462 RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName, 463 IN PUNICODE_STRING a2, 464 IN PUNICODE_STRING a3) 465 { 466 UNIMPLEMENTED; 467 return STATUS_NOT_IMPLEMENTED; 468 } 469 470 ULONG 471 NTAPI 472 RtlGetFullPathName_Ustr( 473 _In_ PUNICODE_STRING FileName, 474 _In_ ULONG Size, 475 _Out_z_bytecap_(Size) PWSTR Buffer, 476 _Out_opt_ PCWSTR *ShortName, 477 _Out_opt_ PBOOLEAN InvalidName, 478 _Out_ RTL_PATH_TYPE *PathType) 479 { 480 NTSTATUS Status; 481 PWCHAR FileNameBuffer; 482 ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength; 483 BOOLEAN SkipTrailingPathSeparators; 484 WCHAR c; 485 486 487 ULONG reqsize = 0; 488 PCWSTR ptr; 489 490 PCUNICODE_STRING CurDirName; 491 UNICODE_STRING EnvVarName, EnvVarValue; 492 WCHAR EnvVarNameBuffer[4]; 493 494 ULONG PrefixCut = 0; // Where the path really starts (after the skipped prefix) 495 PWCHAR Prefix = NULL; // pointer to the string to be inserted as the new path prefix 496 ULONG PrefixLength = 0; 497 PWCHAR Source; 498 ULONG SourceLength; 499 500 501 /* For now, assume the name is valid */ 502 DPRINT("Filename: %wZ\n", FileName); 503 DPRINT("Size and buffer: %lx %p\n", Size, Buffer); 504 if (InvalidName) *InvalidName = FALSE; 505 506 /* Handle initial path type and failure case */ 507 *PathType = RtlPathTypeUnknown; 508 if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0; 509 510 /* Break filename into component parts */ 511 FileNameBuffer = FileName->Buffer; 512 FileNameLength = FileName->Length; 513 FileNameChars = FileNameLength / sizeof(WCHAR); 514 515 /* Kill trailing spaces */ 516 c = FileNameBuffer[FileNameChars - 1]; 517 while ((FileNameLength != 0) && (c == L' ')) 518 { 519 /* Keep going, ignoring the spaces */ 520 FileNameLength -= sizeof(WCHAR); 521 if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1]; 522 } 523 524 /* Check if anything is left */ 525 if (FileNameLength == 0) return 0; 526 527 /* 528 * Check whether we'll need to skip trailing path separators in the 529 * computed full path name. If the original file name already contained 530 * trailing separators, then we keep them in the full path name. On the 531 * other hand, if the original name didn't contain any trailing separators 532 * then we'll skip it in the full path name. 533 */ 534 SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]); 535 536 /* Check if this is a DOS name */ 537 DosLength = RtlIsDosDeviceName_Ustr(FileName); 538 DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName); 539 if (DosLength != 0) 540 { 541 /* Zero out the short name */ 542 if (ShortName) *ShortName = NULL; 543 544 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */ 545 DosLengthOffset = HIWORD(DosLength); 546 DosLength = LOWORD(DosLength); 547 548 /* Do we have a DOS length, and does the caller want validity? */ 549 if (InvalidName && (DosLengthOffset != 0)) 550 { 551 /* Do the check */ 552 Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName); 553 554 /* If the check failed, or the name is invalid, fail here */ 555 if (!NT_SUCCESS(Status)) return 0; 556 if (*InvalidName) return 0; 557 } 558 559 /* Add the size of the device root and check if it fits in the size */ 560 FullLength = DosLength + DeviceRootString.Length; 561 if (FullLength < Size) 562 { 563 /* Add the device string */ 564 RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length); 565 566 /* Now add the DOS device name */ 567 RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length, 568 (PCHAR)FileNameBuffer + DosLengthOffset, 569 DosLength); 570 571 /* Null terminate */ 572 *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL; 573 return FullLength; 574 } 575 576 /* Otherwise, there's no space, so return the buffer size needed */ 577 if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0; 578 return FullLength + sizeof(UNICODE_NULL); 579 } 580 581 /* Zero-out the destination buffer. FileName must be different from Buffer */ 582 RtlZeroMemory(Buffer, Size); 583 584 /* Get the path type */ 585 *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer); 586 587 588 589 /********************************************** 590 ** CODE REWRITING IS HAPPENING THERE ** 591 **********************************************/ 592 Source = FileNameBuffer; 593 SourceLength = FileNameLength; 594 EnvVarValue.Buffer = NULL; 595 596 /* Lock the PEB to get the current directory */ 597 RtlAcquirePebLock(); 598 CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath; 599 600 switch (*PathType) 601 { 602 case RtlPathTypeUncAbsolute: /* \\foo */ 603 { 604 PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer); 605 break; 606 } 607 608 case RtlPathTypeLocalDevice: /* \\.\foo */ 609 { 610 PrefixCut = 4; 611 break; 612 } 613 614 case RtlPathTypeDriveAbsolute: /* c:\foo */ 615 { 616 ASSERT(FileNameBuffer[1] == L':'); 617 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2])); 618 619 // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]); 620 Prefix = FileNameBuffer; 621 PrefixLength = 3 * sizeof(WCHAR); 622 Source += 3; 623 SourceLength -= 3 * sizeof(WCHAR); 624 625 PrefixCut = 3; 626 break; 627 } 628 629 case RtlPathTypeDriveRelative: /* c:foo */ 630 { 631 WCHAR CurDrive, NewDrive; 632 633 Source += 2; 634 SourceLength -= 2 * sizeof(WCHAR); 635 636 CurDrive = RtlpUpcaseUnicodeChar(CurDirName->Buffer[0]); 637 NewDrive = RtlpUpcaseUnicodeChar(FileNameBuffer[0]); 638 639 if ((NewDrive != CurDrive) || CurDirName->Buffer[1] != L':') 640 { 641 EnvVarNameBuffer[0] = L'='; 642 EnvVarNameBuffer[1] = NewDrive; 643 EnvVarNameBuffer[2] = L':'; 644 EnvVarNameBuffer[3] = UNICODE_NULL; 645 646 EnvVarName.Length = 3 * sizeof(WCHAR); 647 EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR); 648 EnvVarName.Buffer = EnvVarNameBuffer; 649 650 // FIXME: Is it possible to use the user-given buffer ? 651 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size); 652 EnvVarValue.Length = 0; 653 EnvVarValue.MaximumLength = (USHORT)Size; 654 EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size); 655 if (EnvVarValue.Buffer == NULL) 656 { 657 Prefix = NULL; 658 PrefixLength = 0; 659 goto Quit; 660 } 661 662 Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue); 663 switch (Status) 664 { 665 case STATUS_SUCCESS: 666 /* 667 * (From Wine) 668 * FIXME: Win2k seems to check that the environment 669 * variable actually points to an existing directory. 670 * If not, root of the drive is used (this seems also 671 * to be the only place in RtlGetFullPathName that the 672 * existence of a part of a path is checked). 673 */ 674 EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\'; 675 Prefix = EnvVarValue.Buffer; 676 PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */ 677 break; 678 679 case STATUS_BUFFER_TOO_SMALL: 680 reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL); 681 goto Quit; 682 683 default: 684 DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName, Status); 685 686 EnvVarNameBuffer[0] = NewDrive; 687 EnvVarNameBuffer[1] = L':'; 688 EnvVarNameBuffer[2] = L'\\'; 689 EnvVarNameBuffer[3] = UNICODE_NULL; 690 Prefix = EnvVarNameBuffer; 691 PrefixLength = 3 * sizeof(WCHAR); 692 693 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer); 694 EnvVarValue.Buffer = NULL; 695 break; 696 } 697 PrefixCut = 3; 698 break; 699 } 700 /* Fall through */ 701 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n"); 702 } 703 704 case RtlPathTypeRelative: /* foo */ 705 { 706 Prefix = CurDirName->Buffer; 707 PrefixLength = CurDirName->Length; 708 if (CurDirName->Buffer[1] != L':') 709 { 710 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer); 711 } 712 else 713 { 714 PrefixCut = 3; 715 } 716 break; 717 } 718 719 case RtlPathTypeRooted: /* \xxx */ 720 { 721 if (CurDirName->Buffer[1] == L':') 722 { 723 // The path starts with "C:\" 724 ASSERT(CurDirName->Buffer[1] == L':'); 725 ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2])); 726 727 Prefix = CurDirName->Buffer; 728 PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\" 729 730 PrefixCut = 3; // Source index location incremented of + 3 731 } 732 else 733 { 734 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer); 735 PrefixLength = PrefixCut * sizeof(WCHAR); 736 Prefix = CurDirName->Buffer; 737 } 738 break; 739 } 740 741 case RtlPathTypeRootLocalDevice: /* \\. */ 742 { 743 Prefix = DeviceRootString.Buffer; 744 PrefixLength = DeviceRootString.Length; 745 Source += 3; 746 SourceLength -= 3 * sizeof(WCHAR); 747 748 PrefixCut = 4; 749 break; 750 } 751 752 case RtlPathTypeUnknown: 753 goto Quit; 754 } 755 756 /* Do we have enough space for storing the full path? */ 757 reqsize = PrefixLength; 758 if (reqsize + SourceLength + sizeof(WCHAR) > Size) 759 { 760 /* Not enough space, return needed size (including terminating '\0') */ 761 reqsize += SourceLength + sizeof(WCHAR); 762 goto Quit; 763 } 764 765 /* 766 * Build the full path 767 */ 768 /* Copy the path's prefix */ 769 if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength); 770 /* Copy the remaining part of the path */ 771 RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR)); 772 773 /* Some cleanup */ 774 Prefix = NULL; 775 if (EnvVarValue.Buffer) 776 { 777 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer); 778 EnvVarValue.Buffer = NULL; 779 } 780 781 /* 782 * Finally, put the path in canonical form (remove redundant . and .., 783 * (back)slashes...) and retrieve the length of the full path name 784 * (without its terminating null character) (in chars). 785 */ 786 reqsize = RtlpCollapsePath(Buffer, /* Size, reqsize, */ PrefixCut, SkipTrailingPathSeparators); 787 788 /* Find the file part, which is present after the last path separator */ 789 if (ShortName) 790 { 791 ptr = wcsrchr(Buffer, L'\\'); 792 if (ptr) ++ptr; // Skip it 793 794 /* 795 * For UNC paths, the file part is after the \\share\dir part of the path. 796 */ 797 PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3); 798 799 if (ptr && *ptr && (ptr >= Buffer + PrefixCut)) 800 { 801 *ShortName = ptr; 802 } 803 else 804 { 805 /* Zero-out the short name */ 806 *ShortName = NULL; 807 } 808 } 809 810 Quit: 811 /* Release PEB lock */ 812 RtlReleasePebLock(); 813 814 return reqsize; 815 } 816 817 NTSTATUS 818 NTAPI 819 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath, 820 OUT PUNICODE_STRING NtPath, 821 OUT PCWSTR *PartName, 822 OUT PRTL_RELATIVE_NAME_U RelativeName) 823 { 824 ULONG DosLength; 825 PWSTR NewBuffer, p; 826 827 /* Validate the input */ 828 if (!DosPath) return STATUS_OBJECT_NAME_INVALID; 829 830 /* Validate the DOS length */ 831 DosLength = DosPath->Length; 832 if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG; 833 834 /* Make space for the new path */ 835 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 836 0, 837 DosLength + sizeof(UNICODE_NULL)); 838 if (!NewBuffer) return STATUS_NO_MEMORY; 839 840 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */ 841 RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length); 842 RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length, 843 DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR), 844 DosPath->Length - RtlpDosDevicesPrefix.Length); 845 NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL; 846 847 /* Did the caller send a relative name? */ 848 if (RelativeName) 849 { 850 /* Zero initialize it */ 851 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); 852 RelativeName->ContainingDirectory = NULL; 853 RelativeName->CurDirRef = 0; 854 } 855 856 /* Did the caller request a partial name? */ 857 if (PartName) 858 { 859 /* Loop from the back until we find a path separator */ 860 p = &NewBuffer[DosLength / sizeof(WCHAR)]; 861 while (--p > NewBuffer) 862 { 863 /* We found a path separator, move past it */ 864 if (*p == OBJ_NAME_PATH_SEPARATOR) 865 { 866 ++p; 867 break; 868 } 869 } 870 871 /* Check whether a separator was found and if something remains */ 872 if ((p > NewBuffer) && *p) 873 { 874 /* What follows the path separator is the partial name */ 875 *PartName = p; 876 } 877 else 878 { 879 /* The path ends with a path separator, no partial name */ 880 *PartName = NULL; 881 } 882 } 883 884 /* Build the final NT path string */ 885 NtPath->Buffer = NewBuffer; 886 NtPath->Length = (USHORT)DosLength; 887 NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL); 888 return STATUS_SUCCESS; 889 } 890 891 NTSTATUS 892 NTAPI 893 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative, 894 IN PCUNICODE_STRING DosName, 895 OUT PUNICODE_STRING NtName, 896 OUT PCWSTR *PartName, 897 OUT PRTL_RELATIVE_NAME_U RelativeName) 898 { 899 WCHAR BigBuffer[MAX_PATH + 1]; 900 PWCHAR PrefixBuffer, NewBuffer, Buffer; 901 ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length; 902 UNICODE_STRING CapturedDosName, PartNameString, FullPath; 903 BOOLEAN QuickPath; 904 RTL_PATH_TYPE InputPathType, BufferPathType; 905 NTSTATUS Status; 906 BOOLEAN NameInvalid; 907 PCURDIR CurrentDirectory; 908 909 /* Assume MAX_PATH for now */ 910 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n", 911 HaveRelative, DosName, NtName, PartName, RelativeName); 912 MaxLength = sizeof(BigBuffer); 913 914 /* Validate the input */ 915 if (!DosName) return STATUS_OBJECT_NAME_INVALID; 916 917 /* Capture input string */ 918 CapturedDosName = *DosName; 919 920 /* Check for the presence or absence of the NT prefix "\\?\" form */ 921 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE)) 922 if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) || 923 (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) || 924 (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) || 925 (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) || 926 (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3])) 927 { 928 /* NT prefix not present */ 929 930 /* Quick path won't be used */ 931 QuickPath = FALSE; 932 933 /* Use the static buffer */ 934 Buffer = BigBuffer; 935 MaxLength += RtlpDosDevicesUncPrefix.Length; 936 937 /* Allocate a buffer to hold the path */ 938 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength); 939 DPRINT("MaxLength: %lx\n", MaxLength); 940 if (!NewBuffer) return STATUS_NO_MEMORY; 941 } 942 else 943 { 944 /* NT prefix present */ 945 946 /* Use the optimized path after acquiring the lock */ 947 QuickPath = TRUE; 948 NewBuffer = NULL; 949 } 950 951 /* Lock the PEB and check if the quick path can be used */ 952 RtlAcquirePebLock(); 953 if (QuickPath) 954 { 955 /* Some simple fixups will get us the correct path */ 956 DPRINT("Quick path\n"); 957 Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName, 958 NtName, 959 PartName, 960 RelativeName); 961 962 /* Release the lock, we're done here */ 963 RtlReleasePebLock(); 964 return Status; 965 } 966 967 /* Call the main function to get the full path name and length */ 968 PathLength = RtlGetFullPathName_Ustr(&CapturedDosName, 969 MAX_PATH * sizeof(WCHAR), 970 Buffer, 971 PartName, 972 &NameInvalid, 973 &InputPathType); 974 if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR)))) 975 { 976 /* Invalid name, fail */ 977 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength); 978 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 979 RtlReleasePebLock(); 980 return STATUS_OBJECT_NAME_INVALID; 981 } 982 983 /* Start by assuming the path starts with \??\ (DOS Devices Path) */ 984 PrefixLength = RtlpDosDevicesPrefix.Length; 985 PrefixBuffer = RtlpDosDevicesPrefix.Buffer; 986 PrefixCut = 0; 987 988 /* Check where it really is */ 989 BufferPathType = RtlDetermineDosPathNameType_U(Buffer); 990 DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType); 991 switch (BufferPathType) 992 { 993 /* It's actually a UNC path in \??\UNC\ */ 994 case RtlPathTypeUncAbsolute: 995 PrefixLength = RtlpDosDevicesUncPrefix.Length; 996 PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer; 997 PrefixCut = 2; 998 break; 999 1000 case RtlPathTypeLocalDevice: 1001 /* We made a good guess, go with it but skip the \??\ */ 1002 PrefixCut = 4; 1003 break; 1004 1005 case RtlPathTypeDriveAbsolute: 1006 case RtlPathTypeDriveRelative: 1007 case RtlPathTypeRooted: 1008 case RtlPathTypeRelative: 1009 /* Our guess was good, roll with it */ 1010 break; 1011 1012 /* Nothing else is expected */ 1013 default: 1014 ASSERT(FALSE); 1015 } 1016 1017 /* Now copy the prefix and the buffer */ 1018 RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength); 1019 RtlCopyMemory((PCHAR)NewBuffer + PrefixLength, 1020 Buffer + PrefixCut, 1021 PathLength - (PrefixCut * sizeof(WCHAR))); 1022 1023 /* Compute the length */ 1024 Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR); 1025 LengthChars = Length / sizeof(WCHAR); 1026 1027 /* Setup the actual NT path string and terminate it */ 1028 NtName->Buffer = NewBuffer; 1029 NtName->Length = (USHORT)Length; 1030 NtName->MaximumLength = (USHORT)MaxLength; 1031 NewBuffer[LengthChars] = UNICODE_NULL; 1032 DPRINT("New buffer: %S\n", NewBuffer); 1033 DPRINT("NT Name: %wZ\n", NtName); 1034 1035 /* Check if a partial name was requested */ 1036 if ((PartName) && (*PartName)) 1037 { 1038 /* Convert to Unicode */ 1039 Status = RtlInitUnicodeStringEx(&PartNameString, *PartName); 1040 if (NT_SUCCESS(Status)) 1041 { 1042 /* Set the partial name */ 1043 *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))]; 1044 } 1045 else 1046 { 1047 /* Fail */ 1048 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 1049 RtlReleasePebLock(); 1050 return Status; 1051 } 1052 } 1053 1054 /* Check if a relative name was asked for */ 1055 if (RelativeName) 1056 { 1057 /* Setup the structure */ 1058 RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); 1059 RelativeName->ContainingDirectory = NULL; 1060 RelativeName->CurDirRef = NULL; 1061 1062 /* Check if the input path itself was relative */ 1063 if (InputPathType == RtlPathTypeRelative) 1064 { 1065 /* Get current directory */ 1066 CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory); 1067 if (CurrentDirectory->Handle) 1068 { 1069 Status = RtlInitUnicodeStringEx(&FullPath, Buffer); 1070 if (!NT_SUCCESS(Status)) 1071 { 1072 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 1073 RtlReleasePebLock(); 1074 return Status; 1075 } 1076 1077 /* If current directory is bigger than full path, there's no way */ 1078 if (CurrentDirectory->DosPath.Length > FullPath.Length) 1079 { 1080 RtlReleasePebLock(); 1081 return Status; 1082 } 1083 1084 /* File is in current directory */ 1085 if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE)) 1086 { 1087 /* Make relative name string */ 1088 RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR)); 1089 RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length); 1090 /* If relative name starts with \, skip it */ 1091 if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) 1092 { 1093 RelativeName->RelativeName.Buffer++; 1094 RelativeName->RelativeName.Length -= sizeof(WCHAR); 1095 } 1096 RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length; 1097 DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName)); 1098 1099 if (!HaveRelative) 1100 { 1101 RelativeName->ContainingDirectory = CurrentDirectory->Handle; 1102 return Status; 1103 } 1104 1105 /* Give back current directory data & reference counter */ 1106 RelativeName->CurDirRef = RtlpCurDirRef; 1107 if (RelativeName->CurDirRef) 1108 { 1109 InterlockedIncrement(&RtlpCurDirRef->RefCount); 1110 } 1111 1112 RelativeName->ContainingDirectory = CurrentDirectory->Handle; 1113 } 1114 } 1115 } 1116 } 1117 1118 /* Done */ 1119 RtlReleasePebLock(); 1120 return STATUS_SUCCESS; 1121 } 1122 1123 NTSTATUS 1124 NTAPI 1125 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative, 1126 IN PCWSTR DosName, 1127 OUT PUNICODE_STRING NtName, 1128 OUT PCWSTR *PartName, 1129 OUT PRTL_RELATIVE_NAME_U RelativeName) 1130 { 1131 NTSTATUS Status; 1132 UNICODE_STRING NameString; 1133 1134 /* Create the unicode name */ 1135 Status = RtlInitUnicodeStringEx(&NameString, DosName); 1136 if (NT_SUCCESS(Status)) 1137 { 1138 /* Call the unicode function */ 1139 Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative, 1140 &NameString, 1141 NtName, 1142 PartName, 1143 RelativeName); 1144 } 1145 1146 /* Return status */ 1147 return Status; 1148 } 1149 1150 BOOLEAN 1151 NTAPI 1152 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName, 1153 OUT PUNICODE_STRING NtName, 1154 OUT PCWSTR *PartName, 1155 OUT PRTL_RELATIVE_NAME_U RelativeName) 1156 { 1157 /* Call the internal function */ 1158 ASSERT(RelativeName); 1159 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE, 1160 DosName, 1161 NtName, 1162 PartName, 1163 RelativeName)); 1164 } 1165 1166 BOOLEAN 1167 NTAPI 1168 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName, 1169 IN BOOLEAN SucceedIfBusy) 1170 { 1171 BOOLEAN Result; 1172 RTL_RELATIVE_NAME_U RelativeName; 1173 UNICODE_STRING NtPathName; 1174 PVOID Buffer; 1175 OBJECT_ATTRIBUTES ObjectAttributes; 1176 NTSTATUS Status; 1177 FILE_BASIC_INFORMATION BasicInformation; 1178 1179 /* Get the NT Path */ 1180 Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName, 1181 &NtPathName, 1182 NULL, 1183 &RelativeName); 1184 if (!Result) return FALSE; 1185 1186 /* Save the buffer */ 1187 Buffer = NtPathName.Buffer; 1188 1189 /* Check if we have a relative name */ 1190 if (RelativeName.RelativeName.Length) 1191 { 1192 /* Use it */ 1193 NtPathName = RelativeName.RelativeName; 1194 } 1195 else 1196 { 1197 /* Otherwise ignore it */ 1198 RelativeName.ContainingDirectory = NULL; 1199 } 1200 1201 /* Initialize the object attributes */ 1202 InitializeObjectAttributes(&ObjectAttributes, 1203 &NtPathName, 1204 OBJ_CASE_INSENSITIVE, 1205 RelativeName.ContainingDirectory, 1206 NULL); 1207 1208 /* Query the attributes and free the buffer now */ 1209 Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation); 1210 RtlReleaseRelativeName(&RelativeName); 1211 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1212 1213 /* Check if we failed */ 1214 if (!NT_SUCCESS(Status)) 1215 { 1216 /* Check if we failed because the file is in use */ 1217 if ((Status == STATUS_SHARING_VIOLATION) || 1218 (Status == STATUS_ACCESS_DENIED)) 1219 { 1220 /* Check if the caller wants this to be considered OK */ 1221 Result = SucceedIfBusy ? TRUE : FALSE; 1222 } 1223 else 1224 { 1225 /* A failure because the file didn't exist */ 1226 Result = FALSE; 1227 } 1228 } 1229 else 1230 { 1231 /* The file exists */ 1232 Result = TRUE; 1233 } 1234 1235 /* Return the result */ 1236 return Result; 1237 } 1238 1239 BOOLEAN 1240 NTAPI 1241 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName) 1242 { 1243 /* Call the updated API */ 1244 return RtlDoesFileExists_UstrEx(FileName, TRUE); 1245 } 1246 1247 BOOLEAN 1248 NTAPI 1249 RtlDoesFileExists_UEx(IN PCWSTR FileName, 1250 IN BOOLEAN SucceedIfBusy) 1251 { 1252 UNICODE_STRING NameString; 1253 1254 /* Create the unicode name*/ 1255 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName))) 1256 { 1257 /* Call the unicode function */ 1258 return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy); 1259 } 1260 1261 /* Fail */ 1262 return FALSE; 1263 } 1264 1265 /* PUBLIC FUNCTIONS ***********************************************************/ 1266 1267 /* 1268 * @implemented 1269 */ 1270 VOID 1271 NTAPI 1272 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName) 1273 { 1274 /* Check if a directory reference was grabbed */ 1275 if (RelativeName->CurDirRef) 1276 { 1277 /* Decrease reference count */ 1278 if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount)) 1279 { 1280 /* If no one uses it any longer, close handle & free */ 1281 NtClose(RelativeName->CurDirRef->Handle); 1282 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef); 1283 } 1284 RelativeName->CurDirRef = NULL; 1285 } 1286 } 1287 1288 /* 1289 * @implemented 1290 */ 1291 ULONG 1292 NTAPI 1293 RtlGetLongestNtPathLength(VOID) 1294 { 1295 /* 1296 * The longest NT path is a DOS path that actually sits on a UNC path (ie: 1297 * a mapped network drive), which is accessed through the DOS Global?? path. 1298 * This is, and has always been equal to, 269 characters, except in Wine 1299 * which claims this is 277. Go figure. 1300 */ 1301 return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL); 1302 } 1303 1304 /* 1305 * @implemented 1306 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators 1307 * (with a 'e' instead of a 'a' in "Seperators"). 1308 */ 1309 NTSTATUS 1310 NTAPI 1311 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags, 1312 IN PCUNICODE_STRING PathString, 1313 OUT PULONG Length) 1314 { 1315 ULONG NumChars; 1316 1317 /* Parameters validation */ 1318 if (Length == NULL) return STATUS_INVALID_PARAMETER; 1319 1320 *Length = 0; 1321 1322 if (PathString == NULL) return STATUS_INVALID_PARAMETER; 1323 1324 /* No flags are supported yet */ 1325 if (Flags != 0) return STATUS_INVALID_PARAMETER; 1326 1327 NumChars = PathString->Length / sizeof(WCHAR); 1328 1329 /* 1330 * Notice that we skip the last character, therefore: 1331 * - if we have: "some/path/f" we test for: "some/path/" 1332 * - if we have: "some/path/" we test for: "some/path" 1333 * - if we have: "s" we test for: "" 1334 * - if we have: "" then NumChars was already zero and we aren't there 1335 */ 1336 1337 while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1])) 1338 { 1339 --NumChars; 1340 } 1341 1342 *Length = NumChars; 1343 return STATUS_SUCCESS; 1344 } 1345 1346 /* 1347 * @implemented 1348 */ 1349 RTL_PATH_TYPE 1350 NTAPI 1351 RtlDetermineDosPathNameType_U(IN PCWSTR Path) 1352 { 1353 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path); 1354 1355 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */ 1356 if (IS_PATH_SEPARATOR(Path[0])) 1357 { 1358 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */ 1359 if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */ 1360 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ 1361 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ 1362 return RtlPathTypeRootLocalDevice; /* \\. or \\? */ 1363 } 1364 else 1365 { 1366 if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ 1367 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */ 1368 return RtlPathTypeDriveRelative; /* x: */ 1369 } 1370 } 1371 1372 /* 1373 * @implemented 1374 */ 1375 ULONG 1376 NTAPI 1377 RtlIsDosDeviceName_U(IN PCWSTR Path) 1378 { 1379 UNICODE_STRING PathString; 1380 NTSTATUS Status; 1381 1382 /* Build the string */ 1383 Status = RtlInitUnicodeStringEx(&PathString, Path); 1384 if (!NT_SUCCESS(Status)) return 0; 1385 1386 /* 1387 * Returns 0 if name is not valid DOS device name, or DWORD with 1388 * offset in bytes to DOS device name from beginning of buffer in high word 1389 * and size in bytes of DOS device name in low word 1390 */ 1391 return RtlIsDosDeviceName_Ustr(&PathString); 1392 } 1393 1394 /* 1395 * @implemented 1396 */ 1397 ULONG 1398 NTAPI 1399 RtlGetCurrentDirectory_U( 1400 _In_ ULONG MaximumLength, 1401 _Out_bytecap_(MaximumLength) PWSTR Buffer) 1402 { 1403 ULONG Length, Bytes; 1404 PCURDIR CurDir; 1405 PWSTR CurDirName; 1406 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer); 1407 1408 /* Lock the PEB to get the current directory */ 1409 RtlAcquirePebLock(); 1410 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory; 1411 1412 /* Get the buffer and character length */ 1413 CurDirName = CurDir->DosPath.Buffer; 1414 Length = CurDir->DosPath.Length / sizeof(WCHAR); 1415 ASSERT((CurDirName != NULL) && (Length > 0)); 1416 1417 /* 1418 * DosPath.Buffer should always have a trailing slash. There is an assert 1419 * below which checks for this. 1420 * 1421 * This function either returns x:\ for a root (keeping the original buffer) 1422 * or it returns x:\path\foo for a directory (replacing the trailing slash 1423 * with a NULL. 1424 */ 1425 Bytes = Length * sizeof(WCHAR); 1426 if ((Length <= 1) || (CurDirName[Length - 2] == L':')) 1427 { 1428 /* Check if caller does not have enough space */ 1429 if (MaximumLength <= Bytes) 1430 { 1431 /* Call has no space for it, fail, add the trailing slash */ 1432 RtlReleasePebLock(); 1433 return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR); 1434 } 1435 } 1436 else 1437 { 1438 /* Check if caller does not have enough space */ 1439 if (MaximumLength < Bytes) 1440 { 1441 /* Call has no space for it, fail */ 1442 RtlReleasePebLock(); 1443 return Bytes; 1444 } 1445 } 1446 1447 /* Copy the buffer since we seem to have space */ 1448 RtlCopyMemory(Buffer, CurDirName, Bytes); 1449 1450 /* The buffer should end with a path separator */ 1451 ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR); 1452 1453 /* Again check for our two cases (drive root vs path) */ 1454 if ((Length <= 1) || (Buffer[Length - 2] != L':')) 1455 { 1456 /* Replace the trailing slash with a null */ 1457 Buffer[Length - 1] = UNICODE_NULL; 1458 --Length; 1459 } 1460 else 1461 { 1462 /* Append the null char since there's no trailing slash */ 1463 Buffer[Length] = UNICODE_NULL; 1464 } 1465 1466 /* Release PEB lock */ 1467 RtlReleasePebLock(); 1468 DPRINT("CurrentDirectory %S\n", Buffer); 1469 return Length * sizeof(WCHAR); 1470 } 1471 1472 /* 1473 * @implemented 1474 */ 1475 NTSTATUS 1476 NTAPI 1477 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path) 1478 { 1479 PCURDIR CurDir; 1480 NTSTATUS Status; 1481 RTL_PATH_TYPE PathType; 1482 IO_STATUS_BLOCK IoStatusBlock; 1483 UNICODE_STRING FullPath, NtName; 1484 PRTLP_CURDIR_REF OldCurDir = NULL; 1485 OBJECT_ATTRIBUTES ObjectAttributes; 1486 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo; 1487 ULONG SavedLength, CharLength, FullPathLength; 1488 HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL; 1489 1490 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path); 1491 1492 /* Initialize for failure case */ 1493 RtlInitEmptyUnicodeString(&NtName, NULL, 0); 1494 1495 /* Can't set current directory on DOS device */ 1496 if (RtlIsDosDeviceName_Ustr(Path)) 1497 { 1498 return STATUS_NOT_A_DIRECTORY; 1499 } 1500 1501 /* Get current directory */ 1502 RtlAcquirePebLock(); 1503 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory; 1504 1505 /* Check if we have to drop current handle */ 1506 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE) 1507 { 1508 OldHandle = CurDir->Handle; 1509 CurDir->Handle = NULL; 1510 } 1511 1512 /* Allocate a buffer for full path (using max possible length */ 1513 FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength); 1514 if (!FullPath.Buffer) 1515 { 1516 Status = STATUS_NO_MEMORY; 1517 goto Leave; 1518 } 1519 1520 /* Init string */ 1521 FullPath.Length = 0; 1522 FullPath.MaximumLength = CurDir->DosPath.MaximumLength; 1523 1524 /* Get new directory full path */ 1525 FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType); 1526 if (!FullPathLength) 1527 { 1528 Status = STATUS_OBJECT_NAME_INVALID; 1529 goto Leave; 1530 } 1531 1532 SavedLength = FullPath.MaximumLength; 1533 CharLength = FullPathLength / sizeof(WCHAR); 1534 1535 if (FullPathLength > FullPath.MaximumLength) 1536 { 1537 Status = STATUS_NAME_TOO_LONG; 1538 goto Leave; 1539 } 1540 1541 /* Translate it to NT name */ 1542 if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL)) 1543 { 1544 Status = STATUS_OBJECT_NAME_INVALID; 1545 goto Leave; 1546 } 1547 1548 InitializeObjectAttributes(&ObjectAttributes, &NtName, 1549 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, 1550 NULL, NULL); 1551 1552 /* If previous current directory was removable, then check it for dropping */ 1553 if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS) 1554 { 1555 /* Get back normal handle */ 1556 CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS); 1557 CurDir->Handle = NULL; 1558 1559 /* Get device information */ 1560 Status = NtQueryVolumeInformationFile(CurDirHandle, 1561 &IoStatusBlock, 1562 &FileFsDeviceInfo, 1563 sizeof(FileFsDeviceInfo), 1564 FileFsDeviceInformation); 1565 /* Retry without taking care of removable device */ 1566 if (!NT_SUCCESS(Status)) 1567 { 1568 Status = RtlSetCurrentDirectory_U(Path); 1569 goto Leave; 1570 } 1571 } 1572 else 1573 { 1574 /* Open directory */ 1575 Status = NtOpenFile(&CurDirHandle, 1576 SYNCHRONIZE | FILE_TRAVERSE, 1577 &ObjectAttributes, 1578 &IoStatusBlock, 1579 FILE_SHARE_READ | FILE_SHARE_WRITE, 1580 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); 1581 if (!NT_SUCCESS(Status)) goto Leave; 1582 1583 /* Get device information */ 1584 Status = NtQueryVolumeInformationFile(CurDirHandle, 1585 &IoStatusBlock, 1586 &FileFsDeviceInfo, 1587 sizeof(FileFsDeviceInfo), 1588 FileFsDeviceInformation); 1589 if (!NT_SUCCESS(Status)) goto Leave; 1590 } 1591 1592 /* If device is removable, mark handle */ 1593 if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) 1594 { 1595 CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE); 1596 } 1597 1598 FullPath.Length = (USHORT)FullPathLength; 1599 1600 /* If full path isn't \ terminated, do it */ 1601 if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR) 1602 { 1603 if ((CharLength + 1) * sizeof(WCHAR) > SavedLength) 1604 { 1605 Status = STATUS_NAME_TOO_LONG; 1606 goto Leave; 1607 } 1608 1609 FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR; 1610 FullPath.Buffer[CharLength + 1] = UNICODE_NULL; 1611 FullPath.Length += sizeof(WCHAR); 1612 } 1613 1614 /* If we have previous current directory with only us as reference, save it */ 1615 if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1) 1616 { 1617 OldCurDirHandle = RtlpCurDirRef->Handle; 1618 } 1619 else 1620 { 1621 /* Allocate new current directory struct saving previous one */ 1622 OldCurDir = RtlpCurDirRef; 1623 RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF)); 1624 if (!RtlpCurDirRef) 1625 { 1626 RtlpCurDirRef = OldCurDir; 1627 OldCurDir = NULL; 1628 Status = STATUS_NO_MEMORY; 1629 goto Leave; 1630 } 1631 1632 /* Set reference to 1 (us) */ 1633 RtlpCurDirRef->RefCount = 1; 1634 } 1635 1636 /* Save new data */ 1637 CurDir->Handle = CurDirHandle; 1638 RtlpCurDirRef->Handle = CurDirHandle; 1639 CurDirHandle = NULL; 1640 1641 /* Copy full path */ 1642 RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR)); 1643 CurDir->DosPath.Length = FullPath.Length; 1644 1645 Status = STATUS_SUCCESS; 1646 1647 Leave: 1648 RtlReleasePebLock(); 1649 1650 if (FullPath.Buffer) 1651 { 1652 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer); 1653 } 1654 1655 if (NtName.Buffer) 1656 { 1657 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer); 1658 } 1659 1660 if (CurDirHandle) NtClose(CurDirHandle); 1661 1662 if (OldHandle) NtClose(OldHandle); 1663 1664 if (OldCurDirHandle) NtClose(OldCurDirHandle); 1665 1666 if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0) 1667 { 1668 NtClose(OldCurDir->Handle); 1669 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir); 1670 } 1671 1672 return Status; 1673 } 1674 1675 /* 1676 * @implemented 1677 */ 1678 ULONG 1679 NTAPI 1680 RtlGetFullPathName_UEx( 1681 _In_ PWSTR FileName, 1682 _In_ ULONG BufferLength, 1683 _Out_writes_bytes_(BufferLength) PWSTR Buffer, 1684 _Out_opt_ PWSTR *FilePart, 1685 _Out_opt_ RTL_PATH_TYPE *InputPathType) 1686 { 1687 UNICODE_STRING FileNameString; 1688 NTSTATUS status; 1689 1690 if (InputPathType) 1691 *InputPathType = 0; 1692 1693 /* Build the string */ 1694 status = RtlInitUnicodeStringEx(&FileNameString, FileName); 1695 if (!NT_SUCCESS(status)) return 0; 1696 1697 /* Call the extended function */ 1698 return RtlGetFullPathName_Ustr( 1699 &FileNameString, 1700 BufferLength, 1701 Buffer, 1702 (PCWSTR*)FilePart, 1703 NULL, 1704 InputPathType); 1705 } 1706 1707 /****************************************************************** 1708 * RtlGetFullPathName_U (NTDLL.@) 1709 * 1710 * Returns the number of bytes written to buffer (not including the 1711 * terminating NULL) if the function succeeds, or the required number of bytes 1712 * (including the terminating NULL) if the buffer is too small. 1713 * 1714 * file_part will point to the filename part inside buffer (except if we use 1715 * DOS device name, in which case file_in_buf is NULL) 1716 * 1717 * @implemented 1718 */ 1719 1720 /* 1721 * @implemented 1722 */ 1723 ULONG 1724 NTAPI 1725 RtlGetFullPathName_U( 1726 _In_ PCWSTR FileName, 1727 _In_ ULONG Size, 1728 _Out_z_bytecap_(Size) PWSTR Buffer, 1729 _Out_opt_ PWSTR *ShortName) 1730 { 1731 RTL_PATH_TYPE PathType; 1732 1733 /* Call the extended function */ 1734 return RtlGetFullPathName_UEx((PWSTR)FileName, 1735 Size, 1736 Buffer, 1737 ShortName, 1738 &PathType); 1739 } 1740 1741 /* 1742 * @implemented 1743 */ 1744 BOOLEAN 1745 NTAPI 1746 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName, 1747 OUT PUNICODE_STRING NtName, 1748 OUT PCWSTR *PartName, 1749 OUT PRTL_RELATIVE_NAME_U RelativeName) 1750 { 1751 /* Call the internal function */ 1752 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE, 1753 DosName, 1754 NtName, 1755 PartName, 1756 RelativeName)); 1757 } 1758 1759 /* 1760 * @implemented 1761 */ 1762 NTSTATUS 1763 NTAPI 1764 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName, 1765 OUT PUNICODE_STRING NtName, 1766 OUT PCWSTR *PartName, 1767 OUT PRTL_RELATIVE_NAME_U RelativeName) 1768 { 1769 /* Call the internal function */ 1770 return RtlpDosPathNameToRelativeNtPathName_U(FALSE, 1771 DosName, 1772 NtName, 1773 PartName, 1774 RelativeName); 1775 } 1776 1777 /* 1778 * @implemented 1779 */ 1780 BOOLEAN 1781 NTAPI 1782 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName, 1783 OUT PUNICODE_STRING NtName, 1784 OUT PCWSTR *PartName, 1785 OUT PRTL_RELATIVE_NAME_U RelativeName) 1786 { 1787 /* Call the internal function */ 1788 ASSERT(RelativeName); 1789 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE, 1790 DosName, 1791 NtName, 1792 PartName, 1793 RelativeName)); 1794 } 1795 1796 /* 1797 * @implemented 1798 */ 1799 NTSTATUS 1800 NTAPI 1801 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName, 1802 OUT PUNICODE_STRING NtName, 1803 OUT PCWSTR *PartName, 1804 OUT PRTL_RELATIVE_NAME_U RelativeName) 1805 { 1806 /* Call the internal function */ 1807 ASSERT(RelativeName); 1808 return RtlpDosPathNameToRelativeNtPathName_U(TRUE, 1809 DosName, 1810 NtName, 1811 PartName, 1812 RelativeName); 1813 } 1814 1815 /* 1816 * @implemented 1817 */ 1818 NTSTATUS NTAPI RtlNtPathNameToDosPathName(IN ULONG Flags, 1819 IN OUT PRTL_UNICODE_STRING_BUFFER Path, 1820 OUT PULONG PathType, 1821 PULONG Unknown) 1822 { 1823 PCUNICODE_STRING UsePrefix = NULL, AlternatePrefix = NULL; 1824 1825 if (PathType) 1826 *PathType = 0; 1827 1828 if (!Path || Flags) 1829 return STATUS_INVALID_PARAMETER; 1830 1831 /* The initial check is done on Path->String */ 1832 if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix, &Path->String, TRUE)) 1833 { 1834 UsePrefix = &RtlpDosDevicesUncPrefix; 1835 AlternatePrefix = &RtlpDoubleSlashPrefix; 1836 if (PathType) 1837 *PathType = RTL_CONVERTED_UNC_PATH; 1838 } 1839 else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix, &Path->String, FALSE)) 1840 { 1841 UsePrefix = &RtlpDosDevicesPrefix; 1842 if (PathType) 1843 *PathType = RTL_CONVERTED_NT_PATH; 1844 } 1845 1846 if (UsePrefix) 1847 { 1848 NTSTATUS Status; 1849 1850 USHORT Len = Path->String.Length - UsePrefix->Length; 1851 if (AlternatePrefix) 1852 Len += AlternatePrefix->Length; 1853 1854 Status = RtlEnsureBufferSize(0, &Path->ByteBuffer, Len); 1855 if (!NT_SUCCESS(Status)) 1856 return Status; 1857 1858 if (Len + sizeof(UNICODE_NULL) <= Path->ByteBuffer.Size) 1859 { 1860 /* Then, the contents of Path->ByteBuffer are always used... */ 1861 if (AlternatePrefix) 1862 { 1863 memcpy(Path->ByteBuffer.Buffer, AlternatePrefix->Buffer, AlternatePrefix->Length); 1864 memmove(Path->ByteBuffer.Buffer + AlternatePrefix->Length, Path->ByteBuffer.Buffer + UsePrefix->Length, 1865 Len - AlternatePrefix->Length); 1866 } 1867 else 1868 { 1869 memmove(Path->ByteBuffer.Buffer, Path->ByteBuffer.Buffer + UsePrefix->Length, Len); 1870 } 1871 Path->String.Buffer = (PWSTR)Path->ByteBuffer.Buffer; 1872 Path->String.Length = Len; 1873 Path->String.MaximumLength = Path->ByteBuffer.Size; 1874 Path->String.Buffer[Len / sizeof(WCHAR)] = UNICODE_NULL; 1875 } 1876 return STATUS_SUCCESS; 1877 } 1878 1879 if (PathType) 1880 { 1881 switch (RtlDetermineDosPathNameType_Ustr(&Path->String)) 1882 { 1883 case RtlPathTypeUncAbsolute: 1884 case RtlPathTypeDriveAbsolute: 1885 case RtlPathTypeLocalDevice: 1886 case RtlPathTypeRootLocalDevice: 1887 *PathType = RTL_UNCHANGED_DOS_PATH; 1888 break; 1889 case RtlPathTypeUnknown: 1890 case RtlPathTypeDriveRelative: 1891 case RtlPathTypeRooted: 1892 case RtlPathTypeRelative: 1893 *PathType = RTL_UNCHANGED_UNK_PATH; 1894 break; 1895 } 1896 } 1897 1898 return STATUS_SUCCESS; 1899 } 1900 1901 /* 1902 * @implemented 1903 */ 1904 ULONG 1905 NTAPI 1906 RtlDosSearchPath_U(IN PCWSTR Path, 1907 IN PCWSTR FileName, 1908 IN PCWSTR Extension, 1909 IN ULONG Size, 1910 IN PWSTR Buffer, 1911 OUT PWSTR *PartName) 1912 { 1913 NTSTATUS Status; 1914 ULONG ExtensionLength, Length, FileNameLength, PathLength; 1915 UNICODE_STRING TempString; 1916 PWCHAR NewBuffer, BufferStart; 1917 PCWSTR p; 1918 1919 /* Check if this is an absolute path */ 1920 if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative) 1921 { 1922 /* Check if the file exists */ 1923 if (RtlDoesFileExists_UEx(FileName, TRUE)) 1924 { 1925 /* Get the full name, which does the DOS lookup */ 1926 return RtlGetFullPathName_U(FileName, Size, Buffer, PartName); 1927 } 1928 1929 /* Doesn't exist, so fail */ 1930 return 0; 1931 } 1932 1933 /* Scan the filename */ 1934 p = FileName; 1935 while (*p) 1936 { 1937 /* Looking for an extension */ 1938 if (*p == L'.') 1939 { 1940 /* No extension string needed -- it's part of the filename */ 1941 Extension = NULL; 1942 break; 1943 } 1944 1945 /* Next character */ 1946 p++; 1947 } 1948 1949 /* Do we have an extension? */ 1950 if (!Extension) 1951 { 1952 /* Nope, don't worry about one */ 1953 ExtensionLength = 0; 1954 } 1955 else 1956 { 1957 /* Build a temporary string to get the extension length */ 1958 Status = RtlInitUnicodeStringEx(&TempString, Extension); 1959 if (!NT_SUCCESS(Status)) return 0; 1960 ExtensionLength = TempString.Length; 1961 } 1962 1963 /* Build a temporary string to get the path length */ 1964 Status = RtlInitUnicodeStringEx(&TempString, Path); 1965 if (!NT_SUCCESS(Status)) return 0; 1966 PathLength = TempString.Length; 1967 1968 /* Build a temporary string to get the filename length */ 1969 Status = RtlInitUnicodeStringEx(&TempString, FileName); 1970 if (!NT_SUCCESS(Status)) return 0; 1971 FileNameLength = TempString.Length; 1972 1973 /* Allocate the buffer for the new string name */ 1974 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 1975 0, 1976 FileNameLength + 1977 ExtensionLength + 1978 PathLength + 1979 3 * sizeof(WCHAR)); 1980 if (!NewBuffer) 1981 { 1982 /* Fail the call */ 1983 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n", 1984 __FUNCTION__); 1985 return 0; 1986 } 1987 1988 /* Final loop to build the path */ 1989 while (TRUE) 1990 { 1991 /* Check if we have a valid character */ 1992 BufferStart = NewBuffer; 1993 if (*Path) 1994 { 1995 /* Loop as long as there's no semicolon */ 1996 while (*Path != L';') 1997 { 1998 /* Copy the next character */ 1999 *BufferStart++ = *Path++; 2000 if (!*Path) break; 2001 } 2002 2003 /* We found a semi-colon, to stop path processing on this loop */ 2004 if (*Path == L';') ++Path; 2005 } 2006 2007 /* Add a terminating slash if needed */ 2008 if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR)) 2009 { 2010 *BufferStart++ = OBJ_NAME_PATH_SEPARATOR; 2011 } 2012 2013 /* Bail out if we reached the end */ 2014 if (!*Path) Path = NULL; 2015 2016 /* Copy the file name and check if an extension is needed */ 2017 RtlCopyMemory(BufferStart, FileName, FileNameLength); 2018 if (ExtensionLength) 2019 { 2020 /* Copy the extension too */ 2021 RtlCopyMemory((PCHAR)BufferStart + FileNameLength, 2022 Extension, 2023 ExtensionLength + sizeof(WCHAR)); 2024 } 2025 else 2026 { 2027 /* Just NULL-terminate */ 2028 *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL; 2029 } 2030 2031 /* Now, does this file exist? */ 2032 if (RtlDoesFileExists_UEx(NewBuffer, FALSE)) 2033 { 2034 /* Call the full-path API to get the length */ 2035 Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName); 2036 break; 2037 } 2038 2039 /* If we got here, path doesn't exist, so fail the call */ 2040 Length = 0; 2041 if (!Path) break; 2042 } 2043 2044 /* Free the allocation and return the length */ 2045 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 2046 return Length; 2047 } 2048 2049 /* 2050 * @implemented 2051 */ 2052 NTSTATUS 2053 NTAPI 2054 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName, 2055 IN PUNICODE_STRING StaticString, 2056 IN PUNICODE_STRING DynamicString, 2057 IN PUNICODE_STRING *StringUsed, 2058 IN PSIZE_T FilePartSize, 2059 OUT PBOOLEAN NameInvalid, 2060 OUT RTL_PATH_TYPE* PathType, 2061 OUT PSIZE_T LengthNeeded) 2062 { 2063 NTSTATUS Status; 2064 PWCHAR StaticBuffer; 2065 PCWCH ShortName; 2066 ULONG Length; 2067 USHORT StaticLength; 2068 UNICODE_STRING TempDynamicString; 2069 2070 /* Initialize all our locals */ 2071 ShortName = NULL; 2072 StaticBuffer = NULL; 2073 TempDynamicString.Buffer = NULL; 2074 2075 /* Initialize the input parameters */ 2076 if (StringUsed) *StringUsed = NULL; 2077 if (LengthNeeded) *LengthNeeded = 0; 2078 if (FilePartSize) *FilePartSize = 0; 2079 2080 /* Check for invalid parameters */ 2081 if ((DynamicString) && !(StringUsed) && (StaticString)) 2082 { 2083 return STATUS_INVALID_PARAMETER; 2084 } 2085 2086 /* Check if we did not get an input string */ 2087 if (!StaticString) 2088 { 2089 /* Allocate one */ 2090 StaticLength = MAX_PATH * sizeof(WCHAR); 2091 StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR); 2092 if (!StaticBuffer) return STATUS_NO_MEMORY; 2093 } 2094 else 2095 { 2096 /* Use the one we received */ 2097 StaticBuffer = StaticString->Buffer; 2098 StaticLength = StaticString->MaximumLength; 2099 } 2100 2101 /* Call the lower-level function */ 2102 Length = RtlGetFullPathName_Ustr(FileName, 2103 StaticLength, 2104 StaticBuffer, 2105 &ShortName, 2106 NameInvalid, 2107 PathType); 2108 DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer); 2109 if (!Length) 2110 { 2111 /* Fail if it failed */ 2112 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n", 2113 __FUNCTION__, 2114 __LINE__); 2115 Status = STATUS_OBJECT_NAME_INVALID; 2116 goto Quickie; 2117 } 2118 2119 /* Check if it fits inside our static string */ 2120 if ((StaticString) && (Length < StaticLength)) 2121 { 2122 /* Set the final length */ 2123 StaticString->Length = (USHORT)Length; 2124 2125 /* Set the file part size */ 2126 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 2127 2128 /* Return the static string if requested */ 2129 if (StringUsed) *StringUsed = StaticString; 2130 2131 /* We are done with success */ 2132 Status = STATUS_SUCCESS; 2133 goto Quickie; 2134 } 2135 2136 /* Did we not have an input dynamic string ?*/ 2137 if (!DynamicString) 2138 { 2139 /* Return the length we need */ 2140 if (LengthNeeded) *LengthNeeded = Length; 2141 2142 /* And fail such that the caller can try again */ 2143 Status = STATUS_BUFFER_TOO_SMALL; 2144 goto Quickie; 2145 } 2146 2147 /* Check if it fits in our static buffer */ 2148 if ((StaticBuffer) && (Length < StaticLength)) 2149 { 2150 /* NULL-terminate it */ 2151 StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 2152 2153 /* Set the settings for the dynamic string the caller sent */ 2154 DynamicString->MaximumLength = StaticLength; 2155 DynamicString->Length = (USHORT)Length; 2156 DynamicString->Buffer = StaticBuffer; 2157 2158 /* Set the part size */ 2159 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0; 2160 2161 /* Return the dynamic string if requested */ 2162 if (StringUsed) *StringUsed = DynamicString; 2163 2164 /* Do not free the static buffer on exit, and return success */ 2165 StaticBuffer = NULL; 2166 Status = STATUS_SUCCESS; 2167 goto Quickie; 2168 } 2169 2170 /* Now try again under the PEB lock */ 2171 RtlAcquirePebLock(); 2172 Length = RtlGetFullPathName_Ustr(FileName, 2173 StaticLength, 2174 StaticBuffer, 2175 &ShortName, 2176 NameInvalid, 2177 PathType); 2178 if (!Length) 2179 { 2180 /* It failed */ 2181 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 2182 __FUNCTION__, __LINE__); 2183 Status = STATUS_OBJECT_NAME_INVALID; 2184 goto Release; 2185 } 2186 2187 /* Check if it fits inside our static string now */ 2188 if ((StaticString) && (Length < StaticLength)) 2189 { 2190 /* Set the final length */ 2191 StaticString->Length = (USHORT)Length; 2192 2193 /* Set the file part size */ 2194 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 2195 2196 /* Return the static string if requested */ 2197 if (StringUsed) *StringUsed = StaticString; 2198 2199 /* We are done with success */ 2200 Status = STATUS_SUCCESS; 2201 goto Release; 2202 } 2203 2204 /* Check if the path won't even fit in a real string */ 2205 if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) 2206 { 2207 /* Name is way too long, fail */ 2208 Status = STATUS_NAME_TOO_LONG; 2209 goto Release; 2210 } 2211 2212 /* Allocate the string to hold the path name now */ 2213 TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR), 2214 TAG_USTR); 2215 if (!TempDynamicString.Buffer) 2216 { 2217 /* Out of memory, fail */ 2218 Status = STATUS_NO_MEMORY; 2219 goto Release; 2220 } 2221 2222 /* Add space for a NULL terminator, and now check the full path */ 2223 TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL); 2224 Length = RtlGetFullPathName_Ustr(FileName, 2225 Length, 2226 TempDynamicString.Buffer, 2227 &ShortName, 2228 NameInvalid, 2229 PathType); 2230 if (!Length) 2231 { 2232 /* Some path error, so fail out */ 2233 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 2234 __FUNCTION__, __LINE__); 2235 Status = STATUS_OBJECT_NAME_INVALID; 2236 goto Release; 2237 } 2238 2239 /* It should fit in the string we just allocated */ 2240 ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR))); 2241 if (Length > TempDynamicString.MaximumLength) 2242 { 2243 /* This is really weird and would mean some kind of race */ 2244 Status = STATUS_INTERNAL_ERROR; 2245 goto Release; 2246 } 2247 2248 /* Return the file part size */ 2249 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0; 2250 2251 /* Terminate the whole string now */ 2252 TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 2253 2254 /* Finalize the string and return it to the user */ 2255 DynamicString->Buffer = TempDynamicString.Buffer; 2256 DynamicString->Length = (USHORT)Length; 2257 DynamicString->MaximumLength = TempDynamicString.MaximumLength; 2258 if (StringUsed) *StringUsed = DynamicString; 2259 2260 /* Return success and make sure we don't free the buffer on exit */ 2261 TempDynamicString.Buffer = NULL; 2262 Status = STATUS_SUCCESS; 2263 2264 Release: 2265 /* Release the PEB lock */ 2266 RtlReleasePebLock(); 2267 2268 Quickie: 2269 /* Free any buffers we should be freeing */ 2270 DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer); 2271 if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer)) 2272 { 2273 RtlpFreeMemory(StaticBuffer, TAG_USTR); 2274 } 2275 if (TempDynamicString.Buffer) 2276 { 2277 RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR); 2278 } 2279 2280 /* Print out any unusual errors */ 2281 if ((NT_ERROR(Status)) && 2282 (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) 2283 { 2284 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n", 2285 __FUNCTION__, FileName, Status); 2286 } 2287 2288 /* Return, we're all done */ 2289 return Status; 2290 } 2291 2292 /* 2293 * @implemented 2294 */ 2295 NTSTATUS 2296 NTAPI 2297 RtlDosSearchPath_Ustr(IN ULONG Flags, 2298 IN PUNICODE_STRING PathString, 2299 IN PUNICODE_STRING FileNameString, 2300 IN PUNICODE_STRING ExtensionString, 2301 IN PUNICODE_STRING CallerBuffer, 2302 IN OUT PUNICODE_STRING DynamicString OPTIONAL, 2303 OUT PUNICODE_STRING* FullNameOut OPTIONAL, 2304 OUT PSIZE_T FilePartSize OPTIONAL, 2305 OUT PSIZE_T LengthNeeded OPTIONAL) 2306 { 2307 WCHAR StaticCandidateBuffer[MAX_PATH]; 2308 UNICODE_STRING StaticCandidateString; 2309 NTSTATUS Status; 2310 RTL_PATH_TYPE PathType; 2311 PWCHAR p, End, CandidateEnd, SegmentEnd; 2312 SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0; 2313 USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0; 2314 PUNICODE_STRING FullIsolatedPath; 2315 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n", 2316 Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString); 2317 2318 /* Initialize the input string */ 2319 RtlInitEmptyUnicodeString(&StaticCandidateString, 2320 StaticCandidateBuffer, 2321 sizeof(StaticCandidateBuffer)); 2322 2323 /* Initialize optional arguments */ 2324 if (FullNameOut ) *FullNameOut = NULL; 2325 if (FilePartSize) *FilePartSize = 0; 2326 if (LengthNeeded) *LengthNeeded = 0; 2327 if (DynamicString) 2328 { 2329 DynamicString->Length = DynamicString->MaximumLength = 0; 2330 DynamicString->Buffer = NULL; 2331 } 2332 2333 /* Check for invalid parameters */ 2334 if ((Flags & ~7) || 2335 !(PathString) || 2336 !(FileNameString) || 2337 ((CallerBuffer) && (DynamicString) && !(FullNameOut))) 2338 { 2339 /* Fail */ 2340 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__); 2341 Status = STATUS_INVALID_PARAMETER; 2342 goto Quickie; 2343 } 2344 2345 /* First check what kind of path this is */ 2346 PathType = RtlDetermineDosPathNameType_Ustr(FileNameString); 2347 2348 /* Check if the caller wants to prevent relative .\ and ..\ paths */ 2349 if ((Flags & 2) && 2350 (PathType == RtlPathTypeRelative) && 2351 (FileNameString->Length >= (2 * sizeof(WCHAR))) && 2352 (FileNameString->Buffer[0] == L'.') && 2353 ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) || 2354 ((FileNameString->Buffer[1] == L'.') && 2355 ((FileNameString->Length >= (3 * sizeof(WCHAR))) && 2356 (IS_PATH_SEPARATOR(FileNameString->Buffer[2])))))) 2357 { 2358 /* Yes, and this path is like that, so make it seem unknown */ 2359 PathType = RtlPathTypeUnknown; 2360 } 2361 2362 /* Now check relative vs non-relative paths */ 2363 if (PathType == RtlPathTypeRelative) 2364 { 2365 /* Does the caller want SxS? */ 2366 if (Flags & 1) 2367 { 2368 /* Apply the SxS magic */ 2369 FullIsolatedPath = NULL; 2370 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE, 2371 FileNameString, 2372 ExtensionString, 2373 CallerBuffer, 2374 DynamicString, 2375 &FullIsolatedPath, 2376 NULL, 2377 FilePartSize, 2378 LengthNeeded); 2379 if (NT_SUCCESS(Status)) 2380 { 2381 /* We found the SxS path, return it */ 2382 if (FullNameOut) *FullNameOut = FullIsolatedPath; 2383 goto Quickie; 2384 } 2385 else if (Status != STATUS_SXS_KEY_NOT_FOUND) 2386 { 2387 /* Critical SxS error, fail */ 2388 DbgPrint("%s: Failing because call to " 2389 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with " 2390 "status 0x%08lx\n", 2391 __FUNCTION__, 2392 FileNameString, 2393 Status); 2394 goto Quickie; 2395 } 2396 } 2397 2398 /* No SxS key found, or not requested, check if there's an extension */ 2399 if (ExtensionString) 2400 { 2401 /* Save the extension length, and check if there's a file name */ 2402 ExtensionLength = ExtensionString->Length; 2403 if (FileNameString->Length) 2404 { 2405 /* Start parsing the file name */ 2406 End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)]; 2407 while (End > FileNameString->Buffer) 2408 { 2409 /* If we find a path separator, there's no extension */ 2410 if (IS_PATH_SEPARATOR(*--End)) break; 2411 2412 /* Otherwise, did we find an extension dot? */ 2413 if (*End == L'.') 2414 { 2415 /* Ignore what the caller sent it, use the filename's */ 2416 ExtensionString = NULL; 2417 ExtensionLength = 0; 2418 break; 2419 } 2420 } 2421 } 2422 } 2423 2424 /* Check if we got a path */ 2425 if (PathString->Length) 2426 { 2427 /* Start parsing the path name, looking for path separators */ 2428 End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)]; 2429 p = End; 2430 while ((p > PathString->Buffer) && (*--p == L';')) 2431 { 2432 /* This is the size of the path -- handle a trailing slash */ 2433 PathSize = End - p - 1; 2434 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 2435 2436 /* Check if we found a bigger path than before */ 2437 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 2438 2439 /* Keep going with the path after this path separator */ 2440 End = p; 2441 } 2442 2443 /* This is the trailing path, run the same code as above */ 2444 PathSize = End - p; 2445 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 2446 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 2447 2448 /* Finally, convert the largest path size into WCHAR */ 2449 MaxPathSize *= sizeof(WCHAR); 2450 } 2451 2452 /* Use the extension, the file name, and the largest path as the size */ 2453 WorstCaseLength = ExtensionLength + 2454 FileNameString->Length + 2455 (USHORT)MaxPathSize + 2456 sizeof(UNICODE_NULL); 2457 if (WorstCaseLength > UNICODE_STRING_MAX_BYTES) 2458 { 2459 /* It has to fit in a registry string, if not, fail here */ 2460 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed " 2461 "worst case file name length is %Iu bytes\n", 2462 __FUNCTION__, 2463 WorstCaseLength); 2464 Status = STATUS_NAME_TOO_LONG; 2465 goto Quickie; 2466 } 2467 2468 /* Scan the path now, to see if we can find the file */ 2469 p = PathString->Buffer; 2470 End = &p[PathString->Length / sizeof(WCHAR)]; 2471 while (p < End) 2472 { 2473 /* Find out where this path ends */ 2474 for (SegmentEnd = p; 2475 ((SegmentEnd != End) && (*SegmentEnd != L';')); 2476 SegmentEnd++); 2477 2478 /* Compute the size of this path */ 2479 ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR); 2480 2481 /* Handle trailing slash if there isn't one */ 2482 if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1)))) 2483 { 2484 /* Add space for one */ 2485 SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR); 2486 } 2487 2488 /* Now check if our initial static buffer is too small */ 2489 if (StaticCandidateString.MaximumLength < 2490 (SegmentSize + ExtensionLength + FileNameString->Length)) 2491 { 2492 /* At this point we should've been using our static buffer */ 2493 ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer); 2494 if (StaticCandidateString.Buffer != StaticCandidateBuffer) 2495 { 2496 /* Something is really messed up if this was the dynamic string */ 2497 DbgPrint("%s: internal error #1; " 2498 "CandidateString.Buffer = %p; " 2499 "StaticCandidateBuffer = %p\n", 2500 __FUNCTION__, 2501 StaticCandidateString.Buffer, 2502 StaticCandidateBuffer); 2503 Status = STATUS_INTERNAL_ERROR; 2504 goto Quickie; 2505 } 2506 2507 /* We checked before that the maximum possible size shoudl fit! */ 2508 ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) < 2509 UNICODE_STRING_MAX_BYTES); 2510 if ((SegmentSize + ExtensionLength + FileNameString->Length) > 2511 (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR))) 2512 { 2513 /* For some reason it's not fitting anymore. Something messed up */ 2514 DbgPrint("%s: internal error #2; SegmentSize = %u, " 2515 "FileName->Length = %u, DefaultExtensionLength = %u\n", 2516 __FUNCTION__, 2517 SegmentSize, 2518 FileNameString->Length, 2519 ExtensionLength); 2520 Status = STATUS_INTERNAL_ERROR; 2521 goto Quickie; 2522 } 2523 2524 /* Now allocate the dynamic string */ 2525 StaticCandidateString.MaximumLength = FileNameString->Length + 2526 WorstCaseLength; 2527 StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength, 2528 TAG_USTR); 2529 if (!StaticCandidateString.Buffer) 2530 { 2531 /* Out of memory, fail */ 2532 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n", 2533 __FUNCTION__, 2534 StaticCandidateString.MaximumLength); 2535 Status = STATUS_NO_MEMORY; 2536 goto Quickie; 2537 } 2538 } 2539 2540 /* Copy the path in the string */ 2541 RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount); 2542 2543 /* Get to the end of the string, and add the trailing slash if missing */ 2544 CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)]; 2545 if ((SegmentSize) && (SegmentSize != ByteCount)) 2546 { 2547 *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR; 2548 } 2549 2550 /* Copy the filename now */ 2551 RtlCopyMemory(CandidateEnd, 2552 FileNameString->Buffer, 2553 FileNameString->Length); 2554 CandidateEnd += (FileNameString->Length / sizeof(WCHAR)); 2555 2556 /* Check if there was an extension */ 2557 if (ExtensionString) 2558 { 2559 /* Copy the extension too */ 2560 RtlCopyMemory(CandidateEnd, 2561 ExtensionString->Buffer, 2562 ExtensionString->Length); 2563 CandidateEnd += (ExtensionString->Length / sizeof(WCHAR)); 2564 } 2565 2566 /* We are done, terminate it */ 2567 *CandidateEnd = UNICODE_NULL; 2568 2569 /* Now set the final length of the string so it becomes valid */ 2570 StaticCandidateString.Length = (USHORT)(CandidateEnd - 2571 StaticCandidateString.Buffer) * 2572 sizeof(WCHAR); 2573 2574 /* Check if this file exists */ 2575 DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer); 2576 if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE)) 2577 { 2578 /* Awesome, it does, now get the full path */ 2579 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 2580 CallerBuffer, 2581 DynamicString, 2582 (PUNICODE_STRING*)FullNameOut, 2583 FilePartSize, 2584 NULL, 2585 &PathType, 2586 LengthNeeded); 2587 if (!(NT_SUCCESS(Status)) && 2588 ((Status != STATUS_NO_SUCH_FILE) && 2589 (Status != STATUS_BUFFER_TOO_SMALL))) 2590 { 2591 DbgPrint("%s: Failing because we thought we found %wZ on " 2592 "the search path, but RtlGetfullPathNameUStrEx() " 2593 "returned %08lx\n", 2594 __FUNCTION__, 2595 &StaticCandidateString, 2596 Status); 2597 } 2598 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2599 goto Quickie; 2600 } 2601 else 2602 { 2603 /* Otherwise, move to the next path */ 2604 if (SegmentEnd != End) 2605 { 2606 /* Handle the case of the path separator trailing */ 2607 p = SegmentEnd + 1; 2608 } 2609 else 2610 { 2611 p = SegmentEnd; 2612 } 2613 } 2614 } 2615 2616 /* Loop finished and we didn't break out -- fail */ 2617 Status = STATUS_NO_SUCH_FILE; 2618 } 2619 else 2620 { 2621 /* We have a full path, so check if it does exist */ 2622 DPRINT("%wZ\n", FileNameString); 2623 if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE)) 2624 { 2625 /* It doesn't exist, did we have an extension? */ 2626 if (!(ExtensionString) || !(ExtensionString->Length)) 2627 { 2628 /* No extension, so just fail */ 2629 Status = STATUS_NO_SUCH_FILE; 2630 goto Quickie; 2631 } 2632 2633 /* There was an extension, check if the filename already had one */ 2634 if (!(Flags & 4) && (FileNameString->Length)) 2635 { 2636 /* Parse the filename */ 2637 p = FileNameString->Buffer; 2638 End = &p[FileNameString->Length / sizeof(WCHAR)]; 2639 while (End > p) 2640 { 2641 /* If there's a path separator, there's no extension */ 2642 if (IS_PATH_SEPARATOR(*--End)) break; 2643 2644 /* Othwerwise, did we find an extension dot? */ 2645 if (*End == L'.') 2646 { 2647 /* File already had an extension, so fail */ 2648 Status = STATUS_NO_SUCH_FILE; 2649 goto Quickie; 2650 } 2651 } 2652 } 2653 2654 /* So there is an extension, we'll try again by adding it */ 2655 NamePlusExtLength = FileNameString->Length + 2656 ExtensionString->Length + 2657 sizeof(UNICODE_NULL); 2658 if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES) 2659 { 2660 /* It won't fit in any kind of valid string, so fail */ 2661 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n", 2662 __FUNCTION__, 2663 NamePlusExtLength); 2664 Status = STATUS_NAME_TOO_LONG; 2665 goto Quickie; 2666 } 2667 2668 /* Fill it fit in our temporary string? */ 2669 if (NamePlusExtLength > StaticCandidateString.MaximumLength) 2670 { 2671 /* It won't fit anymore, allocate a dynamic string for it */ 2672 StaticCandidateString.MaximumLength = NamePlusExtLength; 2673 StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength, 2674 TAG_USTR); 2675 if (!StaticCandidateString.Buffer) 2676 { 2677 /* Ran out of memory, so fail */ 2678 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n", 2679 __FUNCTION__); 2680 Status = STATUS_NO_MEMORY; 2681 goto Quickie; 2682 } 2683 } 2684 2685 /* Copy the filename */ 2686 RtlCopyUnicodeString(&StaticCandidateString, FileNameString); 2687 2688 /* Copy the extension */ 2689 RtlAppendUnicodeStringToString(&StaticCandidateString, 2690 ExtensionString); 2691 2692 DPRINT("SB: %wZ\n", &StaticCandidateString); 2693 2694 /* And check if this file now exists */ 2695 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE)) 2696 { 2697 /* Still no joy, fail out */ 2698 Status = STATUS_NO_SUCH_FILE; 2699 goto Quickie; 2700 } 2701 2702 /* File was found, get the final full path */ 2703 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 2704 CallerBuffer, 2705 DynamicString, 2706 (PUNICODE_STRING*)FullNameOut, 2707 FilePartSize, 2708 NULL, 2709 &PathType, 2710 LengthNeeded); 2711 if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE)) 2712 { 2713 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() " 2714 "failed with status %08lx\n", 2715 __FUNCTION__, 2716 &StaticCandidateString, 2717 Status); 2718 } 2719 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2720 } 2721 else 2722 { 2723 /* File was found on the first try, get the final full path */ 2724 Status = RtlGetFullPathName_UstrEx(FileNameString, 2725 CallerBuffer, 2726 DynamicString, 2727 (PUNICODE_STRING*)FullNameOut, 2728 FilePartSize, 2729 NULL, 2730 &PathType, 2731 LengthNeeded); 2732 if (!(NT_SUCCESS(Status)) && 2733 ((Status != STATUS_NO_SUCH_FILE) && 2734 (Status != STATUS_BUFFER_TOO_SMALL))) 2735 { 2736 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ " 2737 "failed with status %08lx\n", 2738 __FUNCTION__, 2739 FileNameString, 2740 Status); 2741 } 2742 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2743 } 2744 } 2745 2746 Quickie: 2747 /* Anything that was not an error, turn into STATUS_SUCCESS */ 2748 if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS; 2749 2750 /* Check if we had a dynamic string */ 2751 if ((StaticCandidateString.Buffer) && 2752 (StaticCandidateString.Buffer != StaticCandidateBuffer)) 2753 { 2754 /* Free it */ 2755 RtlFreeUnicodeString(&StaticCandidateString); 2756 } 2757 2758 /* Return the status */ 2759 return Status; 2760 } 2761 2762 /* 2763 * @implemented 2764 */ 2765 BOOLEAN 2766 NTAPI 2767 RtlDoesFileExists_U(IN PCWSTR FileName) 2768 { 2769 /* Call the new function */ 2770 return RtlDoesFileExists_UEx(FileName, TRUE); 2771 } 2772 2773 /* EOF */ 2774