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