Sync with trunk (r48545)
[reactos.git] / dll / win32 / kernel32 / file / find.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/find.c
6 * PURPOSE: Find functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <k32.h>
15 #define NDEBUG
16 #include <debug.h>
17 static ULONG gDebugChannel = kernel32file;
18
19 /* TYPES ********************************************************************/
20
21 #define FIND_DATA_SIZE 0x4000
22
23 #define FIND_DEVICE_HANDLE ((HANDLE)0x1)
24
25 typedef struct _KERNEL32_FIND_FILE_DATA
26 {
27 HANDLE DirectoryHandle;
28 RTL_CRITICAL_SECTION Lock;
29 PFILE_BOTH_DIR_INFORMATION pFileInfo;
30 BOOLEAN DirectoryOnly;
31 BOOLEAN HasMoreData;
32 BOOLEAN HasData;
33 BOOLEAN LockInitialized;
34 } KERNEL32_FIND_FILE_DATA, *PKERNEL32_FIND_FILE_DATA;
35
36 typedef struct _KERNEL32_FIND_STREAM_DATA
37 {
38 STREAM_INFO_LEVELS InfoLevel;
39 PFILE_STREAM_INFORMATION pFileStreamInfo;
40 PFILE_STREAM_INFORMATION pCurrent;
41 } KERNEL32_FIND_STREAM_DATA, *PKERNEL32_FIND_STREAM_DATA;
42
43 typedef enum _KERNEL32_FIND_DATA_TYPE
44 {
45 FileFind,
46 StreamFind
47 } KERNEL32_FIND_DATA_TYPE;
48
49 typedef struct _KERNEL32_FIND_DATA_HEADER
50 {
51 KERNEL32_FIND_DATA_TYPE Type;
52 } KERNEL32_FIND_DATA_HEADER, *PKERNEL32_FIND_DATA_HEADER;
53
54
55 /* FUNCTIONS ****************************************************************/
56
57 static VOID
58 InternalCopyDeviceFindDataW(LPWIN32_FIND_DATAW lpFindFileData,
59 LPCWSTR lpFileName,
60 ULONG DeviceNameInfo)
61 {
62 UNICODE_STRING DeviceName;
63
64 DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF);
65 DeviceName.Buffer = (LPWSTR)((ULONG_PTR)lpFileName + (DeviceNameInfo >> 16));
66
67 /* Return the data */
68 RtlZeroMemory(lpFindFileData,
69 sizeof(*lpFindFileData));
70 lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
71 RtlCopyMemory(lpFindFileData->cFileName,
72 DeviceName.Buffer,
73 DeviceName.Length);
74 }
75
76 static VOID
77 InternalCopyDeviceFindDataA(LPWIN32_FIND_DATAA lpFindFileData,
78 PUNICODE_STRING FileName,
79 ULONG DeviceNameInfo)
80 {
81 UNICODE_STRING DeviceName;
82 ANSI_STRING BufferA;
83 CHAR Buffer[MAX_PATH];
84
85 DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF);
86 DeviceName.Buffer = (LPWSTR)((ULONG_PTR)FileName->Buffer + (DeviceNameInfo >> 16));
87
88 BufferA.MaximumLength = sizeof(Buffer) - sizeof(Buffer[0]);
89 BufferA.Buffer = Buffer;
90 if (bIsFileApiAnsi)
91 RtlUnicodeStringToAnsiString (&BufferA, &DeviceName, FALSE);
92 else
93 RtlUnicodeStringToOemString (&BufferA, &DeviceName, FALSE);
94
95 /* Return the data */
96 RtlZeroMemory(lpFindFileData,
97 sizeof(*lpFindFileData));
98 lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
99 RtlCopyMemory(lpFindFileData->cFileName,
100 BufferA.Buffer,
101 BufferA.Length);
102 }
103
104 static VOID
105 InternalCopyFindDataW(LPWIN32_FIND_DATAW lpFindFileData,
106 PFILE_BOTH_DIR_INFORMATION lpFileInfo)
107 {
108 lpFindFileData->dwFileAttributes = lpFileInfo->FileAttributes;
109
110 lpFindFileData->ftCreationTime.dwHighDateTime = lpFileInfo->CreationTime.u.HighPart;
111 lpFindFileData->ftCreationTime.dwLowDateTime = lpFileInfo->CreationTime.u.LowPart;
112
113 lpFindFileData->ftLastAccessTime.dwHighDateTime = lpFileInfo->LastAccessTime.u.HighPart;
114 lpFindFileData->ftLastAccessTime.dwLowDateTime = lpFileInfo->LastAccessTime.u.LowPart;
115
116 lpFindFileData->ftLastWriteTime.dwHighDateTime = lpFileInfo->LastWriteTime.u.HighPart;
117 lpFindFileData->ftLastWriteTime.dwLowDateTime = lpFileInfo->LastWriteTime.u.LowPart;
118
119 lpFindFileData->nFileSizeHigh = lpFileInfo->EndOfFile.u.HighPart;
120 lpFindFileData->nFileSizeLow = lpFileInfo->EndOfFile.u.LowPart;
121
122 memcpy (lpFindFileData->cFileName, lpFileInfo->FileName, lpFileInfo->FileNameLength);
123 lpFindFileData->cFileName[lpFileInfo->FileNameLength / sizeof(WCHAR)] = 0;
124
125 memcpy (lpFindFileData->cAlternateFileName, lpFileInfo->ShortName, lpFileInfo->ShortNameLength);
126 lpFindFileData->cAlternateFileName[lpFileInfo->ShortNameLength / sizeof(WCHAR)] = 0;
127 }
128
129 static VOID
130 InternalCopyFindDataA(LPWIN32_FIND_DATAA lpFindFileData,
131 PFILE_BOTH_DIR_INFORMATION lpFileInfo)
132 {
133 UNICODE_STRING FileNameU;
134 ANSI_STRING FileNameA;
135
136 lpFindFileData->dwFileAttributes = lpFileInfo->FileAttributes;
137
138 lpFindFileData->ftCreationTime.dwHighDateTime = lpFileInfo->CreationTime.u.HighPart;
139 lpFindFileData->ftCreationTime.dwLowDateTime = lpFileInfo->CreationTime.u.LowPart;
140
141 lpFindFileData->ftLastAccessTime.dwHighDateTime = lpFileInfo->LastAccessTime.u.HighPart;
142 lpFindFileData->ftLastAccessTime.dwLowDateTime = lpFileInfo->LastAccessTime.u.LowPart;
143
144 lpFindFileData->ftLastWriteTime.dwHighDateTime = lpFileInfo->LastWriteTime.u.HighPart;
145 lpFindFileData->ftLastWriteTime.dwLowDateTime = lpFileInfo->LastWriteTime.u.LowPart;
146
147 lpFindFileData->nFileSizeHigh = lpFileInfo->EndOfFile.u.HighPart;
148 lpFindFileData->nFileSizeLow = lpFileInfo->EndOfFile.u.LowPart;
149
150 FileNameU.Length = FileNameU.MaximumLength = (USHORT)lpFileInfo->FileNameLength;
151 FileNameU.Buffer = lpFileInfo->FileName;
152
153 FileNameA.MaximumLength = sizeof(lpFindFileData->cFileName) - sizeof(CHAR);
154 FileNameA.Buffer = lpFindFileData->cFileName;
155
156 /* convert unicode string to ansi (or oem) */
157 if (bIsFileApiAnsi)
158 RtlUnicodeStringToAnsiString (&FileNameA, &FileNameU, FALSE);
159 else
160 RtlUnicodeStringToOemString (&FileNameA, &FileNameU, FALSE);
161
162 FileNameA.Buffer[FileNameA.Length] = 0;
163
164 TRACE("lpFileInfo->ShortNameLength %d\n", lpFileInfo->ShortNameLength);
165
166 FileNameU.Length = FileNameU.MaximumLength = lpFileInfo->ShortNameLength;
167 FileNameU.Buffer = lpFileInfo->ShortName;
168
169 FileNameA.MaximumLength = sizeof(lpFindFileData->cAlternateFileName) - sizeof(CHAR);
170 FileNameA.Buffer = lpFindFileData->cAlternateFileName;
171
172 /* convert unicode string to ansi (or oem) */
173 if (bIsFileApiAnsi)
174 RtlUnicodeStringToAnsiString (&FileNameA, &FileNameU, FALSE);
175 else
176 RtlUnicodeStringToOemString (&FileNameA, &FileNameU, FALSE);
177
178 FileNameA.Buffer[FileNameA.Length] = 0;
179 }
180
181 /*
182 * @implemented
183 */
184 BOOL
185 WINAPI
186 InternalFindNextFile (
187 HANDLE hFindFile,
188 PUNICODE_STRING SearchPattern,
189 PVOID lpFindFileData,
190 BOOL bUnicode
191 )
192 {
193 PKERNEL32_FIND_DATA_HEADER IHeader;
194 PKERNEL32_FIND_FILE_DATA IData;
195 IO_STATUS_BLOCK IoStatusBlock;
196 BOOLEAN Locked = FALSE;
197 PFILE_BOTH_DIR_INFORMATION Buffer, FoundFile = NULL;
198 NTSTATUS Status = STATUS_SUCCESS;
199
200 TRACE("InternalFindNextFile(%lx, %wZ)\n", hFindFile, SearchPattern);
201
202 if (hFindFile != FIND_DEVICE_HANDLE)
203 {
204 IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindFile;
205 if (hFindFile == NULL || hFindFile == INVALID_HANDLE_VALUE ||
206 IHeader->Type != FileFind)
207 {
208 SetLastError (ERROR_INVALID_HANDLE);
209 return FALSE;
210 }
211
212 IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
213 Buffer = (PFILE_BOTH_DIR_INFORMATION)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA));
214
215 if (SearchPattern == NULL)
216 {
217 RtlEnterCriticalSection(&IData->Lock);
218 Locked = TRUE;
219 }
220
221 do
222 {
223 if (IData->HasData)
224 {
225 if (!IData->DirectoryOnly || (IData->pFileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
226 {
227 FoundFile = IData->pFileInfo;
228 }
229
230 if (IData->pFileInfo->NextEntryOffset != 0)
231 {
232 ULONG_PTR BufferEnd;
233
234 IData->pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((ULONG_PTR)IData->pFileInfo + IData->pFileInfo->NextEntryOffset);
235
236 /* Be paranoid and make sure that the next entry is completely there */
237 BufferEnd = (ULONG_PTR)Buffer + FIND_DATA_SIZE;
238 if (BufferEnd < (ULONG_PTR)IData->pFileInfo ||
239 BufferEnd < (ULONG_PTR)&IData->pFileInfo->FileNameLength + sizeof(IData->pFileInfo->FileNameLength) ||
240 BufferEnd <= (ULONG_PTR)&IData->pFileInfo->FileName[IData->pFileInfo->FileNameLength])
241 {
242 goto NeedMoreData;
243 }
244 }
245 else
246 {
247 NeedMoreData:
248 IData->HasData = FALSE;
249
250 if (!IData->HasMoreData)
251 break;
252 }
253 }
254 else
255 {
256 IData->pFileInfo = Buffer;
257 IData->pFileInfo->NextEntryOffset = 0;
258 Status = NtQueryDirectoryFile (IData->DirectoryHandle,
259 NULL,
260 NULL,
261 NULL,
262 &IoStatusBlock,
263 (PVOID)IData->pFileInfo,
264 FIND_DATA_SIZE,
265 FileBothDirectoryInformation,
266 FALSE,
267 SearchPattern,
268 SearchPattern != NULL);
269
270 if (Status == STATUS_BUFFER_OVERFLOW)
271 {
272 IData->HasMoreData = TRUE;
273 Status = STATUS_SUCCESS;
274 }
275 else
276 {
277 if (!NT_SUCCESS(Status))
278 break;
279
280 IData->HasMoreData = FALSE;
281 }
282
283 IData->HasData = TRUE;
284 SearchPattern = NULL;
285 }
286
287 } while (FoundFile == NULL);
288
289 if (FoundFile != NULL)
290 {
291 _SEH2_TRY
292 {
293 if (bUnicode)
294 {
295 InternalCopyFindDataW(lpFindFileData,
296 FoundFile);
297 }
298 else
299 {
300 InternalCopyFindDataA(lpFindFileData,
301 FoundFile);
302 }
303 }
304 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
305 {
306 }
307 _SEH2_END;
308 }
309
310 if (Locked)
311 RtlLeaveCriticalSection(&IData->Lock);
312 }
313
314 if (!NT_SUCCESS(Status))
315 {
316 SetLastErrorByStatus (Status);
317 return FALSE;
318 }
319 else if (FoundFile == NULL)
320 {
321 SetLastError (ERROR_NO_MORE_FILES);
322 return FALSE;
323 }
324
325 return TRUE;
326 }
327
328
329 /*
330 * @implemented
331 */
332 HANDLE
333 WINAPI
334 InternalFindFirstFile (
335 LPCWSTR lpFileName,
336 BOOLEAN DirectoryOnly,
337 PVOID lpFindFileData,
338 BOOL bUnicode
339 )
340 {
341 OBJECT_ATTRIBUTES ObjectAttributes;
342 PKERNEL32_FIND_DATA_HEADER IHeader;
343 PKERNEL32_FIND_FILE_DATA IData;
344 IO_STATUS_BLOCK IoStatusBlock;
345 UNICODE_STRING NtPathU, FileName, PathFileName;
346 NTSTATUS Status;
347 PWSTR NtPathBuffer;
348 BOOLEAN RemovedLastChar = FALSE;
349 BOOL bResult;
350 CURDIR DirInfo;
351 ULONG DeviceNameInfo;
352 HANDLE hDirectory = NULL;
353
354 TRACE("FindFirstFileW(lpFileName %S)\n",
355 lpFileName);
356
357 RtlZeroMemory(&PathFileName,
358 sizeof(PathFileName));
359 RtlInitUnicodeString(&FileName,
360 lpFileName);
361
362 bResult = RtlDosPathNameToNtPathName_U (lpFileName,
363 &NtPathU,
364 (PCWSTR *)((ULONG_PTR)&PathFileName.Buffer),
365 &DirInfo);
366 if (FALSE == bResult)
367 {
368 SetLastError(ERROR_PATH_NOT_FOUND);
369 return INVALID_HANDLE_VALUE;
370 }
371
372 /* Save the buffer pointer for later, we need to free it! */
373 NtPathBuffer = NtPathU.Buffer;
374
375 /* If there is a file name/pattern then determine it's length */
376 if (PathFileName.Buffer != NULL)
377 {
378 PathFileName.Length = NtPathU.Length -
379 (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)NtPathU.Buffer);
380 }
381 PathFileName.MaximumLength = PathFileName.Length;
382
383 if (DirInfo.DosPath.Length != 0 && DirInfo.DosPath.Buffer != PathFileName.Buffer)
384 {
385 if (PathFileName.Buffer != NULL)
386 {
387 /* This is a relative path to DirInfo.Handle, adjust NtPathU! */
388 NtPathU.Length = NtPathU.MaximumLength =
389 (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)DirInfo.DosPath.Buffer);
390 NtPathU.Buffer = DirInfo.DosPath.Buffer;
391 }
392 }
393 else
394 {
395 /* This is an absolute path, NtPathU receives the full path */
396 DirInfo.Handle = NULL;
397 if (PathFileName.Buffer != NULL)
398 {
399 NtPathU.Length = NtPathU.MaximumLength =
400 (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)NtPathU.Buffer);
401 }
402 }
403
404 /* Remove the last character of the path (Unless the path is a drive and
405 ends with ":\"). If the caller however supplies a path to a device, such
406 as "C:\NUL" then the last character gets cut off, which later results in
407 NtOpenFile to return STATUS_OBJECT_NAME_NOT_FOUND, which in turn triggers
408 a fake DOS device check with RtlIsDosDeviceName_U. However, if there is a
409 real device with a name eg. "NU" in the system, FindFirstFile will succeed,
410 rendering the fake DOS device check useless... Why would they invent such a
411 stupid and broken behavior?! */
412 if (NtPathU.Length >= 2 * sizeof(WCHAR) &&
413 NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 1] != L'\\' &&
414 NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 2] != L':')
415 {
416 NtPathU.Length -= sizeof(WCHAR);
417 RemovedLastChar = TRUE;
418 }
419
420 TRACE("lpFileName: \"%ws\"\n", lpFileName);
421 TRACE("NtPathU: \"%wZ\"\n", &NtPathU);
422 TRACE("PathFileName: \"%wZ\"\n", &PathFileName);
423 TRACE("RelativeTo: 0x%p\n", DirInfo.Handle);
424
425 InitializeObjectAttributes (&ObjectAttributes,
426 &NtPathU,
427 OBJ_CASE_INSENSITIVE,
428 DirInfo.Handle,
429 NULL);
430
431 Status = NtOpenFile (&hDirectory,
432 FILE_LIST_DIRECTORY | SYNCHRONIZE,
433 &ObjectAttributes,
434 &IoStatusBlock,
435 FILE_SHARE_READ|FILE_SHARE_WRITE,
436 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
437
438 if (Status == STATUS_NOT_A_DIRECTORY && RemovedLastChar)
439 {
440 /* Try again, this time with the last character ... */
441 NtPathU.Length += sizeof(WCHAR);
442
443 Status = NtOpenFile (&hDirectory,
444 FILE_LIST_DIRECTORY | SYNCHRONIZE,
445 &ObjectAttributes,
446 &IoStatusBlock,
447 FILE_SHARE_READ|FILE_SHARE_WRITE,
448 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
449
450 NtPathU.Length += sizeof(WCHAR);
451 }
452
453 if (!NT_SUCCESS(Status))
454 {
455 RtlFreeHeap (hProcessHeap,
456 0,
457 NtPathBuffer);
458
459 /* See if the application tries to look for a DOS device */
460 DeviceNameInfo = RtlIsDosDeviceName_U((PWSTR)((ULONG_PTR)lpFileName));
461 if (DeviceNameInfo != 0)
462 {
463 if (bUnicode)
464 {
465 InternalCopyDeviceFindDataW(lpFindFileData,
466 lpFileName,
467 DeviceNameInfo);
468 }
469 else
470 {
471 InternalCopyDeviceFindDataA(lpFindFileData,
472 &FileName,
473 DeviceNameInfo);
474 }
475
476 return FIND_DEVICE_HANDLE;
477 }
478
479 SetLastErrorByStatus (Status);
480 return INVALID_HANDLE_VALUE;
481 }
482
483 if (PathFileName.Length == 0)
484 {
485 /* No file part?! */
486 NtClose(hDirectory);
487 RtlFreeHeap (hProcessHeap,
488 0,
489 NtPathBuffer);
490 SetLastError(ERROR_FILE_NOT_FOUND);
491 return INVALID_HANDLE_VALUE;
492 }
493
494 IHeader = RtlAllocateHeap (hProcessHeap,
495 HEAP_ZERO_MEMORY,
496 sizeof(KERNEL32_FIND_DATA_HEADER) +
497 sizeof(KERNEL32_FIND_FILE_DATA) + FIND_DATA_SIZE);
498 if (NULL == IHeader)
499 {
500 RtlFreeHeap (hProcessHeap,
501 0,
502 NtPathBuffer);
503 NtClose(hDirectory);
504
505 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
506 return INVALID_HANDLE_VALUE;
507 }
508
509 IHeader->Type = FileFind;
510 IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
511 IData->DirectoryHandle = hDirectory;
512 IData->HasMoreData = TRUE;
513
514 /* change pattern: "*.*" --> "*" */
515 if (PathFileName.Length == 6 &&
516 RtlCompareMemory(PathFileName.Buffer,
517 L"*.*",
518 6) == 6)
519 {
520 PathFileName.Length = 2;
521 }
522
523 IData->pFileInfo = (PVOID)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA));
524 IData->pFileInfo->FileIndex = 0;
525 IData->DirectoryOnly = DirectoryOnly;
526
527 bResult = InternalFindNextFile((HANDLE)IHeader,
528 &PathFileName,
529 lpFindFileData,
530 bUnicode);
531
532 RtlFreeHeap (hProcessHeap,
533 0,
534 NtPathBuffer);
535
536 if (!bResult)
537 {
538 FindClose((HANDLE)IHeader);
539 return INVALID_HANDLE_VALUE;
540 }
541
542 RtlInitializeCriticalSection(&IData->Lock);
543 IData->LockInitialized = TRUE;
544
545 return (HANDLE)IHeader;
546 }
547
548
549 /*
550 * @implemented
551 */
552 HANDLE
553 WINAPI
554 FindFirstFileA (
555 LPCSTR lpFileName,
556 LPWIN32_FIND_DATAA lpFindFileData
557 )
558 {
559 return FindFirstFileExA (lpFileName,
560 FindExInfoStandard,
561 (LPVOID)lpFindFileData,
562 FindExSearchNameMatch,
563 NULL,
564 0);
565 }
566
567
568 /*
569 * @implemented
570 */
571 BOOL
572 WINAPI
573 FindNextFileA (
574 HANDLE hFindFile,
575 LPWIN32_FIND_DATAA lpFindFileData)
576 {
577 return InternalFindNextFile (hFindFile,
578 NULL,
579 lpFindFileData,
580 FALSE);
581 }
582
583
584 /*
585 * @implemented
586 */
587 BOOL
588 WINAPI
589 FindClose (
590 HANDLE hFindFile
591 )
592 {
593 PKERNEL32_FIND_DATA_HEADER IHeader;
594
595 TRACE("FindClose(hFindFile %x)\n",hFindFile);
596
597 if (hFindFile == FIND_DEVICE_HANDLE)
598 return TRUE;
599
600 if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
601 {
602 SetLastError (ERROR_INVALID_HANDLE);
603 return FALSE;
604 }
605
606 IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindFile;
607
608 switch (IHeader->Type)
609 {
610 case FileFind:
611 {
612 PKERNEL32_FIND_FILE_DATA IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
613 CloseHandle (IData->DirectoryHandle);
614 if (IData->LockInitialized)
615 RtlDeleteCriticalSection(&IData->Lock);
616 IData->LockInitialized = FALSE;
617 break;
618 }
619
620 case StreamFind:
621 {
622 PKERNEL32_FIND_STREAM_DATA IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
623 if (IData->pFileStreamInfo != NULL)
624 {
625 RtlFreeHeap (hProcessHeap, 0, IData->pFileStreamInfo);
626 }
627 break;
628 }
629
630 default:
631 SetLastError (ERROR_INVALID_HANDLE);
632 return FALSE;
633 }
634
635 RtlFreeHeap (hProcessHeap, 0, IHeader);
636
637 return TRUE;
638 }
639
640
641 /*
642 * @implemented
643 */
644 HANDLE
645 WINAPI
646 FindFirstFileW (
647 LPCWSTR lpFileName,
648 LPWIN32_FIND_DATAW lpFindFileData
649 )
650 {
651 return FindFirstFileExW (lpFileName,
652 FindExInfoStandard,
653 (LPVOID)lpFindFileData,
654 FindExSearchNameMatch,
655 NULL,
656 0);
657 }
658
659 /*
660 * @implemented
661 */
662 BOOL
663 WINAPI
664 FindNextFileW (
665 HANDLE hFindFile,
666 LPWIN32_FIND_DATAW lpFindFileData
667 )
668 {
669 return InternalFindNextFile (hFindFile,
670 NULL,
671 lpFindFileData,
672 TRUE);
673 }
674
675
676 /*
677 * @unimplemented
678 */
679 HANDLE
680 WINAPI
681 FindFirstFileExW (LPCWSTR lpFileName,
682 FINDEX_INFO_LEVELS fInfoLevelId,
683 LPVOID lpFindFileData,
684 FINDEX_SEARCH_OPS fSearchOp,
685 LPVOID lpSearchFilter,
686 DWORD dwAdditionalFlags)
687 {
688 if (fInfoLevelId != FindExInfoStandard)
689 {
690 SetLastError(ERROR_INVALID_PARAMETER);
691 return INVALID_HANDLE_VALUE;
692 }
693
694 if (fSearchOp == FindExSearchNameMatch || fSearchOp == FindExSearchLimitToDirectories)
695 {
696 if (lpSearchFilter)
697 {
698 SetLastError(ERROR_INVALID_PARAMETER);
699 return INVALID_HANDLE_VALUE;
700 }
701
702 return InternalFindFirstFile (lpFileName,
703 fSearchOp == FindExSearchLimitToDirectories,
704 lpFindFileData,
705 TRUE);
706 }
707
708 SetLastError(ERROR_INVALID_PARAMETER);
709 return INVALID_HANDLE_VALUE;
710 }
711
712 /*
713 * @unimplemented
714 */
715 HANDLE
716 WINAPI
717 FindFirstFileExA (
718 LPCSTR lpFileName,
719 FINDEX_INFO_LEVELS fInfoLevelId,
720 LPVOID lpFindFileData,
721 FINDEX_SEARCH_OPS fSearchOp,
722 LPVOID lpSearchFilter,
723 DWORD dwAdditionalFlags
724 )
725 {
726 UNICODE_STRING FileNameU;
727 ANSI_STRING FileNameA;
728 HANDLE Handle;
729
730 if (fInfoLevelId != FindExInfoStandard)
731 {
732 SetLastError(ERROR_INVALID_PARAMETER);
733 return INVALID_HANDLE_VALUE;
734 }
735 if (fSearchOp == FindExSearchNameMatch || fSearchOp == FindExSearchLimitToDirectories)
736 {
737 if (lpSearchFilter)
738 {
739 SetLastError(ERROR_INVALID_PARAMETER);
740 return INVALID_HANDLE_VALUE;
741 }
742
743 RtlInitAnsiString (&FileNameA, (LPSTR)lpFileName);
744
745 /* convert ansi (or oem) string to unicode */
746 if (bIsFileApiAnsi)
747 RtlAnsiStringToUnicodeString (&FileNameU, &FileNameA, TRUE);
748 else
749 RtlOemStringToUnicodeString (&FileNameU, &FileNameA, TRUE);
750
751 Handle = InternalFindFirstFile (FileNameU.Buffer,
752 fSearchOp == FindExSearchLimitToDirectories,
753 lpFindFileData,
754 FALSE);
755
756 RtlFreeUnicodeString (&FileNameU);
757 return Handle;
758 }
759
760 SetLastError(ERROR_INVALID_PARAMETER);
761 return INVALID_HANDLE_VALUE;
762 }
763
764
765 static VOID
766 InternalCopyStreamInfo(IN OUT PKERNEL32_FIND_STREAM_DATA IData,
767 OUT LPVOID lpFindStreamData)
768 {
769 ASSERT(IData->pCurrent);
770
771 switch (IData->InfoLevel)
772 {
773 case FindStreamInfoStandard:
774 {
775 ULONG StreamNameLen;
776 WIN32_FIND_STREAM_DATA *StreamData = (WIN32_FIND_STREAM_DATA*)lpFindStreamData;
777
778 StreamNameLen = IData->pCurrent->StreamNameLength;
779 if (StreamNameLen > sizeof(StreamData->cStreamName) - sizeof(WCHAR))
780 StreamNameLen = sizeof(StreamData->cStreamName) - sizeof(WCHAR);
781
782 StreamData->StreamSize.QuadPart = IData->pCurrent->StreamSize.QuadPart;
783 RtlCopyMemory(StreamData->cStreamName,
784 IData->pCurrent->StreamName,
785 StreamNameLen);
786 StreamData->cStreamName[StreamNameLen / sizeof(WCHAR)] = L'\0';
787 break;
788 }
789
790 default:
791 ASSERT(FALSE);
792 break;
793 }
794 }
795
796
797 /*
798 * @implemented
799 */
800 HANDLE
801 WINAPI
802 FindFirstStreamW(IN LPCWSTR lpFileName,
803 IN STREAM_INFO_LEVELS InfoLevel,
804 OUT LPVOID lpFindStreamData,
805 IN DWORD dwFlags)
806 {
807 PKERNEL32_FIND_DATA_HEADER IHeader = NULL;
808 PKERNEL32_FIND_STREAM_DATA IData = NULL;
809 OBJECT_ATTRIBUTES ObjectAttributes;
810 IO_STATUS_BLOCK IoStatusBlock;
811 UNICODE_STRING NtPathU;
812 HANDLE FileHandle = NULL;
813 NTSTATUS Status;
814 ULONG BufferSize = 0;
815
816 if (dwFlags != 0 || InfoLevel != FindStreamInfoStandard ||
817 lpFindStreamData == NULL)
818 {
819 SetLastError(ERROR_INVALID_PARAMETER);
820 return INVALID_HANDLE_VALUE;
821 }
822
823 /* validate & translate the filename */
824 if (!RtlDosPathNameToNtPathName_U(lpFileName,
825 &NtPathU,
826 NULL,
827 NULL))
828 {
829 SetLastError(ERROR_PATH_NOT_FOUND);
830 return INVALID_HANDLE_VALUE;
831 }
832
833 /* open the file */
834 InitializeObjectAttributes(&ObjectAttributes,
835 &NtPathU,
836 OBJ_CASE_INSENSITIVE,
837 NULL,
838 NULL);
839
840 Status = NtCreateFile(&FileHandle,
841 0,
842 &ObjectAttributes,
843 &IoStatusBlock,
844 NULL,
845 0,
846 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
847 FILE_OPEN,
848 0,
849 NULL,
850 0);
851 if (!NT_SUCCESS(Status))
852 {
853 goto Cleanup;
854 }
855
856 /* create the search context */
857 IHeader = RtlAllocateHeap(hProcessHeap,
858 0,
859 sizeof(KERNEL32_FIND_DATA_HEADER) +
860 sizeof(KERNEL32_FIND_STREAM_DATA));
861 if (IHeader == NULL)
862 {
863 Status = STATUS_NO_MEMORY;
864 goto Cleanup;
865 }
866
867 IHeader->Type = StreamFind;
868 IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
869
870 /* capture all information about the streams */
871 IData->InfoLevel = InfoLevel;
872 IData->pCurrent = NULL;
873 IData->pFileStreamInfo = NULL;
874
875 do
876 {
877 BufferSize += 0x1000;
878
879 if (IData->pFileStreamInfo == NULL)
880 {
881 IData->pFileStreamInfo = RtlAllocateHeap(hProcessHeap,
882 0,
883 BufferSize);
884 if (IData->pFileStreamInfo == NULL)
885 {
886 Status = STATUS_NO_MEMORY;
887 break;
888 }
889 }
890 else
891 {
892 PFILE_STREAM_INFORMATION pfsi;
893
894 pfsi = RtlReAllocateHeap(hProcessHeap,
895 0,
896 IData->pFileStreamInfo,
897 BufferSize);
898 if (pfsi == NULL)
899 {
900 Status = STATUS_NO_MEMORY;
901 break;
902 }
903
904 IData->pFileStreamInfo = pfsi;
905 }
906
907 Status = NtQueryInformationFile(FileHandle,
908 &IoStatusBlock,
909 IData->pFileStreamInfo,
910 BufferSize,
911 FileStreamInformation);
912
913 } while (Status == STATUS_BUFFER_TOO_SMALL);
914
915 if (NT_SUCCESS(Status))
916 {
917 NtClose(FileHandle);
918 FileHandle = NULL;
919
920 /* select the first stream and return the information */
921 IData->pCurrent = IData->pFileStreamInfo;
922 InternalCopyStreamInfo(IData,
923 lpFindStreamData);
924
925 /* all done */
926 Status = STATUS_SUCCESS;
927 }
928
929 Cleanup:
930 if (FileHandle != NULL)
931 {
932 NtClose(FileHandle);
933 }
934
935 RtlFreeHeap(RtlGetProcessHeap(),
936 0,
937 NtPathU.Buffer);
938
939 if (!NT_SUCCESS(Status))
940 {
941 if (IHeader != NULL)
942 {
943 if (IData->pFileStreamInfo != NULL)
944 {
945 RtlFreeHeap(hProcessHeap,
946 0,
947 IData->pFileStreamInfo);
948 }
949
950 RtlFreeHeap(hProcessHeap,
951 0,
952 IHeader);
953 }
954
955 SetLastErrorByStatus(Status);
956 return INVALID_HANDLE_VALUE;
957 }
958
959 return (HANDLE)IHeader;
960 }
961
962
963 /*
964 * @implemented
965 */
966 BOOL
967 WINAPI
968 FindNextStreamW(IN HANDLE hFindStream,
969 OUT LPVOID lpFindStreamData)
970 {
971 PKERNEL32_FIND_DATA_HEADER IHeader;
972 PKERNEL32_FIND_STREAM_DATA IData;
973
974 IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindStream;
975 if (hFindStream == NULL || hFindStream == INVALID_HANDLE_VALUE ||
976 IHeader->Type != StreamFind)
977 {
978 SetLastError (ERROR_INVALID_HANDLE);
979 return FALSE;
980 }
981
982 IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
983
984 /* select next stream if possible */
985 if (IData->pCurrent->NextEntryOffset != 0)
986 {
987 IData->pCurrent = (PFILE_STREAM_INFORMATION)((ULONG_PTR)IData->pFileStreamInfo +
988 IData->pCurrent->NextEntryOffset);
989 }
990 else
991 {
992 SetLastError(ERROR_HANDLE_EOF);
993 return FALSE;
994 }
995
996 /* return the information */
997 InternalCopyStreamInfo(IData,
998 lpFindStreamData);
999
1000 return TRUE;
1001 }
1002
1003
1004 /* EOF */