1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/find.c 5 * PURPOSE: Find functions 6 * PROGRAMMERS: Ariadne (ariadne@xs4all.nl) 7 * Pierre Schweitzer (pierre.schweitzer@reactos.org) 8 * Hermes Belusca-Maito 9 */ 10 11 /* INCLUDES *******************************************************************/ 12 13 #include <k32.h> 14 #include <ntstrsafe.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 DEBUG_CHANNEL(kernel32file); 19 20 21 /* TYPES **********************************************************************/ 22 23 #define FIND_DATA_SIZE 0x4000 24 #define FIND_DEVICE_HANDLE ((HANDLE)0x1) 25 26 typedef enum _FIND_DATA_TYPE 27 { 28 FindFile = 1, 29 FindStream = 2 30 } FIND_DATA_TYPE; 31 32 /* 33 * FILE_FULL_DIR_INFORMATION and FILE_BOTH_DIR_INFORMATION structures layout. 34 * 35 * 36 * struct FILE_FULL_DIR_INFORMATION | struct FILE_BOTH_DIR_INFORMATION 37 * ------------------------------------+--------------------------------------- 38 * ULONG NextEntryOffset; | ULONG NextEntryOffset; 39 * ULONG FileIndex; | ULONG FileIndex; 40 * LARGE_INTEGER CreationTime; | LARGE_INTEGER CreationTime; 41 * LARGE_INTEGER LastAccessTime; | LARGE_INTEGER LastAccessTime; 42 * LARGE_INTEGER LastWriteTime; | LARGE_INTEGER LastWriteTime; 43 * LARGE_INTEGER ChangeTime; | LARGE_INTEGER ChangeTime; 44 * LARGE_INTEGER EndOfFile; | LARGE_INTEGER EndOfFile; 45 * LARGE_INTEGER AllocationSize; | LARGE_INTEGER AllocationSize; 46 * ULONG FileAttributes; | ULONG FileAttributes; 47 * ULONG FileNameLength; | ULONG FileNameLength; 48 * ULONG EaSize; | ULONG EaSize; 49 * ------------------------------------+--------------------------------------- 50 * WCHAR FileName[1]; | CCHAR ShortNameLength; 51 * | WCHAR ShortName[12]; 52 * | WCHAR FileName[1]; 53 * 54 * Therefore we can use pointers to FILE_FULL_DIR_INFORMATION when one doesn't 55 * want to refer to the ShortName* fields and FileName (useful for implementing 56 * the FindExInfoBasic functionality for FindFirstFileEx), however a cast to 57 * FILE_BOTH_DIR_INFORMATION is required when one wants to use FileName and 58 * ShortName* fields (needed for the FindExInfoStandard functionality). 59 * 60 */ 61 typedef union _DIR_INFORMATION 62 { 63 PVOID DirInfo; 64 PFILE_FULL_DIR_INFORMATION FullDirInfo; 65 PFILE_BOTH_DIR_INFORMATION BothDirInfo; 66 } DIR_INFORMATION; 67 68 typedef struct _FIND_FILE_DATA 69 { 70 HANDLE Handle; 71 FINDEX_INFO_LEVELS InfoLevel; 72 FINDEX_SEARCH_OPS SearchOp; 73 74 /* 75 * For handling STATUS_BUFFER_OVERFLOW errors emitted by 76 * NtQueryDirectoryFile in the FindNextFile function. 77 */ 78 BOOLEAN HasMoreData; 79 80 /* 81 * "Pointer" to the next file info structure in the buffer. 82 * The type is defined by the 'InfoLevel' parameter. 83 */ 84 DIR_INFORMATION NextDirInfo; 85 86 BYTE Buffer[FIND_DATA_SIZE]; 87 } FIND_FILE_DATA, *PFIND_FILE_DATA; 88 89 typedef struct _FIND_STREAM_DATA 90 { 91 STREAM_INFO_LEVELS InfoLevel; 92 PFILE_STREAM_INFORMATION FileStreamInfo; 93 PFILE_STREAM_INFORMATION CurrentInfo; 94 } FIND_STREAM_DATA, *PFIND_STREAM_DATA; 95 96 typedef struct _FIND_DATA_HANDLE 97 { 98 FIND_DATA_TYPE Type; 99 RTL_CRITICAL_SECTION Lock; 100 101 /* 102 * Pointer to the following finding data, located at 103 * (this + 1). The type is defined by the 'Type' parameter. 104 */ 105 union 106 { 107 PFIND_FILE_DATA FindFileData; 108 PFIND_STREAM_DATA FindStreamData; 109 } u; 110 111 } FIND_DATA_HANDLE, *PFIND_DATA_HANDLE; 112 113 114 /* PRIVATE FUNCTIONS **********************************************************/ 115 116 static VOID 117 CopyDeviceFindData(OUT LPWIN32_FIND_DATAW lpFindFileData, 118 IN LPCWSTR lpFileName, 119 IN ULONG DeviceNameInfo) 120 { 121 LPCWSTR DeviceName; 122 SIZE_T Length; 123 124 _SEH2_TRY 125 { 126 /* DeviceNameInfo == { USHORT Offset; USHORT Length } */ 127 Length = (SIZE_T)(DeviceNameInfo & 0xFFFF); 128 DeviceName = (LPCWSTR)((ULONG_PTR)lpFileName + ((DeviceNameInfo >> 16) & 0xFFFF)); 129 130 /* Return the data */ 131 RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData)); 132 lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; 133 RtlStringCbCopyNW(lpFindFileData->cFileName, 134 sizeof(lpFindFileData->cFileName), 135 DeviceName, Length); 136 } 137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 138 { 139 } 140 _SEH2_END; 141 142 return; 143 } 144 145 static VOID 146 CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData, 147 IN FINDEX_INFO_LEVELS fInfoLevelId, 148 IN DIR_INFORMATION DirInfo) 149 { 150 #define ULARGE_INTEGER_2_FILETIME(ft, ul) \ 151 do { \ 152 (ft).dwHighDateTime = (ul).u.HighPart; \ 153 (ft).dwLowDateTime = (ul).u.LowPart ; \ 154 } while(0) 155 156 _SEH2_TRY 157 { 158 RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData)); 159 160 lpFindFileData->dwFileAttributes = DirInfo.FullDirInfo->FileAttributes; 161 162 ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftCreationTime, DirInfo.FullDirInfo->CreationTime); 163 ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftLastAccessTime, DirInfo.FullDirInfo->LastAccessTime); 164 ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftLastWriteTime, DirInfo.FullDirInfo->LastWriteTime); 165 166 lpFindFileData->nFileSizeHigh = DirInfo.FullDirInfo->EndOfFile.u.HighPart; 167 lpFindFileData->nFileSizeLow = DirInfo.FullDirInfo->EndOfFile.u.LowPart; 168 169 /* dwReserved0 contains the NTFS reparse point tag, if any. */ 170 if (DirInfo.FullDirInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 171 lpFindFileData->dwReserved0 = DirInfo.FullDirInfo->EaSize; 172 else 173 lpFindFileData->dwReserved0 = 0; 174 175 /* Unused dwReserved1 field */ 176 lpFindFileData->dwReserved1 = 0; 177 178 if (fInfoLevelId == FindExInfoStandard) 179 { 180 RtlStringCbCopyNW(lpFindFileData->cFileName, 181 sizeof(lpFindFileData->cFileName), 182 DirInfo.BothDirInfo->FileName, 183 DirInfo.BothDirInfo->FileNameLength); 184 185 RtlStringCbCopyNW(lpFindFileData->cAlternateFileName, 186 sizeof(lpFindFileData->cAlternateFileName), 187 DirInfo.BothDirInfo->ShortName, 188 DirInfo.BothDirInfo->ShortNameLength); 189 } 190 else if (fInfoLevelId == FindExInfoBasic) 191 { 192 RtlStringCbCopyNW(lpFindFileData->cFileName, 193 sizeof(lpFindFileData->cFileName), 194 DirInfo.FullDirInfo->FileName, 195 DirInfo.FullDirInfo->FileNameLength); 196 197 lpFindFileData->cAlternateFileName[0] = UNICODE_NULL; 198 } 199 else 200 { 201 /* Invalid InfoLevelId */ 202 ASSERT(FALSE); 203 } 204 } 205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 206 { 207 } 208 _SEH2_END; 209 210 return; 211 } 212 213 static VOID 214 CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData, 215 OUT PWIN32_FIND_STREAM_DATA lpFindStreamData) 216 { 217 _SEH2_TRY 218 { 219 ASSERT(FindStreamData->CurrentInfo); 220 221 switch (FindStreamData->InfoLevel) 222 { 223 case FindStreamInfoStandard: 224 { 225 ULONG StreamNameLen = min(FindStreamData->CurrentInfo->StreamNameLength, 226 sizeof(lpFindStreamData->cStreamName) - sizeof(WCHAR)); 227 228 RtlZeroMemory(lpFindStreamData, sizeof(*lpFindStreamData)); 229 230 lpFindStreamData->StreamSize.QuadPart = FindStreamData->CurrentInfo->StreamSize.QuadPart; 231 RtlCopyMemory(lpFindStreamData->cStreamName, 232 FindStreamData->CurrentInfo->StreamName, 233 StreamNameLen); 234 lpFindStreamData->cStreamName[StreamNameLen / sizeof(WCHAR)] = UNICODE_NULL; 235 236 break; 237 } 238 239 default: 240 { 241 /* Invalid InfoLevel */ 242 ASSERT(FALSE); 243 break; 244 } 245 } 246 } 247 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 248 { 249 } 250 _SEH2_END; 251 252 return; 253 } 254 255 256 /* PUBLIC FUNCTIONS ***********************************************************/ 257 258 /* 259 * @implemented 260 */ 261 HANDLE 262 WINAPI 263 FindFirstFileA(IN LPCSTR lpFileName, 264 OUT LPWIN32_FIND_DATAA lpFindFileData) 265 { 266 HANDLE hSearch; 267 NTSTATUS Status; 268 ANSI_STRING Ansi; 269 UNICODE_STRING UTF8; 270 PUNICODE_STRING lpFileNameW; 271 WIN32_FIND_DATAW FindFileDataW; 272 273 lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName); 274 if (!lpFileNameW) return INVALID_HANDLE_VALUE; 275 276 hSearch = FindFirstFileExW(lpFileNameW->Buffer, 277 FindExInfoStandard, 278 &FindFileDataW, 279 FindExSearchNameMatch, 280 NULL, 0); 281 if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; 282 283 RtlCopyMemory(lpFindFileData, 284 &FindFileDataW, 285 FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); 286 287 RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName); 288 Ansi.Buffer = lpFindFileData->cFileName; 289 Ansi.Length = 0; 290 Ansi.MaximumLength = sizeof(lpFindFileData->cFileName); 291 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 292 if (!NT_SUCCESS(Status)) 293 { 294 FindClose(hSearch); 295 BaseSetLastNTError(Status); 296 return INVALID_HANDLE_VALUE; 297 } 298 299 RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName); 300 Ansi.Buffer = lpFindFileData->cAlternateFileName; 301 Ansi.Length = 0; 302 Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName); 303 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 304 if (!NT_SUCCESS(Status)) 305 { 306 FindClose(hSearch); 307 BaseSetLastNTError(Status); 308 return INVALID_HANDLE_VALUE; 309 } 310 311 return hSearch; 312 } 313 314 315 /* 316 * @implemented 317 */ 318 HANDLE 319 WINAPI 320 FindFirstFileW(IN LPCWSTR lpFileName, 321 OUT LPWIN32_FIND_DATAW lpFindFileData) 322 { 323 return FindFirstFileExW(lpFileName, 324 FindExInfoStandard, 325 lpFindFileData, 326 FindExSearchNameMatch, 327 NULL, 0); 328 } 329 330 331 /* 332 * @implemented 333 */ 334 BOOL 335 WINAPI 336 FindNextFileA(IN HANDLE hFindFile, 337 OUT LPWIN32_FIND_DATAA lpFindFileData) 338 { 339 NTSTATUS Status; 340 ANSI_STRING Ansi; 341 UNICODE_STRING UTF8; 342 WIN32_FIND_DATAW FindFileDataW; 343 344 if (!FindNextFileW(hFindFile, &FindFileDataW)) 345 return FALSE; 346 347 RtlCopyMemory(lpFindFileData, 348 &FindFileDataW, 349 FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); 350 351 RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName); 352 Ansi.Buffer = lpFindFileData->cFileName; 353 Ansi.Length = 0; 354 Ansi.MaximumLength = sizeof(lpFindFileData->cFileName); 355 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 356 if (!NT_SUCCESS(Status)) 357 { 358 BaseSetLastNTError(Status); 359 return FALSE; 360 } 361 362 RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName); 363 Ansi.Buffer = lpFindFileData->cAlternateFileName; 364 Ansi.Length = 0; 365 Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName); 366 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 367 if (!NT_SUCCESS(Status)) 368 { 369 BaseSetLastNTError(Status); 370 return FALSE; 371 } 372 373 return TRUE; 374 } 375 376 377 /* 378 * @implemented 379 */ 380 BOOL 381 WINAPI 382 FindNextFileW(IN HANDLE hFindFile, 383 OUT LPWIN32_FIND_DATAW lpFindFileData) 384 { 385 NTSTATUS Status = STATUS_SUCCESS; 386 DIR_INFORMATION FoundFile = {NULL}; 387 388 TRACE("FindNextFileW(%p, 0x%p)\n", hFindFile, lpFindFileData); 389 390 if (hFindFile != FIND_DEVICE_HANDLE) 391 { 392 PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindFile; 393 PFIND_FILE_DATA FindFileData; 394 FINDEX_INFO_LEVELS InfoLevel; 395 IO_STATUS_BLOCK IoStatusBlock; 396 DIR_INFORMATION DirInfo = {NULL}, NextDirInfo = {NULL}; 397 398 if (hFindFile == NULL || hFindFile == INVALID_HANDLE_VALUE || 399 FindDataHandle->Type != FindFile) 400 { 401 SetLastError(ERROR_INVALID_HANDLE); 402 return FALSE; 403 } 404 405 RtlEnterCriticalSection(&FindDataHandle->Lock); 406 407 FindFileData = FindDataHandle->u.FindFileData; 408 InfoLevel = FindFileData->InfoLevel; 409 410 do 411 { 412 if (FindFileData->NextDirInfo.DirInfo == NULL) 413 { 414 Status = NtQueryDirectoryFile(FindFileData->Handle, 415 NULL, NULL, NULL, 416 &IoStatusBlock, 417 &FindFileData->Buffer, 418 sizeof(FindFileData->Buffer), 419 (InfoLevel == FindExInfoStandard 420 ? FileBothDirectoryInformation 421 : FileFullDirectoryInformation), 422 FALSE, 423 NULL, /* Use the file pattern from the first call */ 424 FALSE); 425 if (Status == STATUS_BUFFER_OVERFLOW) 426 { 427 FindFileData->HasMoreData = TRUE; 428 Status = STATUS_SUCCESS; 429 } 430 else 431 { 432 if (!NT_SUCCESS(Status)) break; 433 FindFileData->HasMoreData = FALSE; 434 } 435 436 FindFileData->NextDirInfo.DirInfo = &FindFileData->Buffer; 437 } 438 439 DirInfo = FindFileData->NextDirInfo; 440 441 if (DirInfo.FullDirInfo->NextEntryOffset != 0) 442 { 443 ULONG_PTR BufferEnd = (ULONG_PTR)&FindFileData->Buffer + sizeof(FindFileData->Buffer); 444 PWSTR pFileName; 445 446 NextDirInfo.DirInfo = FindFileData->NextDirInfo.DirInfo = 447 (PVOID)((ULONG_PTR)DirInfo.DirInfo + DirInfo.FullDirInfo->NextEntryOffset); 448 449 pFileName = (InfoLevel == FindExInfoStandard 450 ? NextDirInfo.BothDirInfo->FileName 451 : NextDirInfo.FullDirInfo->FileName); 452 453 /* Be paranoid and make sure that the next entry is completely there */ 454 if (BufferEnd < (ULONG_PTR)NextDirInfo.DirInfo || 455 BufferEnd < (ULONG_PTR)&NextDirInfo.FullDirInfo->FileNameLength + sizeof(NextDirInfo.FullDirInfo->FileNameLength) || 456 BufferEnd <= (ULONG_PTR)((ULONG_PTR)pFileName + NextDirInfo.FullDirInfo->FileNameLength)) 457 { 458 FindFileData->NextDirInfo.DirInfo = NULL; 459 } 460 } 461 else 462 { 463 FindFileData->NextDirInfo.DirInfo = NULL; 464 } 465 466 if ((FindFileData->SearchOp != FindExSearchLimitToDirectories) || 467 (DirInfo.FullDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 468 { 469 FoundFile = DirInfo; 470 } 471 } while ( FoundFile.DirInfo == NULL && (FindFileData->NextDirInfo.DirInfo || FindFileData->HasMoreData) ); 472 473 if (FoundFile.DirInfo != NULL) 474 { 475 /* Return the information */ 476 CopyFindData(lpFindFileData, InfoLevel, FoundFile); 477 } 478 479 RtlLeaveCriticalSection(&FindDataHandle->Lock); 480 } 481 482 if (!NT_SUCCESS(Status)) 483 { 484 BaseSetLastNTError(Status); 485 return FALSE; 486 } 487 else if (FoundFile.DirInfo == NULL) 488 { 489 SetLastError(ERROR_NO_MORE_FILES); 490 return FALSE; 491 } 492 493 return TRUE; 494 } 495 496 497 /* 498 * @implemented 499 */ 500 BOOL 501 WINAPI 502 FindClose(HANDLE hFindFile) 503 { 504 TRACE("FindClose(hFindFile %p)\n", hFindFile); 505 506 if (hFindFile == FIND_DEVICE_HANDLE) 507 return TRUE; 508 509 if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE) 510 { 511 SetLastError(ERROR_INVALID_HANDLE); 512 return FALSE; 513 } 514 515 /* Protect with SEH against closing attempts on invalid handles. */ 516 _SEH2_TRY 517 { 518 PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindFile; 519 520 switch (FindDataHandle->Type) 521 { 522 case FindFile: 523 { 524 RtlEnterCriticalSection(&FindDataHandle->Lock); 525 NtClose(FindDataHandle->u.FindFileData->Handle); 526 RtlLeaveCriticalSection(&FindDataHandle->Lock); 527 RtlDeleteCriticalSection(&FindDataHandle->Lock); 528 break; 529 } 530 531 case FindStream: 532 { 533 RtlEnterCriticalSection(&FindDataHandle->Lock); 534 if (FindDataHandle->u.FindStreamData->FileStreamInfo != NULL) 535 { 536 RtlFreeHeap(RtlGetProcessHeap(), 0, 537 FindDataHandle->u.FindStreamData->FileStreamInfo); 538 } 539 RtlLeaveCriticalSection(&FindDataHandle->Lock); 540 RtlDeleteCriticalSection(&FindDataHandle->Lock); 541 break; 542 } 543 544 default: 545 { 546 SetLastError(ERROR_INVALID_HANDLE); 547 _SEH2_YIELD(return FALSE); 548 } 549 } 550 551 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 552 } 553 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 554 { 555 BaseSetLastNTError(_SEH2_GetExceptionCode()); 556 _SEH2_YIELD(return FALSE); 557 } 558 _SEH2_END; 559 560 return TRUE; 561 } 562 563 564 /* 565 * @unimplemented 566 */ 567 HANDLE 568 WINAPI 569 FindFirstFileExA(IN LPCSTR lpFileName, 570 IN FINDEX_INFO_LEVELS fInfoLevelId, 571 OUT LPVOID lpFindFileData, 572 IN FINDEX_SEARCH_OPS fSearchOp, 573 LPVOID lpSearchFilter, 574 IN DWORD dwAdditionalFlags) 575 { 576 HANDLE hSearch; 577 NTSTATUS Status; 578 ANSI_STRING Ansi; 579 UNICODE_STRING UTF8; 580 PUNICODE_STRING lpFileNameW; 581 WIN32_FIND_DATAW FindFileDataW; 582 LPWIN32_FIND_DATAA lpFindFileDataA = (LPWIN32_FIND_DATAA)lpFindFileData; 583 584 if ((fInfoLevelId != FindExInfoStandard && fInfoLevelId != FindExInfoBasic) || 585 fSearchOp == FindExSearchLimitToDevices || 586 dwAdditionalFlags & ~FIND_FIRST_EX_CASE_SENSITIVE /* only supported flag for now */) 587 { 588 SetLastError(fSearchOp == FindExSearchLimitToDevices 589 ? ERROR_NOT_SUPPORTED 590 : ERROR_INVALID_PARAMETER); 591 return INVALID_HANDLE_VALUE; 592 } 593 594 lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName); 595 if (!lpFileNameW) return INVALID_HANDLE_VALUE; 596 597 hSearch = FindFirstFileExW(lpFileNameW->Buffer, 598 fInfoLevelId, 599 &FindFileDataW, 600 fSearchOp, 601 lpSearchFilter, 602 dwAdditionalFlags); 603 if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; 604 605 RtlCopyMemory(lpFindFileDataA, 606 &FindFileDataW, 607 FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); 608 609 RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName); 610 Ansi.Buffer = lpFindFileDataA->cFileName; 611 Ansi.Length = 0; 612 Ansi.MaximumLength = sizeof(lpFindFileDataA->cFileName); 613 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 614 if (!NT_SUCCESS(Status)) 615 { 616 FindClose(hSearch); 617 BaseSetLastNTError(Status); 618 return INVALID_HANDLE_VALUE; 619 } 620 621 if (fInfoLevelId != FindExInfoBasic) 622 { 623 RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName); 624 Ansi.Buffer = lpFindFileDataA->cAlternateFileName; 625 Ansi.Length = 0; 626 Ansi.MaximumLength = sizeof(lpFindFileDataA->cAlternateFileName); 627 Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE); 628 if (!NT_SUCCESS(Status)) 629 { 630 FindClose(hSearch); 631 BaseSetLastNTError(Status); 632 return INVALID_HANDLE_VALUE; 633 } 634 } 635 else 636 { 637 lpFindFileDataA->cAlternateFileName[0] = ANSI_NULL; 638 } 639 640 return hSearch; 641 } 642 643 644 /* 645 * @unimplemented 646 */ 647 HANDLE 648 WINAPI 649 FindFirstFileExW(IN LPCWSTR lpFileName, 650 IN FINDEX_INFO_LEVELS fInfoLevelId, 651 OUT LPVOID lpFindFileData, 652 IN FINDEX_SEARCH_OPS fSearchOp, 653 LPVOID lpSearchFilter, 654 IN DWORD dwAdditionalFlags) 655 { 656 TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName); 657 658 if ((fInfoLevelId != FindExInfoStandard && fInfoLevelId != FindExInfoBasic) || 659 fSearchOp == FindExSearchLimitToDevices || 660 dwAdditionalFlags & ~FIND_FIRST_EX_CASE_SENSITIVE /* only supported flag for now */) 661 { 662 SetLastError(fSearchOp == FindExSearchLimitToDevices 663 ? ERROR_NOT_SUPPORTED 664 : ERROR_INVALID_PARAMETER); 665 return INVALID_HANDLE_VALUE; 666 } 667 668 if (fSearchOp == FindExSearchNameMatch || 669 fSearchOp == FindExSearchLimitToDirectories) 670 { 671 LPWIN32_FIND_DATAW Win32FindData = (LPWIN32_FIND_DATAW)lpFindFileData; 672 PFIND_DATA_HANDLE FindDataHandle; 673 PFIND_FILE_DATA FindFileData; 674 675 UNICODE_STRING NtPath, FilePattern, FileName; 676 PWSTR NtPathBuffer; 677 RTL_RELATIVE_NAME_U RelativePath; 678 ULONG DeviceNameInfo = 0; 679 680 NTSTATUS Status; 681 OBJECT_ATTRIBUTES ObjectAttributes; 682 IO_STATUS_BLOCK IoStatusBlock; 683 HANDLE hDirectory = NULL; 684 685 BOOLEAN HadADot = FALSE; 686 687 /* 688 * May represent many FILE_BOTH_DIR_INFORMATION 689 * or many FILE_FULL_DIR_INFORMATION structures. 690 */ 691 BYTE DirectoryInfo[FIND_DATA_SIZE]; 692 DIR_INFORMATION DirInfo = {&DirectoryInfo}; 693 694 /* The search filter is always unused */ 695 if (lpSearchFilter) 696 { 697 SetLastError(ERROR_INVALID_PARAMETER); 698 return INVALID_HANDLE_VALUE; 699 } 700 701 RtlInitUnicodeString(&FileName, lpFileName); 702 if (FileName.Length != 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'.') 703 { 704 HadADot = TRUE; 705 } 706 707 if (!RtlDosPathNameToNtPathName_U(lpFileName, 708 &NtPath, 709 (PCWSTR*)&FilePattern.Buffer, 710 &RelativePath)) 711 { 712 SetLastError(ERROR_PATH_NOT_FOUND); 713 return INVALID_HANDLE_VALUE; 714 } 715 716 DPRINT("lpFileName = '%S'\n", lpFileName); 717 DPRINT("FilePattern.Buffer = '%S'\n", FilePattern.Buffer); 718 DPRINT("RelativePath.RelativeName = '%wZ'\n", &RelativePath.RelativeName); 719 DPRINT("NtPath.Buffer = '%S'\n", NtPath.Buffer); 720 DPRINT("NtPath - Before = '%wZ'\n", &NtPath); 721 722 /* Save the buffer pointer for later, we need to free it! */ 723 NtPathBuffer = NtPath.Buffer; 724 725 /* 726 * Contrary to what Windows does, check NOW whether or not 727 * lpFileName is a DOS driver. Therefore we don't have to 728 * write broken code to check that. 729 */ 730 if (!FilePattern.Buffer || !*FilePattern.Buffer) 731 { 732 /* No file pattern specified, or DOS device */ 733 734 DeviceNameInfo = RtlIsDosDeviceName_U(lpFileName); 735 if (DeviceNameInfo != 0) 736 { 737 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 738 739 /* OK, it's really a DOS device */ 740 CopyDeviceFindData(Win32FindData, lpFileName, DeviceNameInfo); 741 return FIND_DEVICE_HANDLE; 742 } 743 } 744 745 /* A file pattern was specified, or it was not a DOS device */ 746 747 /* If there is a file pattern then determine its length */ 748 if (FilePattern.Buffer != NULL) 749 { 750 FilePattern.Length = NtPath.Length - 751 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer); 752 } 753 else 754 { 755 FilePattern.Length = 0; 756 } 757 FilePattern.MaximumLength = FilePattern.Length; 758 759 if (RelativePath.RelativeName.Length != 0 && 760 RelativePath.RelativeName.Buffer != FilePattern.Buffer) 761 { 762 if (FilePattern.Buffer != NULL) 763 { 764 /* This is a relative path to RelativePath.ContainingDirectory, adjust NtPath! */ 765 NtPath.Length = NtPath.MaximumLength = 766 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)RelativePath.RelativeName.Buffer); 767 NtPath.Buffer = RelativePath.RelativeName.Buffer; 768 } 769 } 770 else 771 { 772 /* This is an absolute path, NtPath receives the full path */ 773 RelativePath.ContainingDirectory = NULL; 774 if (FilePattern.Buffer != NULL) 775 { 776 NtPath.Length = NtPath.MaximumLength = 777 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer); 778 } 779 } 780 781 DPRINT("NtPath - After = '%wZ'\n", &NtPath); 782 DPRINT("FilePattern = '%wZ'\n", &FilePattern); 783 DPRINT("RelativeTo = 0x%p\n", RelativePath.ContainingDirectory); 784 785 InitializeObjectAttributes(&ObjectAttributes, 786 &NtPath, 787 (dwAdditionalFlags & FIND_FIRST_EX_CASE_SENSITIVE) ? 0 : OBJ_CASE_INSENSITIVE, 788 RelativePath.ContainingDirectory, 789 NULL); 790 791 Status = NtOpenFile(&hDirectory, 792 FILE_LIST_DIRECTORY | SYNCHRONIZE, 793 &ObjectAttributes, 794 &IoStatusBlock, 795 FILE_SHARE_READ | FILE_SHARE_WRITE, 796 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); 797 798 if (!NT_SUCCESS(Status)) 799 { 800 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 801 802 /* Adjust the last error codes */ 803 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 804 Status = STATUS_OBJECT_PATH_NOT_FOUND; 805 else if (Status == STATUS_OBJECT_TYPE_MISMATCH) 806 Status = STATUS_OBJECT_PATH_NOT_FOUND; 807 808 BaseSetLastNTError(Status); 809 return INVALID_HANDLE_VALUE; 810 } 811 812 /* 813 * Fail if there is not any file pattern, 814 * since we are not looking for a device. 815 */ 816 if (FilePattern.Length == 0) 817 { 818 NtClose(hDirectory); 819 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 820 821 SetLastError(ERROR_FILE_NOT_FOUND); 822 return INVALID_HANDLE_VALUE; 823 } 824 825 /* Change pattern: "*.*" --> "*" */ 826 if (FilePattern.Length == 6 && 827 RtlCompareMemory(FilePattern.Buffer, L"*.*", 6) == 6) 828 { 829 FilePattern.Length = 2; 830 } 831 else 832 { 833 /* Translate wildcard from "real" world to DOS world for lower interpretation */ 834 USHORT PatternIndex = 0; 835 while (PatternIndex < FilePattern.Length / sizeof(WCHAR)) 836 { 837 if (PatternIndex > 0) 838 { 839 if (FilePattern.Buffer[PatternIndex] == L'.' && 840 FilePattern.Buffer[PatternIndex - 1] == L'*') 841 { 842 FilePattern.Buffer[PatternIndex - 1] = L'<'; 843 } 844 } 845 846 if (FilePattern.Buffer[PatternIndex] == L'?') 847 { 848 FilePattern.Buffer[PatternIndex] = L'>'; 849 if (PatternIndex > 0) 850 { 851 if (FilePattern.Buffer[PatternIndex - 1] == L'.') 852 { 853 FilePattern.Buffer[PatternIndex - 1] = L'\"'; 854 } 855 } 856 } 857 else if (FilePattern.Buffer[PatternIndex] == L'*') 858 { 859 if (PatternIndex > 0) 860 { 861 if (FilePattern.Buffer[PatternIndex - 1] == L'.') 862 { 863 FilePattern.Buffer[PatternIndex - 1] = L'\"'; 864 } 865 } 866 } 867 868 PatternIndex++; 869 } 870 871 /* Handle partial wc if our last dot was eaten */ 872 if (HadADot) 873 { 874 if (FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] == L'*') 875 { 876 FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] = L'<'; 877 } 878 } 879 } 880 881 Status = NtQueryDirectoryFile(hDirectory, 882 NULL, NULL, NULL, 883 &IoStatusBlock, 884 DirInfo.DirInfo, // == &DirectoryInfo 885 sizeof(DirectoryInfo), 886 (fInfoLevelId == FindExInfoStandard 887 ? FileBothDirectoryInformation 888 : FileFullDirectoryInformation), 889 TRUE, /* Return a single entry */ 890 &FilePattern, 891 TRUE); 892 893 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 894 895 if (!NT_SUCCESS(Status)) 896 { 897 NtClose(hDirectory); 898 BaseSetLastNTError(Status); 899 return INVALID_HANDLE_VALUE; 900 } 901 902 ASSERT(DirInfo.FullDirInfo->NextEntryOffset == 0); 903 904 /* Return the information */ 905 CopyFindData(Win32FindData, fInfoLevelId, DirInfo); 906 907 /* 908 * Initialization of the search handle. 909 */ 910 FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(), 911 HEAP_ZERO_MEMORY, 912 sizeof(FIND_DATA_HANDLE) + 913 sizeof(FIND_FILE_DATA)); 914 if (!FindDataHandle) 915 { 916 NtClose(hDirectory); 917 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 918 return INVALID_HANDLE_VALUE; 919 } 920 921 FindDataHandle->Type = FindFile; 922 FindDataHandle->u.FindFileData = (PFIND_FILE_DATA)(FindDataHandle + 1); 923 FindFileData = FindDataHandle->u.FindFileData; 924 925 FindFileData->Handle = hDirectory; 926 FindFileData->InfoLevel = fInfoLevelId; 927 FindFileData->SearchOp = fSearchOp; 928 FindFileData->HasMoreData = FALSE; 929 FindFileData->NextDirInfo.DirInfo = NULL; 930 931 /* The critical section must always be initialized */ 932 Status = RtlInitializeCriticalSection(&FindDataHandle->Lock); 933 if (!NT_SUCCESS(Status)) 934 { 935 NtClose(hDirectory); 936 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 937 938 BaseSetLastNTError(Status); 939 return INVALID_HANDLE_VALUE; 940 } 941 942 return (HANDLE)FindDataHandle; 943 } 944 else 945 { 946 SetLastError(ERROR_NOT_SUPPORTED); 947 return INVALID_HANDLE_VALUE; 948 } 949 } 950 951 952 /* 953 * @implemented 954 */ 955 HANDLE 956 WINAPI 957 FindFirstStreamW(IN LPCWSTR lpFileName, 958 IN STREAM_INFO_LEVELS InfoLevel, 959 OUT LPVOID lpFindStreamData, 960 IN DWORD dwFlags) 961 { 962 PFIND_DATA_HANDLE FindDataHandle = NULL; 963 PFIND_STREAM_DATA FindStreamData; 964 OBJECT_ATTRIBUTES ObjectAttributes; 965 IO_STATUS_BLOCK IoStatusBlock; 966 UNICODE_STRING NtFilePath; 967 HANDLE FileHandle = NULL; 968 NTSTATUS Status; 969 ULONG BufferSize = 0; 970 971 if (dwFlags != 0 || InfoLevel != FindStreamInfoStandard || 972 lpFindStreamData == NULL) 973 { 974 SetLastError(ERROR_INVALID_PARAMETER); 975 return INVALID_HANDLE_VALUE; 976 } 977 978 /* Validate and translate the filename */ 979 if (!RtlDosPathNameToNtPathName_U(lpFileName, 980 &NtFilePath, 981 NULL, NULL)) 982 { 983 SetLastError(ERROR_PATH_NOT_FOUND); 984 return INVALID_HANDLE_VALUE; 985 } 986 987 /* Open the file */ 988 InitializeObjectAttributes(&ObjectAttributes, 989 &NtFilePath, 990 OBJ_CASE_INSENSITIVE, 991 NULL, 992 NULL); 993 994 Status = NtCreateFile(&FileHandle, 995 0, 996 &ObjectAttributes, 997 &IoStatusBlock, 998 NULL, 0, 999 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 1000 FILE_OPEN, 1001 0, NULL, 0); 1002 if (!NT_SUCCESS(Status)) goto Cleanup; 1003 1004 /* 1005 * Initialization of the search handle. 1006 */ 1007 FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(), 1008 HEAP_ZERO_MEMORY, 1009 sizeof(FIND_DATA_HANDLE) + 1010 sizeof(FIND_STREAM_DATA)); 1011 if (!FindDataHandle) 1012 { 1013 Status = STATUS_NO_MEMORY; 1014 goto Cleanup; 1015 } 1016 1017 FindDataHandle->Type = FindStream; 1018 FindDataHandle->u.FindStreamData = (PFIND_STREAM_DATA)(FindDataHandle + 1); 1019 FindStreamData = FindDataHandle->u.FindStreamData; 1020 1021 FindStreamData->InfoLevel = InfoLevel; 1022 FindStreamData->FileStreamInfo = NULL; 1023 FindStreamData->CurrentInfo = NULL; 1024 1025 /* The critical section must always be initialized */ 1026 Status = RtlInitializeCriticalSection(&FindDataHandle->Lock); 1027 if (!NT_SUCCESS(Status)) 1028 { 1029 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 1030 goto Cleanup; 1031 } 1032 1033 /* Capture all information about the streams */ 1034 do 1035 { 1036 BufferSize += 0x1000; 1037 1038 if (FindStreamData->FileStreamInfo == NULL) 1039 { 1040 FindStreamData->FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 1041 HEAP_ZERO_MEMORY, 1042 BufferSize); 1043 if (FindStreamData->FileStreamInfo == NULL) 1044 { 1045 Status = STATUS_NO_MEMORY; 1046 break; 1047 } 1048 } 1049 else 1050 { 1051 PFILE_STREAM_INFORMATION pfsi; 1052 1053 pfsi = RtlReAllocateHeap(RtlGetProcessHeap(), 1054 0, // HEAP_ZERO_MEMORY, 1055 FindStreamData->FileStreamInfo, 1056 BufferSize); 1057 if (pfsi == NULL) 1058 { 1059 Status = STATUS_NO_MEMORY; 1060 break; 1061 } 1062 1063 FindStreamData->FileStreamInfo = pfsi; 1064 } 1065 1066 Status = NtQueryInformationFile(FileHandle, 1067 &IoStatusBlock, 1068 FindStreamData->FileStreamInfo, 1069 BufferSize, 1070 FileStreamInformation); 1071 1072 } while (Status == STATUS_BUFFER_TOO_SMALL); 1073 1074 if (NT_SUCCESS(Status)) 1075 { 1076 /* Select the first stream and return the information */ 1077 FindStreamData->CurrentInfo = FindStreamData->FileStreamInfo; 1078 CopyStreamData(FindStreamData, lpFindStreamData); 1079 1080 /* All done */ 1081 Status = STATUS_SUCCESS; 1082 } 1083 else 1084 { 1085 if (FindStreamData->FileStreamInfo) 1086 { 1087 RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData->FileStreamInfo); 1088 } 1089 1090 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 1091 } 1092 1093 Cleanup: 1094 if (FileHandle) NtClose(FileHandle); 1095 1096 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath.Buffer); 1097 1098 if (NT_SUCCESS(Status)) 1099 { 1100 return (HANDLE)FindDataHandle; 1101 } 1102 else 1103 { 1104 BaseSetLastNTError(Status); 1105 return INVALID_HANDLE_VALUE; 1106 } 1107 } 1108 1109 1110 /* 1111 * @implemented 1112 */ 1113 BOOL 1114 WINAPI 1115 FindNextStreamW(IN HANDLE hFindStream, 1116 OUT LPVOID lpFindStreamData) 1117 { 1118 PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindStream; 1119 PFIND_STREAM_DATA FindStreamData; 1120 1121 if (hFindStream == NULL || hFindStream == INVALID_HANDLE_VALUE || 1122 FindDataHandle->Type != FindStream) 1123 { 1124 SetLastError(ERROR_INVALID_HANDLE); 1125 return FALSE; 1126 } 1127 1128 RtlEnterCriticalSection(&FindDataHandle->Lock); 1129 1130 FindStreamData = FindDataHandle->u.FindStreamData; 1131 1132 /* Select next stream if possible */ 1133 if (FindStreamData->CurrentInfo->NextEntryOffset != 0) 1134 { 1135 FindStreamData->CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)FindStreamData->CurrentInfo + 1136 FindStreamData->CurrentInfo->NextEntryOffset); 1137 1138 /* Return the information */ 1139 CopyStreamData(FindStreamData, lpFindStreamData); 1140 1141 RtlLeaveCriticalSection(&FindDataHandle->Lock); 1142 return TRUE; 1143 } 1144 else 1145 { 1146 RtlLeaveCriticalSection(&FindDataHandle->Lock); 1147 1148 SetLastError(ERROR_HANDLE_EOF); 1149 return FALSE; 1150 } 1151 } 1152 1153 /* EOF */ 1154