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