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