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 returned 0x%08lx\n", 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 /****************************************************************** 1677 * RtlGetFullPathName_U (NTDLL.@) 1678 * 1679 * Returns the number of bytes written to buffer (not including the 1680 * terminating NULL) if the function succeeds, or the required number of bytes 1681 * (including the terminating NULL) if the buffer is too small. 1682 * 1683 * file_part will point to the filename part inside buffer (except if we use 1684 * DOS device name, in which case file_in_buf is NULL) 1685 * 1686 * @implemented 1687 */ 1688 1689 /* 1690 * @implemented 1691 */ 1692 ULONG 1693 NTAPI 1694 RtlGetFullPathName_U( 1695 _In_ PCWSTR FileName, 1696 _In_ ULONG Size, 1697 _Out_z_bytecap_(Size) PWSTR Buffer, 1698 _Out_opt_ PWSTR *ShortName) 1699 { 1700 NTSTATUS Status; 1701 UNICODE_STRING FileNameString; 1702 RTL_PATH_TYPE PathType; 1703 1704 /* Build the string */ 1705 Status = RtlInitUnicodeStringEx(&FileNameString, FileName); 1706 if (!NT_SUCCESS(Status)) return 0; 1707 1708 /* Call the extended function */ 1709 return RtlGetFullPathName_Ustr(&FileNameString, 1710 Size, 1711 Buffer, 1712 (PCWSTR*)ShortName, 1713 NULL, 1714 &PathType); 1715 } 1716 1717 /* 1718 * @implemented 1719 */ 1720 BOOLEAN 1721 NTAPI 1722 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName, 1723 OUT PUNICODE_STRING NtName, 1724 OUT PCWSTR *PartName, 1725 OUT PRTL_RELATIVE_NAME_U RelativeName) 1726 { 1727 /* Call the internal function */ 1728 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE, 1729 DosName, 1730 NtName, 1731 PartName, 1732 RelativeName)); 1733 } 1734 1735 /* 1736 * @implemented 1737 */ 1738 NTSTATUS 1739 NTAPI 1740 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName, 1741 OUT PUNICODE_STRING NtName, 1742 OUT PCWSTR *PartName, 1743 OUT PRTL_RELATIVE_NAME_U RelativeName) 1744 { 1745 /* Call the internal function */ 1746 return RtlpDosPathNameToRelativeNtPathName_U(FALSE, 1747 DosName, 1748 NtName, 1749 PartName, 1750 RelativeName); 1751 } 1752 1753 /* 1754 * @implemented 1755 */ 1756 BOOLEAN 1757 NTAPI 1758 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName, 1759 OUT PUNICODE_STRING NtName, 1760 OUT PCWSTR *PartName, 1761 OUT PRTL_RELATIVE_NAME_U RelativeName) 1762 { 1763 /* Call the internal function */ 1764 ASSERT(RelativeName); 1765 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE, 1766 DosName, 1767 NtName, 1768 PartName, 1769 RelativeName)); 1770 } 1771 1772 /* 1773 * @implemented 1774 */ 1775 NTSTATUS 1776 NTAPI 1777 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName, 1778 OUT PUNICODE_STRING NtName, 1779 OUT PCWSTR *PartName, 1780 OUT PRTL_RELATIVE_NAME_U RelativeName) 1781 { 1782 /* Call the internal function */ 1783 ASSERT(RelativeName); 1784 return RtlpDosPathNameToRelativeNtPathName_U(TRUE, 1785 DosName, 1786 NtName, 1787 PartName, 1788 RelativeName); 1789 } 1790 1791 /* 1792 * @implemented 1793 */ 1794 NTSTATUS NTAPI RtlNtPathNameToDosPathName(IN ULONG Flags, 1795 IN OUT PRTL_UNICODE_STRING_BUFFER Path, 1796 OUT PULONG PathType, 1797 PULONG Unknown) 1798 { 1799 PCUNICODE_STRING UsePrefix = NULL, AlternatePrefix = NULL; 1800 1801 if (PathType) 1802 *PathType = 0; 1803 1804 if (!Path || Flags) 1805 return STATUS_INVALID_PARAMETER; 1806 1807 /* The initial check is done on Path->String */ 1808 if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix, &Path->String, TRUE)) 1809 { 1810 UsePrefix = &RtlpDosDevicesUncPrefix; 1811 AlternatePrefix = &RtlpDoubleSlashPrefix; 1812 if (PathType) 1813 *PathType = RTL_CONVERTED_UNC_PATH; 1814 } 1815 else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix, &Path->String, FALSE)) 1816 { 1817 UsePrefix = &RtlpDosDevicesPrefix; 1818 if (PathType) 1819 *PathType = RTL_CONVERTED_NT_PATH; 1820 } 1821 1822 if (UsePrefix) 1823 { 1824 NTSTATUS Status; 1825 1826 USHORT Len = Path->String.Length - UsePrefix->Length; 1827 if (AlternatePrefix) 1828 Len += AlternatePrefix->Length; 1829 1830 Status = RtlEnsureBufferSize(0, &Path->ByteBuffer, Len); 1831 if (!NT_SUCCESS(Status)) 1832 return Status; 1833 1834 if (Len + sizeof(UNICODE_NULL) <= Path->ByteBuffer.Size) 1835 { 1836 /* Then, the contents of Path->ByteBuffer are always used... */ 1837 if (AlternatePrefix) 1838 { 1839 memcpy(Path->ByteBuffer.Buffer, AlternatePrefix->Buffer, AlternatePrefix->Length); 1840 memmove(Path->ByteBuffer.Buffer + AlternatePrefix->Length, Path->ByteBuffer.Buffer + UsePrefix->Length, 1841 Len - AlternatePrefix->Length); 1842 } 1843 else 1844 { 1845 memmove(Path->ByteBuffer.Buffer, Path->ByteBuffer.Buffer + UsePrefix->Length, Len); 1846 } 1847 Path->String.Buffer = (PWSTR)Path->ByteBuffer.Buffer; 1848 Path->String.Length = Len; 1849 Path->String.MaximumLength = Path->ByteBuffer.Size; 1850 Path->String.Buffer[Len / sizeof(WCHAR)] = UNICODE_NULL; 1851 } 1852 return STATUS_SUCCESS; 1853 } 1854 1855 if (PathType) 1856 { 1857 switch (RtlDetermineDosPathNameType_Ustr(&Path->String)) 1858 { 1859 case RtlPathTypeUncAbsolute: 1860 case RtlPathTypeDriveAbsolute: 1861 case RtlPathTypeLocalDevice: 1862 case RtlPathTypeRootLocalDevice: 1863 *PathType = RTL_UNCHANGED_DOS_PATH; 1864 break; 1865 case RtlPathTypeUnknown: 1866 case RtlPathTypeDriveRelative: 1867 case RtlPathTypeRooted: 1868 case RtlPathTypeRelative: 1869 *PathType = RTL_UNCHANGED_UNK_PATH; 1870 break; 1871 } 1872 } 1873 1874 return STATUS_SUCCESS; 1875 } 1876 1877 /* 1878 * @implemented 1879 */ 1880 ULONG 1881 NTAPI 1882 RtlDosSearchPath_U(IN PCWSTR Path, 1883 IN PCWSTR FileName, 1884 IN PCWSTR Extension, 1885 IN ULONG Size, 1886 IN PWSTR Buffer, 1887 OUT PWSTR *PartName) 1888 { 1889 NTSTATUS Status; 1890 ULONG ExtensionLength, Length, FileNameLength, PathLength; 1891 UNICODE_STRING TempString; 1892 PWCHAR NewBuffer, BufferStart; 1893 PCWSTR p; 1894 1895 /* Check if this is an absolute path */ 1896 if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative) 1897 { 1898 /* Check if the file exists */ 1899 if (RtlDoesFileExists_UEx(FileName, TRUE)) 1900 { 1901 /* Get the full name, which does the DOS lookup */ 1902 return RtlGetFullPathName_U(FileName, Size, Buffer, PartName); 1903 } 1904 1905 /* Doesn't exist, so fail */ 1906 return 0; 1907 } 1908 1909 /* Scan the filename */ 1910 p = FileName; 1911 while (*p) 1912 { 1913 /* Looking for an extension */ 1914 if (*p == L'.') 1915 { 1916 /* No extension string needed -- it's part of the filename */ 1917 Extension = NULL; 1918 break; 1919 } 1920 1921 /* Next character */ 1922 p++; 1923 } 1924 1925 /* Do we have an extension? */ 1926 if (!Extension) 1927 { 1928 /* Nope, don't worry about one */ 1929 ExtensionLength = 0; 1930 } 1931 else 1932 { 1933 /* Build a temporary string to get the extension length */ 1934 Status = RtlInitUnicodeStringEx(&TempString, Extension); 1935 if (!NT_SUCCESS(Status)) return 0; 1936 ExtensionLength = TempString.Length; 1937 } 1938 1939 /* Build a temporary string to get the path length */ 1940 Status = RtlInitUnicodeStringEx(&TempString, Path); 1941 if (!NT_SUCCESS(Status)) return 0; 1942 PathLength = TempString.Length; 1943 1944 /* Build a temporary string to get the filename length */ 1945 Status = RtlInitUnicodeStringEx(&TempString, FileName); 1946 if (!NT_SUCCESS(Status)) return 0; 1947 FileNameLength = TempString.Length; 1948 1949 /* Allocate the buffer for the new string name */ 1950 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 1951 0, 1952 FileNameLength + 1953 ExtensionLength + 1954 PathLength + 1955 3 * sizeof(WCHAR)); 1956 if (!NewBuffer) 1957 { 1958 /* Fail the call */ 1959 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n", 1960 __FUNCTION__); 1961 return 0; 1962 } 1963 1964 /* Final loop to build the path */ 1965 while (TRUE) 1966 { 1967 /* Check if we have a valid character */ 1968 BufferStart = NewBuffer; 1969 if (*Path) 1970 { 1971 /* Loop as long as there's no semicolon */ 1972 while (*Path != L';') 1973 { 1974 /* Copy the next character */ 1975 *BufferStart++ = *Path++; 1976 if (!*Path) break; 1977 } 1978 1979 /* We found a semi-colon, to stop path processing on this loop */ 1980 if (*Path == L';') ++Path; 1981 } 1982 1983 /* Add a terminating slash if needed */ 1984 if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR)) 1985 { 1986 *BufferStart++ = OBJ_NAME_PATH_SEPARATOR; 1987 } 1988 1989 /* Bail out if we reached the end */ 1990 if (!*Path) Path = NULL; 1991 1992 /* Copy the file name and check if an extension is needed */ 1993 RtlCopyMemory(BufferStart, FileName, FileNameLength); 1994 if (ExtensionLength) 1995 { 1996 /* Copy the extension too */ 1997 RtlCopyMemory((PCHAR)BufferStart + FileNameLength, 1998 Extension, 1999 ExtensionLength + sizeof(WCHAR)); 2000 } 2001 else 2002 { 2003 /* Just NULL-terminate */ 2004 *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL; 2005 } 2006 2007 /* Now, does this file exist? */ 2008 if (RtlDoesFileExists_UEx(NewBuffer, FALSE)) 2009 { 2010 /* Call the full-path API to get the length */ 2011 Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName); 2012 break; 2013 } 2014 2015 /* If we got here, path doesn't exist, so fail the call */ 2016 Length = 0; 2017 if (!Path) break; 2018 } 2019 2020 /* Free the allocation and return the length */ 2021 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); 2022 return Length; 2023 } 2024 2025 /* 2026 * @implemented 2027 */ 2028 NTSTATUS 2029 NTAPI 2030 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName, 2031 IN PUNICODE_STRING StaticString, 2032 IN PUNICODE_STRING DynamicString, 2033 IN PUNICODE_STRING *StringUsed, 2034 IN PSIZE_T FilePartSize, 2035 OUT PBOOLEAN NameInvalid, 2036 OUT RTL_PATH_TYPE* PathType, 2037 OUT PSIZE_T LengthNeeded) 2038 { 2039 NTSTATUS Status; 2040 PWCHAR StaticBuffer; 2041 PCWCH ShortName; 2042 ULONG Length; 2043 USHORT StaticLength; 2044 UNICODE_STRING TempDynamicString; 2045 2046 /* Initialize all our locals */ 2047 ShortName = NULL; 2048 StaticBuffer = NULL; 2049 TempDynamicString.Buffer = NULL; 2050 2051 /* Initialize the input parameters */ 2052 if (StringUsed) *StringUsed = NULL; 2053 if (LengthNeeded) *LengthNeeded = 0; 2054 if (FilePartSize) *FilePartSize = 0; 2055 2056 /* Check for invalid parameters */ 2057 if ((DynamicString) && !(StringUsed) && (StaticString)) 2058 { 2059 return STATUS_INVALID_PARAMETER; 2060 } 2061 2062 /* Check if we did not get an input string */ 2063 if (!StaticString) 2064 { 2065 /* Allocate one */ 2066 StaticLength = MAX_PATH * sizeof(WCHAR); 2067 StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR); 2068 if (!StaticBuffer) return STATUS_NO_MEMORY; 2069 } 2070 else 2071 { 2072 /* Use the one we received */ 2073 StaticBuffer = StaticString->Buffer; 2074 StaticLength = StaticString->MaximumLength; 2075 } 2076 2077 /* Call the lower-level function */ 2078 Length = RtlGetFullPathName_Ustr(FileName, 2079 StaticLength, 2080 StaticBuffer, 2081 &ShortName, 2082 NameInvalid, 2083 PathType); 2084 DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer); 2085 if (!Length) 2086 { 2087 /* Fail if it failed */ 2088 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n", 2089 __FUNCTION__, 2090 __LINE__); 2091 Status = STATUS_OBJECT_NAME_INVALID; 2092 goto Quickie; 2093 } 2094 2095 /* Check if it fits inside our static string */ 2096 if ((StaticString) && (Length < StaticLength)) 2097 { 2098 /* Set the final length */ 2099 StaticString->Length = (USHORT)Length; 2100 2101 /* Set the file part size */ 2102 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 2103 2104 /* Return the static string if requested */ 2105 if (StringUsed) *StringUsed = StaticString; 2106 2107 /* We are done with success */ 2108 Status = STATUS_SUCCESS; 2109 goto Quickie; 2110 } 2111 2112 /* Did we not have an input dynamic string ?*/ 2113 if (!DynamicString) 2114 { 2115 /* Return the length we need */ 2116 if (LengthNeeded) *LengthNeeded = Length; 2117 2118 /* And fail such that the caller can try again */ 2119 Status = STATUS_BUFFER_TOO_SMALL; 2120 goto Quickie; 2121 } 2122 2123 /* Check if it fits in our static buffer */ 2124 if ((StaticBuffer) && (Length < StaticLength)) 2125 { 2126 /* NULL-terminate it */ 2127 StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 2128 2129 /* Set the settings for the dynamic string the caller sent */ 2130 DynamicString->MaximumLength = StaticLength; 2131 DynamicString->Length = (USHORT)Length; 2132 DynamicString->Buffer = StaticBuffer; 2133 2134 /* Set the part size */ 2135 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0; 2136 2137 /* Return the dynamic string if requested */ 2138 if (StringUsed) *StringUsed = DynamicString; 2139 2140 /* Do not free the static buffer on exit, and return success */ 2141 StaticBuffer = NULL; 2142 Status = STATUS_SUCCESS; 2143 goto Quickie; 2144 } 2145 2146 /* Now try again under the PEB lock */ 2147 RtlAcquirePebLock(); 2148 Length = RtlGetFullPathName_Ustr(FileName, 2149 StaticLength, 2150 StaticBuffer, 2151 &ShortName, 2152 NameInvalid, 2153 PathType); 2154 if (!Length) 2155 { 2156 /* It failed */ 2157 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 2158 __FUNCTION__, __LINE__); 2159 Status = STATUS_OBJECT_NAME_INVALID; 2160 goto Release; 2161 } 2162 2163 /* Check if it fits inside our static string now */ 2164 if ((StaticString) && (Length < StaticLength)) 2165 { 2166 /* Set the final length */ 2167 StaticString->Length = (USHORT)Length; 2168 2169 /* Set the file part size */ 2170 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0; 2171 2172 /* Return the static string if requested */ 2173 if (StringUsed) *StringUsed = StaticString; 2174 2175 /* We are done with success */ 2176 Status = STATUS_SUCCESS; 2177 goto Release; 2178 } 2179 2180 /* Check if the path won't even fit in a real string */ 2181 if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) 2182 { 2183 /* Name is way too long, fail */ 2184 Status = STATUS_NAME_TOO_LONG; 2185 goto Release; 2186 } 2187 2188 /* Allocate the string to hold the path name now */ 2189 TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR), 2190 TAG_USTR); 2191 if (!TempDynamicString.Buffer) 2192 { 2193 /* Out of memory, fail */ 2194 Status = STATUS_NO_MEMORY; 2195 goto Release; 2196 } 2197 2198 /* Add space for a NULL terminator, and now check the full path */ 2199 TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL); 2200 Length = RtlGetFullPathName_Ustr(FileName, 2201 Length, 2202 TempDynamicString.Buffer, 2203 &ShortName, 2204 NameInvalid, 2205 PathType); 2206 if (!Length) 2207 { 2208 /* Some path error, so fail out */ 2209 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", 2210 __FUNCTION__, __LINE__); 2211 Status = STATUS_OBJECT_NAME_INVALID; 2212 goto Release; 2213 } 2214 2215 /* It should fit in the string we just allocated */ 2216 ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR))); 2217 if (Length > TempDynamicString.MaximumLength) 2218 { 2219 /* This is really weird and would mean some kind of race */ 2220 Status = STATUS_INTERNAL_ERROR; 2221 goto Release; 2222 } 2223 2224 /* Return the file part size */ 2225 if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0; 2226 2227 /* Terminate the whole string now */ 2228 TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL; 2229 2230 /* Finalize the string and return it to the user */ 2231 DynamicString->Buffer = TempDynamicString.Buffer; 2232 DynamicString->Length = (USHORT)Length; 2233 DynamicString->MaximumLength = TempDynamicString.MaximumLength; 2234 if (StringUsed) *StringUsed = DynamicString; 2235 2236 /* Return success and make sure we don't free the buffer on exit */ 2237 TempDynamicString.Buffer = NULL; 2238 Status = STATUS_SUCCESS; 2239 2240 Release: 2241 /* Release the PEB lock */ 2242 RtlReleasePebLock(); 2243 2244 Quickie: 2245 /* Free any buffers we should be freeing */ 2246 DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer); 2247 if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer)) 2248 { 2249 RtlpFreeMemory(StaticBuffer, TAG_USTR); 2250 } 2251 if (TempDynamicString.Buffer) 2252 { 2253 RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR); 2254 } 2255 2256 /* Print out any unusual errors */ 2257 if ((NT_ERROR(Status)) && 2258 (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) 2259 { 2260 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n", 2261 __FUNCTION__, FileName, Status); 2262 } 2263 2264 /* Return, we're all done */ 2265 return Status; 2266 } 2267 2268 /* 2269 * @implemented 2270 */ 2271 NTSTATUS 2272 NTAPI 2273 RtlDosSearchPath_Ustr(IN ULONG Flags, 2274 IN PUNICODE_STRING PathString, 2275 IN PUNICODE_STRING FileNameString, 2276 IN PUNICODE_STRING ExtensionString, 2277 IN PUNICODE_STRING CallerBuffer, 2278 IN OUT PUNICODE_STRING DynamicString OPTIONAL, 2279 OUT PUNICODE_STRING* FullNameOut OPTIONAL, 2280 OUT PSIZE_T FilePartSize OPTIONAL, 2281 OUT PSIZE_T LengthNeeded OPTIONAL) 2282 { 2283 WCHAR StaticCandidateBuffer[MAX_PATH]; 2284 UNICODE_STRING StaticCandidateString; 2285 NTSTATUS Status; 2286 RTL_PATH_TYPE PathType; 2287 PWCHAR p, End, CandidateEnd, SegmentEnd; 2288 SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0; 2289 USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0; 2290 PUNICODE_STRING FullIsolatedPath; 2291 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n", 2292 Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString); 2293 2294 /* Initialize the input string */ 2295 RtlInitEmptyUnicodeString(&StaticCandidateString, 2296 StaticCandidateBuffer, 2297 sizeof(StaticCandidateBuffer)); 2298 2299 /* Initialize optional arguments */ 2300 if (FullNameOut ) *FullNameOut = NULL; 2301 if (FilePartSize) *FilePartSize = 0; 2302 if (LengthNeeded) *LengthNeeded = 0; 2303 if (DynamicString) 2304 { 2305 DynamicString->Length = DynamicString->MaximumLength = 0; 2306 DynamicString->Buffer = NULL; 2307 } 2308 2309 /* Check for invalid parameters */ 2310 if ((Flags & ~7) || 2311 !(PathString) || 2312 !(FileNameString) || 2313 ((CallerBuffer) && (DynamicString) && !(FullNameOut))) 2314 { 2315 /* Fail */ 2316 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__); 2317 Status = STATUS_INVALID_PARAMETER; 2318 goto Quickie; 2319 } 2320 2321 /* First check what kind of path this is */ 2322 PathType = RtlDetermineDosPathNameType_Ustr(FileNameString); 2323 2324 /* Check if the caller wants to prevent relative .\ and ..\ paths */ 2325 if ((Flags & 2) && 2326 (PathType == RtlPathTypeRelative) && 2327 (FileNameString->Length >= (2 * sizeof(WCHAR))) && 2328 (FileNameString->Buffer[0] == L'.') && 2329 ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) || 2330 ((FileNameString->Buffer[1] == L'.') && 2331 ((FileNameString->Length >= (3 * sizeof(WCHAR))) && 2332 (IS_PATH_SEPARATOR(FileNameString->Buffer[2])))))) 2333 { 2334 /* Yes, and this path is like that, so make it seem unknown */ 2335 PathType = RtlPathTypeUnknown; 2336 } 2337 2338 /* Now check relative vs non-relative paths */ 2339 if (PathType == RtlPathTypeRelative) 2340 { 2341 /* Does the caller want SxS? */ 2342 if (Flags & 1) 2343 { 2344 /* Apply the SxS magic */ 2345 FullIsolatedPath = NULL; 2346 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE, 2347 FileNameString, 2348 ExtensionString, 2349 CallerBuffer, 2350 DynamicString, 2351 &FullIsolatedPath, 2352 NULL, 2353 FilePartSize, 2354 LengthNeeded); 2355 if (NT_SUCCESS(Status)) 2356 { 2357 /* We found the SxS path, return it */ 2358 if (FullNameOut) *FullNameOut = FullIsolatedPath; 2359 goto Quickie; 2360 } 2361 else if (Status != STATUS_SXS_KEY_NOT_FOUND) 2362 { 2363 /* Critical SxS error, fail */ 2364 DbgPrint("%s: Failing because call to " 2365 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with " 2366 "status 0x%08lx\n", 2367 __FUNCTION__, 2368 FileNameString, 2369 Status); 2370 goto Quickie; 2371 } 2372 } 2373 2374 /* No SxS key found, or not requested, check if there's an extension */ 2375 if (ExtensionString) 2376 { 2377 /* Save the extension length, and check if there's a file name */ 2378 ExtensionLength = ExtensionString->Length; 2379 if (FileNameString->Length) 2380 { 2381 /* Start parsing the file name */ 2382 End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)]; 2383 while (End > FileNameString->Buffer) 2384 { 2385 /* If we find a path separator, there's no extension */ 2386 if (IS_PATH_SEPARATOR(*--End)) break; 2387 2388 /* Otherwise, did we find an extension dot? */ 2389 if (*End == L'.') 2390 { 2391 /* Ignore what the caller sent it, use the filename's */ 2392 ExtensionString = NULL; 2393 ExtensionLength = 0; 2394 break; 2395 } 2396 } 2397 } 2398 } 2399 2400 /* Check if we got a path */ 2401 if (PathString->Length) 2402 { 2403 /* Start parsing the path name, looking for path separators */ 2404 End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)]; 2405 p = End; 2406 while ((p > PathString->Buffer) && (*--p == L';')) 2407 { 2408 /* This is the size of the path -- handle a trailing slash */ 2409 PathSize = End - p - 1; 2410 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 2411 2412 /* Check if we found a bigger path than before */ 2413 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 2414 2415 /* Keep going with the path after this path separator */ 2416 End = p; 2417 } 2418 2419 /* This is the trailing path, run the same code as above */ 2420 PathSize = End - p; 2421 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; 2422 if (PathSize > MaxPathSize) MaxPathSize = PathSize; 2423 2424 /* Finally, convert the largest path size into WCHAR */ 2425 MaxPathSize *= sizeof(WCHAR); 2426 } 2427 2428 /* Use the extension, the file name, and the largest path as the size */ 2429 WorstCaseLength = ExtensionLength + 2430 FileNameString->Length + 2431 (USHORT)MaxPathSize + 2432 sizeof(UNICODE_NULL); 2433 if (WorstCaseLength > UNICODE_STRING_MAX_BYTES) 2434 { 2435 /* It has to fit in a registry string, if not, fail here */ 2436 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed " 2437 "worst case file name length is %Iu bytes\n", 2438 __FUNCTION__, 2439 WorstCaseLength); 2440 Status = STATUS_NAME_TOO_LONG; 2441 goto Quickie; 2442 } 2443 2444 /* Scan the path now, to see if we can find the file */ 2445 p = PathString->Buffer; 2446 End = &p[PathString->Length / sizeof(WCHAR)]; 2447 while (p < End) 2448 { 2449 /* Find out where this path ends */ 2450 for (SegmentEnd = p; 2451 ((SegmentEnd != End) && (*SegmentEnd != L';')); 2452 SegmentEnd++); 2453 2454 /* Compute the size of this path */ 2455 ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR); 2456 2457 /* Handle trailing slash if there isn't one */ 2458 if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1)))) 2459 { 2460 /* Add space for one */ 2461 SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR); 2462 } 2463 2464 /* Now check if our initial static buffer is too small */ 2465 if (StaticCandidateString.MaximumLength < 2466 (SegmentSize + ExtensionLength + FileNameString->Length)) 2467 { 2468 /* At this point we should've been using our static buffer */ 2469 ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer); 2470 if (StaticCandidateString.Buffer != StaticCandidateBuffer) 2471 { 2472 /* Something is really messed up if this was the dynamic string */ 2473 DbgPrint("%s: internal error #1; " 2474 "CandidateString.Buffer = %p; " 2475 "StaticCandidateBuffer = %p\n", 2476 __FUNCTION__, 2477 StaticCandidateString.Buffer, 2478 StaticCandidateBuffer); 2479 Status = STATUS_INTERNAL_ERROR; 2480 goto Quickie; 2481 } 2482 2483 /* We checked before that the maximum possible size shoudl fit! */ 2484 ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) < 2485 UNICODE_STRING_MAX_BYTES); 2486 if ((SegmentSize + ExtensionLength + FileNameString->Length) > 2487 (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR))) 2488 { 2489 /* For some reason it's not fitting anymore. Something messed up */ 2490 DbgPrint("%s: internal error #2; SegmentSize = %u, " 2491 "FileName->Length = %u, DefaultExtensionLength = %u\n", 2492 __FUNCTION__, 2493 SegmentSize, 2494 FileNameString->Length, 2495 ExtensionLength); 2496 Status = STATUS_INTERNAL_ERROR; 2497 goto Quickie; 2498 } 2499 2500 /* Now allocate the dynamic string */ 2501 StaticCandidateString.MaximumLength = FileNameString->Length + 2502 WorstCaseLength; 2503 StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength, 2504 TAG_USTR); 2505 if (!StaticCandidateString.Buffer) 2506 { 2507 /* Out of memory, fail */ 2508 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n", 2509 __FUNCTION__, 2510 StaticCandidateString.MaximumLength); 2511 Status = STATUS_NO_MEMORY; 2512 goto Quickie; 2513 } 2514 } 2515 2516 /* Copy the path in the string */ 2517 RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount); 2518 2519 /* Get to the end of the string, and add the trailing slash if missing */ 2520 CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)]; 2521 if ((SegmentSize) && (SegmentSize != ByteCount)) 2522 { 2523 *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR; 2524 } 2525 2526 /* Copy the filename now */ 2527 RtlCopyMemory(CandidateEnd, 2528 FileNameString->Buffer, 2529 FileNameString->Length); 2530 CandidateEnd += (FileNameString->Length / sizeof(WCHAR)); 2531 2532 /* Check if there was an extension */ 2533 if (ExtensionString) 2534 { 2535 /* Copy the extension too */ 2536 RtlCopyMemory(CandidateEnd, 2537 ExtensionString->Buffer, 2538 ExtensionString->Length); 2539 CandidateEnd += (ExtensionString->Length / sizeof(WCHAR)); 2540 } 2541 2542 /* We are done, terminate it */ 2543 *CandidateEnd = UNICODE_NULL; 2544 2545 /* Now set the final length of the string so it becomes valid */ 2546 StaticCandidateString.Length = (USHORT)(CandidateEnd - 2547 StaticCandidateString.Buffer) * 2548 sizeof(WCHAR); 2549 2550 /* Check if this file exists */ 2551 DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer); 2552 if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE)) 2553 { 2554 /* Awesome, it does, now get the full path */ 2555 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 2556 CallerBuffer, 2557 DynamicString, 2558 (PUNICODE_STRING*)FullNameOut, 2559 FilePartSize, 2560 NULL, 2561 &PathType, 2562 LengthNeeded); 2563 if (!(NT_SUCCESS(Status)) && 2564 ((Status != STATUS_NO_SUCH_FILE) && 2565 (Status != STATUS_BUFFER_TOO_SMALL))) 2566 { 2567 DbgPrint("%s: Failing because we thought we found %wZ on " 2568 "the search path, but RtlGetfullPathNameUStrEx() " 2569 "returned %08lx\n", 2570 __FUNCTION__, 2571 &StaticCandidateString, 2572 Status); 2573 } 2574 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2575 goto Quickie; 2576 } 2577 else 2578 { 2579 /* Otherwise, move to the next path */ 2580 if (SegmentEnd != End) 2581 { 2582 /* Handle the case of the path separator trailing */ 2583 p = SegmentEnd + 1; 2584 } 2585 else 2586 { 2587 p = SegmentEnd; 2588 } 2589 } 2590 } 2591 2592 /* Loop finished and we didn't break out -- fail */ 2593 Status = STATUS_NO_SUCH_FILE; 2594 } 2595 else 2596 { 2597 /* We have a full path, so check if it does exist */ 2598 DPRINT("%wZ\n", FileNameString); 2599 if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE)) 2600 { 2601 /* It doesn't exist, did we have an extension? */ 2602 if (!(ExtensionString) || !(ExtensionString->Length)) 2603 { 2604 /* No extension, so just fail */ 2605 Status = STATUS_NO_SUCH_FILE; 2606 goto Quickie; 2607 } 2608 2609 /* There was an extension, check if the filename already had one */ 2610 if (!(Flags & 4) && (FileNameString->Length)) 2611 { 2612 /* Parse the filename */ 2613 p = FileNameString->Buffer; 2614 End = &p[FileNameString->Length / sizeof(WCHAR)]; 2615 while (End > p) 2616 { 2617 /* If there's a path separator, there's no extension */ 2618 if (IS_PATH_SEPARATOR(*--End)) break; 2619 2620 /* Othwerwise, did we find an extension dot? */ 2621 if (*End == L'.') 2622 { 2623 /* File already had an extension, so fail */ 2624 Status = STATUS_NO_SUCH_FILE; 2625 goto Quickie; 2626 } 2627 } 2628 } 2629 2630 /* So there is an extension, we'll try again by adding it */ 2631 NamePlusExtLength = FileNameString->Length + 2632 ExtensionString->Length + 2633 sizeof(UNICODE_NULL); 2634 if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES) 2635 { 2636 /* It won't fit in any kind of valid string, so fail */ 2637 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n", 2638 __FUNCTION__, 2639 NamePlusExtLength); 2640 Status = STATUS_NAME_TOO_LONG; 2641 goto Quickie; 2642 } 2643 2644 /* Fill it fit in our temporary string? */ 2645 if (NamePlusExtLength > StaticCandidateString.MaximumLength) 2646 { 2647 /* It won't fit anymore, allocate a dynamic string for it */ 2648 StaticCandidateString.MaximumLength = NamePlusExtLength; 2649 StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength, 2650 TAG_USTR); 2651 if (!StaticCandidateString.Buffer) 2652 { 2653 /* Ran out of memory, so fail */ 2654 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n", 2655 __FUNCTION__); 2656 Status = STATUS_NO_MEMORY; 2657 goto Quickie; 2658 } 2659 } 2660 2661 /* Copy the filename */ 2662 RtlCopyUnicodeString(&StaticCandidateString, FileNameString); 2663 2664 /* Copy the extension */ 2665 RtlAppendUnicodeStringToString(&StaticCandidateString, 2666 ExtensionString); 2667 2668 DPRINT("SB: %wZ\n", &StaticCandidateString); 2669 2670 /* And check if this file now exists */ 2671 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE)) 2672 { 2673 /* Still no joy, fail out */ 2674 Status = STATUS_NO_SUCH_FILE; 2675 goto Quickie; 2676 } 2677 2678 /* File was found, get the final full path */ 2679 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, 2680 CallerBuffer, 2681 DynamicString, 2682 (PUNICODE_STRING*)FullNameOut, 2683 FilePartSize, 2684 NULL, 2685 &PathType, 2686 LengthNeeded); 2687 if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE)) 2688 { 2689 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() " 2690 "failed with status %08lx\n", 2691 __FUNCTION__, 2692 &StaticCandidateString, 2693 Status); 2694 } 2695 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2696 } 2697 else 2698 { 2699 /* File was found on the first try, get the final full path */ 2700 Status = RtlGetFullPathName_UstrEx(FileNameString, 2701 CallerBuffer, 2702 DynamicString, 2703 (PUNICODE_STRING*)FullNameOut, 2704 FilePartSize, 2705 NULL, 2706 &PathType, 2707 LengthNeeded); 2708 if (!(NT_SUCCESS(Status)) && 2709 ((Status != STATUS_NO_SUCH_FILE) && 2710 (Status != STATUS_BUFFER_TOO_SMALL))) 2711 { 2712 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ " 2713 "failed with status %08lx\n", 2714 __FUNCTION__, 2715 FileNameString, 2716 Status); 2717 } 2718 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); 2719 } 2720 } 2721 2722 Quickie: 2723 /* Anything that was not an error, turn into STATUS_SUCCESS */ 2724 if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS; 2725 2726 /* Check if we had a dynamic string */ 2727 if ((StaticCandidateString.Buffer) && 2728 (StaticCandidateString.Buffer != StaticCandidateBuffer)) 2729 { 2730 /* Free it */ 2731 RtlFreeUnicodeString(&StaticCandidateString); 2732 } 2733 2734 /* Return the status */ 2735 return Status; 2736 } 2737 2738 /* 2739 * @implemented 2740 */ 2741 BOOLEAN 2742 NTAPI 2743 RtlDoesFileExists_U(IN PCWSTR FileName) 2744 { 2745 /* Call the new function */ 2746 return RtlDoesFileExists_UEx(FileName, TRUE); 2747 } 2748 2749 /* EOF */ 2750