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 * NOTE: NtQueryDirectoryFile requires the buffer to be ULONG-aligned 691 */ 692 DECLSPEC_ALIGN(4) BYTE DirectoryInfo[FIND_DATA_SIZE]; 693 DIR_INFORMATION DirInfo = { .DirInfo = &DirectoryInfo }; 694 695 /* The search filter is always unused */ 696 if (lpSearchFilter) 697 { 698 SetLastError(ERROR_INVALID_PARAMETER); 699 return INVALID_HANDLE_VALUE; 700 } 701 702 RtlInitUnicodeString(&FileName, lpFileName); 703 if (FileName.Length != 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'.') 704 { 705 HadADot = TRUE; 706 } 707 708 if (!RtlDosPathNameToNtPathName_U(lpFileName, 709 &NtPath, 710 (PCWSTR*)&FilePattern.Buffer, 711 &RelativePath)) 712 { 713 SetLastError(ERROR_PATH_NOT_FOUND); 714 return INVALID_HANDLE_VALUE; 715 } 716 717 DPRINT("lpFileName = '%S'\n", lpFileName); 718 DPRINT("FilePattern.Buffer = '%S'\n", FilePattern.Buffer); 719 DPRINT("RelativePath.RelativeName = '%wZ'\n", &RelativePath.RelativeName); 720 DPRINT("NtPath.Buffer = '%S'\n", NtPath.Buffer); 721 DPRINT("NtPath - Before = '%wZ'\n", &NtPath); 722 723 /* Save the buffer pointer for later, we need to free it! */ 724 NtPathBuffer = NtPath.Buffer; 725 726 /* 727 * Contrary to what Windows does, check NOW whether or not 728 * lpFileName is a DOS driver. Therefore we don't have to 729 * write broken code to check that. 730 */ 731 if (!FilePattern.Buffer || !*FilePattern.Buffer) 732 { 733 /* No file pattern specified, or DOS device */ 734 735 DeviceNameInfo = RtlIsDosDeviceName_U(lpFileName); 736 if (DeviceNameInfo != 0) 737 { 738 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 739 740 /* OK, it's really a DOS device */ 741 CopyDeviceFindData(Win32FindData, lpFileName, DeviceNameInfo); 742 return FIND_DEVICE_HANDLE; 743 } 744 } 745 746 /* A file pattern was specified, or it was not a DOS device */ 747 748 /* If there is a file pattern then determine its length */ 749 if (FilePattern.Buffer != NULL) 750 { 751 FilePattern.Length = NtPath.Length - 752 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer); 753 } 754 else 755 { 756 FilePattern.Length = 0; 757 } 758 FilePattern.MaximumLength = FilePattern.Length; 759 760 if (RelativePath.RelativeName.Length != 0 && 761 RelativePath.RelativeName.Buffer != FilePattern.Buffer) 762 { 763 if (FilePattern.Buffer != NULL) 764 { 765 /* This is a relative path to RelativePath.ContainingDirectory, adjust NtPath! */ 766 NtPath.Length = NtPath.MaximumLength = 767 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)RelativePath.RelativeName.Buffer); 768 NtPath.Buffer = RelativePath.RelativeName.Buffer; 769 } 770 } 771 else 772 { 773 /* This is an absolute path, NtPath receives the full path */ 774 RelativePath.ContainingDirectory = NULL; 775 if (FilePattern.Buffer != NULL) 776 { 777 NtPath.Length = NtPath.MaximumLength = 778 (USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer); 779 } 780 } 781 782 DPRINT("NtPath - After = '%wZ'\n", &NtPath); 783 DPRINT("FilePattern = '%wZ'\n", &FilePattern); 784 DPRINT("RelativeTo = 0x%p\n", RelativePath.ContainingDirectory); 785 786 InitializeObjectAttributes(&ObjectAttributes, 787 &NtPath, 788 (dwAdditionalFlags & FIND_FIRST_EX_CASE_SENSITIVE) ? 0 : OBJ_CASE_INSENSITIVE, 789 RelativePath.ContainingDirectory, 790 NULL); 791 792 Status = NtOpenFile(&hDirectory, 793 FILE_LIST_DIRECTORY | SYNCHRONIZE, 794 &ObjectAttributes, 795 &IoStatusBlock, 796 FILE_SHARE_READ | FILE_SHARE_WRITE, 797 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); 798 799 if (!NT_SUCCESS(Status)) 800 { 801 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 802 803 /* Adjust the last error codes */ 804 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 805 Status = STATUS_OBJECT_PATH_NOT_FOUND; 806 else if (Status == STATUS_OBJECT_TYPE_MISMATCH) 807 Status = STATUS_OBJECT_PATH_NOT_FOUND; 808 809 BaseSetLastNTError(Status); 810 return INVALID_HANDLE_VALUE; 811 } 812 813 /* 814 * Fail if there is not any file pattern, 815 * since we are not looking for a device. 816 */ 817 if (FilePattern.Length == 0) 818 { 819 NtClose(hDirectory); 820 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 821 822 SetLastError(ERROR_FILE_NOT_FOUND); 823 return INVALID_HANDLE_VALUE; 824 } 825 826 /* Change pattern: "*.*" --> "*" */ 827 if (FilePattern.Length == 6 && 828 RtlCompareMemory(FilePattern.Buffer, L"*.*", 6) == 6) 829 { 830 FilePattern.Length = 2; 831 } 832 else 833 { 834 /* Translate wildcard from "real" world to DOS world for lower interpretation */ 835 USHORT PatternIndex = 0; 836 while (PatternIndex < FilePattern.Length / sizeof(WCHAR)) 837 { 838 if (PatternIndex > 0) 839 { 840 if (FilePattern.Buffer[PatternIndex] == L'.' && 841 FilePattern.Buffer[PatternIndex - 1] == L'*') 842 { 843 FilePattern.Buffer[PatternIndex - 1] = L'<'; 844 } 845 } 846 847 if (FilePattern.Buffer[PatternIndex] == L'?') 848 { 849 FilePattern.Buffer[PatternIndex] = L'>'; 850 if (PatternIndex > 0) 851 { 852 if (FilePattern.Buffer[PatternIndex - 1] == L'.') 853 { 854 FilePattern.Buffer[PatternIndex - 1] = L'\"'; 855 } 856 } 857 } 858 else if (FilePattern.Buffer[PatternIndex] == L'*') 859 { 860 if (PatternIndex > 0) 861 { 862 if (FilePattern.Buffer[PatternIndex - 1] == L'.') 863 { 864 FilePattern.Buffer[PatternIndex - 1] = L'\"'; 865 } 866 } 867 } 868 869 PatternIndex++; 870 } 871 872 /* Handle partial wc if our last dot was eaten */ 873 if (HadADot) 874 { 875 if (FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] == L'*') 876 { 877 FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] = L'<'; 878 } 879 } 880 } 881 882 Status = NtQueryDirectoryFile(hDirectory, 883 NULL, NULL, NULL, 884 &IoStatusBlock, 885 DirInfo.DirInfo, // == &DirectoryInfo 886 sizeof(DirectoryInfo), 887 (fInfoLevelId == FindExInfoStandard 888 ? FileBothDirectoryInformation 889 : FileFullDirectoryInformation), 890 TRUE, /* Return a single entry */ 891 &FilePattern, 892 TRUE); 893 894 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer); 895 896 if (!NT_SUCCESS(Status)) 897 { 898 NtClose(hDirectory); 899 BaseSetLastNTError(Status); 900 return INVALID_HANDLE_VALUE; 901 } 902 903 ASSERT(DirInfo.FullDirInfo->NextEntryOffset == 0); 904 905 /* Return the information */ 906 CopyFindData(Win32FindData, fInfoLevelId, DirInfo); 907 908 /* 909 * Initialization of the search handle. 910 */ 911 FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(), 912 HEAP_ZERO_MEMORY, 913 sizeof(FIND_DATA_HANDLE) + 914 sizeof(FIND_FILE_DATA)); 915 if (!FindDataHandle) 916 { 917 NtClose(hDirectory); 918 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 919 return INVALID_HANDLE_VALUE; 920 } 921 922 FindDataHandle->Type = FindFile; 923 FindDataHandle->u.FindFileData = (PFIND_FILE_DATA)(FindDataHandle + 1); 924 FindFileData = FindDataHandle->u.FindFileData; 925 926 FindFileData->Handle = hDirectory; 927 FindFileData->InfoLevel = fInfoLevelId; 928 FindFileData->SearchOp = fSearchOp; 929 FindFileData->HasMoreData = FALSE; 930 FindFileData->NextDirInfo.DirInfo = NULL; 931 932 /* The critical section must always be initialized */ 933 Status = RtlInitializeCriticalSection(&FindDataHandle->Lock); 934 if (!NT_SUCCESS(Status)) 935 { 936 NtClose(hDirectory); 937 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 938 939 BaseSetLastNTError(Status); 940 return INVALID_HANDLE_VALUE; 941 } 942 943 return (HANDLE)FindDataHandle; 944 } 945 else 946 { 947 SetLastError(ERROR_NOT_SUPPORTED); 948 return INVALID_HANDLE_VALUE; 949 } 950 } 951 952 953 /* 954 * @implemented 955 */ 956 HANDLE 957 WINAPI 958 FindFirstStreamW(IN LPCWSTR lpFileName, 959 IN STREAM_INFO_LEVELS InfoLevel, 960 OUT LPVOID lpFindStreamData, 961 IN DWORD dwFlags) 962 { 963 PFIND_DATA_HANDLE FindDataHandle = NULL; 964 PFIND_STREAM_DATA FindStreamData; 965 OBJECT_ATTRIBUTES ObjectAttributes; 966 IO_STATUS_BLOCK IoStatusBlock; 967 UNICODE_STRING NtFilePath; 968 HANDLE FileHandle = NULL; 969 NTSTATUS Status; 970 ULONG BufferSize = 0; 971 972 if (dwFlags != 0 || InfoLevel != FindStreamInfoStandard || 973 lpFindStreamData == NULL) 974 { 975 SetLastError(ERROR_INVALID_PARAMETER); 976 return INVALID_HANDLE_VALUE; 977 } 978 979 /* Validate and translate the filename */ 980 if (!RtlDosPathNameToNtPathName_U(lpFileName, 981 &NtFilePath, 982 NULL, NULL)) 983 { 984 SetLastError(ERROR_PATH_NOT_FOUND); 985 return INVALID_HANDLE_VALUE; 986 } 987 988 /* Open the file */ 989 InitializeObjectAttributes(&ObjectAttributes, 990 &NtFilePath, 991 OBJ_CASE_INSENSITIVE, 992 NULL, 993 NULL); 994 995 Status = NtCreateFile(&FileHandle, 996 0, 997 &ObjectAttributes, 998 &IoStatusBlock, 999 NULL, 0, 1000 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 1001 FILE_OPEN, 1002 0, NULL, 0); 1003 if (!NT_SUCCESS(Status)) goto Cleanup; 1004 1005 /* 1006 * Initialization of the search handle. 1007 */ 1008 FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(), 1009 HEAP_ZERO_MEMORY, 1010 sizeof(FIND_DATA_HANDLE) + 1011 sizeof(FIND_STREAM_DATA)); 1012 if (!FindDataHandle) 1013 { 1014 Status = STATUS_NO_MEMORY; 1015 goto Cleanup; 1016 } 1017 1018 FindDataHandle->Type = FindStream; 1019 FindDataHandle->u.FindStreamData = (PFIND_STREAM_DATA)(FindDataHandle + 1); 1020 FindStreamData = FindDataHandle->u.FindStreamData; 1021 1022 FindStreamData->InfoLevel = InfoLevel; 1023 FindStreamData->FileStreamInfo = NULL; 1024 FindStreamData->CurrentInfo = NULL; 1025 1026 /* The critical section must always be initialized */ 1027 Status = RtlInitializeCriticalSection(&FindDataHandle->Lock); 1028 if (!NT_SUCCESS(Status)) 1029 { 1030 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 1031 goto Cleanup; 1032 } 1033 1034 /* Capture all information about the streams */ 1035 do 1036 { 1037 BufferSize += 0x1000; 1038 1039 if (FindStreamData->FileStreamInfo == NULL) 1040 { 1041 FindStreamData->FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 1042 HEAP_ZERO_MEMORY, 1043 BufferSize); 1044 if (FindStreamData->FileStreamInfo == NULL) 1045 { 1046 Status = STATUS_NO_MEMORY; 1047 break; 1048 } 1049 } 1050 else 1051 { 1052 PFILE_STREAM_INFORMATION pfsi; 1053 1054 pfsi = RtlReAllocateHeap(RtlGetProcessHeap(), 1055 0, // HEAP_ZERO_MEMORY, 1056 FindStreamData->FileStreamInfo, 1057 BufferSize); 1058 if (pfsi == NULL) 1059 { 1060 Status = STATUS_NO_MEMORY; 1061 break; 1062 } 1063 1064 FindStreamData->FileStreamInfo = pfsi; 1065 } 1066 1067 Status = NtQueryInformationFile(FileHandle, 1068 &IoStatusBlock, 1069 FindStreamData->FileStreamInfo, 1070 BufferSize, 1071 FileStreamInformation); 1072 1073 } while (Status == STATUS_BUFFER_TOO_SMALL); 1074 1075 if (NT_SUCCESS(Status)) 1076 { 1077 /* Select the first stream and return the information */ 1078 FindStreamData->CurrentInfo = FindStreamData->FileStreamInfo; 1079 CopyStreamData(FindStreamData, lpFindStreamData); 1080 1081 /* All done */ 1082 Status = STATUS_SUCCESS; 1083 } 1084 else 1085 { 1086 if (FindStreamData->FileStreamInfo) 1087 { 1088 RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData->FileStreamInfo); 1089 } 1090 1091 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle); 1092 } 1093 1094 Cleanup: 1095 if (FileHandle) NtClose(FileHandle); 1096 1097 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath.Buffer); 1098 1099 if (NT_SUCCESS(Status)) 1100 { 1101 return (HANDLE)FindDataHandle; 1102 } 1103 else 1104 { 1105 BaseSetLastNTError(Status); 1106 return INVALID_HANDLE_VALUE; 1107 } 1108 } 1109 1110 1111 /* 1112 * @implemented 1113 */ 1114 BOOL 1115 WINAPI 1116 FindNextStreamW(IN HANDLE hFindStream, 1117 OUT LPVOID lpFindStreamData) 1118 { 1119 PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindStream; 1120 PFIND_STREAM_DATA FindStreamData; 1121 1122 if (hFindStream == NULL || hFindStream == INVALID_HANDLE_VALUE || 1123 FindDataHandle->Type != FindStream) 1124 { 1125 SetLastError(ERROR_INVALID_HANDLE); 1126 return FALSE; 1127 } 1128 1129 RtlEnterCriticalSection(&FindDataHandle->Lock); 1130 1131 FindStreamData = FindDataHandle->u.FindStreamData; 1132 1133 /* Select next stream if possible */ 1134 if (FindStreamData->CurrentInfo->NextEntryOffset != 0) 1135 { 1136 FindStreamData->CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)FindStreamData->CurrentInfo + 1137 FindStreamData->CurrentInfo->NextEntryOffset); 1138 1139 /* Return the information */ 1140 CopyStreamData(FindStreamData, lpFindStreamData); 1141 1142 RtlLeaveCriticalSection(&FindDataHandle->Lock); 1143 return TRUE; 1144 } 1145 else 1146 { 1147 RtlLeaveCriticalSection(&FindDataHandle->Lock); 1148 1149 SetLastError(ERROR_HANDLE_EOF); 1150 return FALSE; 1151 } 1152 } 1153 1154 /* EOF */ 1155