[SHELL32] Fix Control_RunDLLW (#5400)
[reactos.git] / dll / win32 / kernel32 / client / file / find.c
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 */