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