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