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