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