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)
11 /* INCLUDES *******************************************************************/
16 DEBUG_CHANNEL(kernel32file
);
19 /* TYPES **********************************************************************/
21 #define FIND_DATA_SIZE 0x4000
22 #define FIND_DEVICE_HANDLE ((HANDLE)0x1)
24 typedef enum _FIND_DATA_TYPE
31 * FILE_FULL_DIR_INFORMATION and FILE_BOTH_DIR_INFORMATION structures layout.
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];
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).
59 typedef union _DIR_INFORMATION
62 PFILE_FULL_DIR_INFORMATION FullDirInfo
;
63 PFILE_BOTH_DIR_INFORMATION BothDirInfo
;
66 typedef struct _FIND_FILE_DATA
69 FINDEX_INFO_LEVELS InfoLevel
;
70 FINDEX_SEARCH_OPS SearchOp
;
73 * For handling STATUS_BUFFER_OVERFLOW errors emitted by
74 * NtQueryDirectoryFile in the FildNextFile function.
79 * "Pointer" to the next file info structure in the buffer.
80 * The type is defined by the 'InfoLevel' parameter.
82 DIR_INFORMATION NextDirInfo
;
84 BYTE Buffer
[FIND_DATA_SIZE
];
85 } FIND_FILE_DATA
, *PFIND_FILE_DATA
;
87 typedef struct _FIND_STREAM_DATA
89 STREAM_INFO_LEVELS InfoLevel
;
90 PFILE_STREAM_INFORMATION FileStreamInfo
;
91 PFILE_STREAM_INFORMATION CurrentInfo
;
92 } FIND_STREAM_DATA
, *PFIND_STREAM_DATA
;
94 typedef struct _FIND_DATA_HANDLE
97 RTL_CRITICAL_SECTION Lock
;
100 * Pointer to the following finding data, located at
101 * (this + 1). The type is defined by the 'Type' parameter.
105 PFIND_FILE_DATA FindFileData
;
106 PFIND_STREAM_DATA FindStreamData
;
109 } FIND_DATA_HANDLE
, *PFIND_DATA_HANDLE
;
112 /* PRIVATE FUNCTIONS **********************************************************/
115 CopyDeviceFindData(OUT LPWIN32_FIND_DATAW lpFindFileData
,
116 IN LPCWSTR lpFileName
,
117 IN ULONG DeviceNameInfo
)
119 UNICODE_STRING DeviceName
;
123 /* DeviceNameInfo == { USHORT Offset; USHORT Length } */
124 DeviceName
.Length
= DeviceName
.MaximumLength
= (USHORT
)(DeviceNameInfo
& 0xFFFF);
125 DeviceName
.Buffer
= (LPWSTR
)((ULONG_PTR
)lpFileName
+ ((DeviceNameInfo
>> 16) & 0xFFFF));
127 /* Return the data */
128 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
129 lpFindFileData
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
130 RtlCopyMemory(lpFindFileData
->cFileName
,
134 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
143 CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData
,
144 IN FINDEX_INFO_LEVELS fInfoLevelId
,
145 IN DIR_INFORMATION DirInfo
)
147 #define ULARGE_INTEGER_2_FILETIME(ft, ul) \
149 (ft).dwHighDateTime = (ul).u.HighPart; \
150 (ft).dwLowDateTime = (ul).u.LowPart ; \
155 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
157 lpFindFileData
->dwFileAttributes
= DirInfo
.FullDirInfo
->FileAttributes
;
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
);
163 lpFindFileData
->nFileSizeHigh
= DirInfo
.FullDirInfo
->EndOfFile
.u
.HighPart
;
164 lpFindFileData
->nFileSizeLow
= DirInfo
.FullDirInfo
->EndOfFile
.u
.LowPart
;
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
;
170 lpFindFileData
->dwReserved0
= 0;
172 /* Unused dwReserved1 field */
173 lpFindFileData
->dwReserved1
= 0;
175 if (fInfoLevelId
== FindExInfoStandard
)
177 RtlCopyMemory(lpFindFileData
->cFileName
,
178 DirInfo
.BothDirInfo
->FileName
,
179 DirInfo
.BothDirInfo
->FileNameLength
);
180 lpFindFileData
->cFileName
[DirInfo
.BothDirInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
182 RtlCopyMemory(lpFindFileData
->cAlternateFileName
,
183 DirInfo
.BothDirInfo
->ShortName
,
184 DirInfo
.BothDirInfo
->ShortNameLength
);
185 lpFindFileData
->cAlternateFileName
[DirInfo
.BothDirInfo
->ShortNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
187 else if (fInfoLevelId
== FindExInfoBasic
)
189 RtlCopyMemory(lpFindFileData
->cFileName
,
190 DirInfo
.FullDirInfo
->FileName
,
191 DirInfo
.FullDirInfo
->FileNameLength
);
192 lpFindFileData
->cFileName
[DirInfo
.FullDirInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
194 lpFindFileData
->cAlternateFileName
[0] = UNICODE_NULL
;
198 /* Invalid InfoLevelId */
202 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
211 CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData
,
212 OUT PWIN32_FIND_STREAM_DATA lpFindStreamData
)
216 ASSERT(FindStreamData
->CurrentInfo
);
218 switch (FindStreamData
->InfoLevel
)
220 case FindStreamInfoStandard
:
222 ULONG StreamNameLen
= min(FindStreamData
->CurrentInfo
->StreamNameLength
,
223 sizeof(lpFindStreamData
->cStreamName
) - sizeof(WCHAR
));
225 RtlZeroMemory(lpFindStreamData
, sizeof(*lpFindStreamData
));
227 lpFindStreamData
->StreamSize
.QuadPart
= FindStreamData
->CurrentInfo
->StreamSize
.QuadPart
;
228 RtlCopyMemory(lpFindStreamData
->cStreamName
,
229 FindStreamData
->CurrentInfo
->StreamName
,
231 lpFindStreamData
->cStreamName
[StreamNameLen
/ sizeof(WCHAR
)] = UNICODE_NULL
;
238 /* Invalid InfoLevel */
244 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
253 /* PUBLIC FUNCTIONS ***********************************************************/
260 FindFirstFileA(IN LPCSTR lpFileName
,
261 OUT LPWIN32_FIND_DATAA lpFindFileData
)
267 PUNICODE_STRING lpFileNameW
;
268 WIN32_FIND_DATAW FindFileDataW
;
270 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
271 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
273 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
276 FindExSearchNameMatch
,
278 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
280 RtlCopyMemory(lpFindFileData
,
282 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
284 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
285 Ansi
.Buffer
= lpFindFileData
->cFileName
;
287 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
288 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
289 if (!NT_SUCCESS(Status
))
292 BaseSetLastNTError(Status
);
293 return INVALID_HANDLE_VALUE
;
296 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
297 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
299 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
300 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
301 if (!NT_SUCCESS(Status
))
304 BaseSetLastNTError(Status
);
305 return INVALID_HANDLE_VALUE
;
317 FindFirstFileW(IN LPCWSTR lpFileName
,
318 OUT LPWIN32_FIND_DATAW lpFindFileData
)
320 return FindFirstFileExW(lpFileName
,
323 FindExSearchNameMatch
,
333 FindNextFileA(IN HANDLE hFindFile
,
334 OUT LPWIN32_FIND_DATAA lpFindFileData
)
339 WIN32_FIND_DATAW FindFileDataW
;
341 if (!FindNextFileW(hFindFile
, &FindFileDataW
))
344 RtlCopyMemory(lpFindFileData
,
346 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
348 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
349 Ansi
.Buffer
= lpFindFileData
->cFileName
;
351 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
352 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
353 if (!NT_SUCCESS(Status
))
355 BaseSetLastNTError(Status
);
359 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
360 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
362 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
363 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
364 if (!NT_SUCCESS(Status
))
366 BaseSetLastNTError(Status
);
379 FindNextFileW(IN HANDLE hFindFile
,
380 OUT LPWIN32_FIND_DATAW lpFindFileData
)
382 NTSTATUS Status
= STATUS_SUCCESS
;
383 DIR_INFORMATION FoundFile
= {NULL
};
385 TRACE("FindNextFileW(%lx, 0x%p)\n", hFindFile
, lpFindFileData
);
387 if (hFindFile
!= FIND_DEVICE_HANDLE
)
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
};
395 if (hFindFile
== NULL
|| hFindFile
== INVALID_HANDLE_VALUE
||
396 FindDataHandle
->Type
!= FindFile
)
398 SetLastError(ERROR_INVALID_HANDLE
);
402 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
404 FindFileData
= FindDataHandle
->u
.FindFileData
;
405 InfoLevel
= FindFileData
->InfoLevel
;
409 if (FindFileData
->NextDirInfo
.DirInfo
== NULL
)
411 Status
= NtQueryDirectoryFile(FindFileData
->Handle
,
414 &FindFileData
->Buffer
,
415 sizeof(FindFileData
->Buffer
),
416 (InfoLevel
== FindExInfoStandard
417 ? FileBothDirectoryInformation
418 : FileFullDirectoryInformation
),
420 NULL
, /* Use the file pattern from the first call */
422 if (Status
== STATUS_BUFFER_OVERFLOW
)
424 FindFileData
->HasMoreData
= TRUE
;
425 Status
= STATUS_SUCCESS
;
429 if (!NT_SUCCESS(Status
)) break;
430 FindFileData
->HasMoreData
= FALSE
;
433 FindFileData
->NextDirInfo
.DirInfo
= &FindFileData
->Buffer
;
436 DirInfo
= FindFileData
->NextDirInfo
;
438 if (DirInfo
.FullDirInfo
->NextEntryOffset
!= 0)
440 ULONG_PTR BufferEnd
= (ULONG_PTR
)&FindFileData
->Buffer
+ sizeof(FindFileData
->Buffer
);
443 NextDirInfo
.DirInfo
= FindFileData
->NextDirInfo
.DirInfo
=
444 (PVOID
)((ULONG_PTR
)DirInfo
.DirInfo
+ DirInfo
.FullDirInfo
->NextEntryOffset
);
446 pFileName
= (InfoLevel
== FindExInfoStandard
447 ? NextDirInfo
.BothDirInfo
->FileName
448 : NextDirInfo
.FullDirInfo
->FileName
);
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
))
455 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
460 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
463 if ((FindFileData
->SearchOp
!= FindExSearchLimitToDirectories
) ||
464 (DirInfo
.FullDirInfo
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
468 } while ( FoundFile
.DirInfo
== NULL
&& (FindFileData
->NextDirInfo
.DirInfo
|| FindFileData
->HasMoreData
) );
470 if (FoundFile
.DirInfo
!= NULL
)
472 /* Return the information */
473 CopyFindData(lpFindFileData
, InfoLevel
, FoundFile
);
476 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
479 if (!NT_SUCCESS(Status
))
481 BaseSetLastNTError(Status
);
484 else if (FoundFile
.DirInfo
== NULL
)
486 SetLastError(ERROR_NO_MORE_FILES
);
499 FindClose(HANDLE hFindFile
)
501 TRACE("FindClose(hFindFile %x)\n", hFindFile
);
503 if (hFindFile
== FIND_DEVICE_HANDLE
)
506 if (!hFindFile
|| hFindFile
== INVALID_HANDLE_VALUE
)
508 SetLastError(ERROR_INVALID_HANDLE
);
512 /* Protect with SEH against closing attempts on invalid handles. */
515 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindFile
;
517 switch (FindDataHandle
->Type
)
521 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
522 NtClose(FindDataHandle
->u
.FindFileData
->Handle
);
523 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
524 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
530 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
531 if (FindDataHandle
->u
.FindStreamData
->FileStreamInfo
!= NULL
)
533 RtlFreeHeap(RtlGetProcessHeap(), 0,
534 FindDataHandle
->u
.FindStreamData
->FileStreamInfo
);
536 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
537 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
543 SetLastError(ERROR_INVALID_HANDLE
);
544 _SEH2_YIELD(return FALSE
);
548 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
549 _SEH2_YIELD(return TRUE
);
551 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
553 BaseSetLastNTError(_SEH2_GetExceptionCode());
554 _SEH2_YIELD(return FALSE
);
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
)
576 PUNICODE_STRING lpFileNameW
;
577 WIN32_FIND_DATAW FindFileDataW
;
578 LPWIN32_FIND_DATAA lpFindFileDataA
= (LPWIN32_FIND_DATAA
)lpFindFileData
;
580 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
581 fSearchOp
== FindExSearchLimitToDevices
||
582 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
584 SetLastError(fSearchOp
== FindExSearchLimitToDevices
585 ? ERROR_NOT_SUPPORTED
586 : ERROR_INVALID_PARAMETER
);
587 return INVALID_HANDLE_VALUE
;
590 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
591 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
593 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
599 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
601 RtlCopyMemory(lpFindFileDataA
,
603 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
605 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
606 Ansi
.Buffer
= lpFindFileDataA
->cFileName
;
608 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cFileName
);
609 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
610 if (!NT_SUCCESS(Status
))
613 BaseSetLastNTError(Status
);
614 return INVALID_HANDLE_VALUE
;
617 if (fInfoLevelId
!= FindExInfoBasic
)
619 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
620 Ansi
.Buffer
= lpFindFileDataA
->cAlternateFileName
;
622 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cAlternateFileName
);
623 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
624 if (!NT_SUCCESS(Status
))
627 BaseSetLastNTError(Status
);
628 return INVALID_HANDLE_VALUE
;
633 lpFindFileDataA
->cAlternateFileName
[0] = ANSI_NULL
;
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
)
652 TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName
);
654 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
655 fSearchOp
== FindExSearchLimitToDevices
||
656 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
658 SetLastError(fSearchOp
== FindExSearchLimitToDevices
659 ? ERROR_NOT_SUPPORTED
660 : ERROR_INVALID_PARAMETER
);
661 return INVALID_HANDLE_VALUE
;
664 if (fSearchOp
== FindExSearchNameMatch
||
665 fSearchOp
== FindExSearchLimitToDirectories
)
667 LPWIN32_FIND_DATAW Win32FindData
= (LPWIN32_FIND_DATAW
)lpFindFileData
;
668 PFIND_DATA_HANDLE FindDataHandle
;
669 PFIND_FILE_DATA FindFileData
;
671 UNICODE_STRING NtPath
, FilePattern
;
673 RTL_RELATIVE_NAME_U RelativePath
;
674 ULONG DeviceNameInfo
= 0;
677 OBJECT_ATTRIBUTES ObjectAttributes
;
678 IO_STATUS_BLOCK IoStatusBlock
;
679 HANDLE hDirectory
= NULL
;
682 * May represent many FILE_BOTH_DIR_INFORMATION
683 * or many FILE_FULL_DIR_INFORMATION structures.
685 BYTE DirectoryInfo
[FIND_DATA_SIZE
];
686 DIR_INFORMATION DirInfo
= {&DirectoryInfo
};
688 /* The search filter is always unused */
691 SetLastError(ERROR_INVALID_PARAMETER
);
692 return INVALID_HANDLE_VALUE
;
695 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
697 (PCWSTR
*)&FilePattern
.Buffer
,
700 SetLastError(ERROR_PATH_NOT_FOUND
);
701 return INVALID_HANDLE_VALUE
;
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
);
710 /* Save the buffer pointer for later, we need to free it! */
711 NtPathBuffer
= NtPath
.Buffer
;
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.
718 if (!FilePattern
.Buffer
|| !*FilePattern
.Buffer
)
720 /* No file pattern specified, or DOS device */
722 DeviceNameInfo
= RtlIsDosDeviceName_U(lpFileName
);
723 if (DeviceNameInfo
!= 0)
725 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
727 /* OK, it's really a DOS device */
728 CopyDeviceFindData(Win32FindData
, lpFileName
, DeviceNameInfo
);
729 return FIND_DEVICE_HANDLE
;
733 /* A file pattern was specified, or it was not a DOS device */
735 /* If there is a file pattern then determine its length */
736 if (FilePattern
.Buffer
!= NULL
)
738 FilePattern
.Length
= NtPath
.Length
-
739 (USHORT
)((ULONG_PTR
)FilePattern
.Buffer
- (ULONG_PTR
)NtPath
.Buffer
);
743 FilePattern
.Length
= 0;
745 FilePattern
.MaximumLength
= FilePattern
.Length
;
747 if (RelativePath
.RelativeName
.Length
!= 0 &&
748 RelativePath
.RelativeName
.Buffer
!= FilePattern
.Buffer
)
750 if (FilePattern
.Buffer
!= NULL
)
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
;
760 /* This is an absolute path, NtPath receives the full path */
761 RelativePath
.ContainingDirectory
= NULL
;
762 if (FilePattern
.Buffer
!= NULL
)
764 NtPath
.Length
= NtPath
.MaximumLength
=
765 (USHORT
)((ULONG_PTR
)FilePattern
.Buffer
- (ULONG_PTR
)NtPath
.Buffer
);
769 DPRINT("NtPath - After = '%wZ'\n", &NtPath
);
770 DPRINT("FilePattern = '%wZ'\n", &FilePattern
);
771 DPRINT("RelativeTo = 0x%p\n", RelativePath
.ContainingDirectory
);
773 InitializeObjectAttributes(&ObjectAttributes
,
775 (dwAdditionalFlags
& FIND_FIRST_EX_CASE_SENSITIVE
) ? 0 : OBJ_CASE_INSENSITIVE
,
776 RelativePath
.ContainingDirectory
,
779 Status
= NtOpenFile(&hDirectory
,
780 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
783 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
784 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
786 if (!NT_SUCCESS(Status
))
788 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
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
;
796 BaseSetLastNTError(Status
);
797 return INVALID_HANDLE_VALUE
;
801 * Fail if there is not any file pattern,
802 * since we are not looking for a device.
804 if (FilePattern
.Length
== 0)
807 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
809 SetLastError(ERROR_FILE_NOT_FOUND
);
810 return INVALID_HANDLE_VALUE
;
813 /* Change pattern: "*.*" --> "*" */
814 if (FilePattern
.Length
== 6 &&
815 RtlCompareMemory(FilePattern
.Buffer
, L
"*.*", 6) == 6)
817 FilePattern
.Length
= 2;
820 Status
= NtQueryDirectoryFile(hDirectory
,
823 DirInfo
.DirInfo
, // == &DirectoryInfo
824 sizeof(DirectoryInfo
),
825 (fInfoLevelId
== FindExInfoStandard
826 ? FileBothDirectoryInformation
827 : FileFullDirectoryInformation
),
828 TRUE
, /* Return a single entry */
832 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
834 if (!NT_SUCCESS(Status
))
837 BaseSetLastNTError(Status
);
838 return INVALID_HANDLE_VALUE
;
841 ASSERT(DirInfo
.FullDirInfo
->NextEntryOffset
== 0);
843 /* Return the information */
844 CopyFindData(Win32FindData
, fInfoLevelId
, DirInfo
);
847 * Initialization of the search handle.
849 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
851 sizeof(FIND_DATA_HANDLE
) +
852 sizeof(FIND_FILE_DATA
));
856 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
857 return INVALID_HANDLE_VALUE
;
860 FindDataHandle
->Type
= FindFile
;
861 FindDataHandle
->u
.FindFileData
= (PFIND_FILE_DATA
)(FindDataHandle
+ 1);
862 FindFileData
= FindDataHandle
->u
.FindFileData
;
864 FindFileData
->Handle
= hDirectory
;
865 FindFileData
->InfoLevel
= fInfoLevelId
;
866 FindFileData
->SearchOp
= fSearchOp
;
867 FindFileData
->HasMoreData
= FALSE
;
868 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
870 /* The critical section must always be initialized */
871 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
872 if (!NT_SUCCESS(Status
))
875 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
877 BaseSetLastNTError(Status
);
878 return INVALID_HANDLE_VALUE
;
881 return (HANDLE
)FindDataHandle
;
885 SetLastError(ERROR_NOT_SUPPORTED
);
886 return INVALID_HANDLE_VALUE
;
896 FindFirstStreamW(IN LPCWSTR lpFileName
,
897 IN STREAM_INFO_LEVELS InfoLevel
,
898 OUT LPVOID lpFindStreamData
,
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
;
908 ULONG BufferSize
= 0;
910 if (dwFlags
!= 0 || InfoLevel
!= FindStreamInfoStandard
||
911 lpFindStreamData
== NULL
)
913 SetLastError(ERROR_INVALID_PARAMETER
);
914 return INVALID_HANDLE_VALUE
;
917 /* Validate and translate the filename */
918 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
922 SetLastError(ERROR_PATH_NOT_FOUND
);
923 return INVALID_HANDLE_VALUE
;
927 InitializeObjectAttributes(&ObjectAttributes
,
929 OBJ_CASE_INSENSITIVE
,
933 Status
= NtCreateFile(&FileHandle
,
938 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
941 if (!NT_SUCCESS(Status
)) goto Cleanup
;
944 * Initialization of the search handle.
946 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
948 sizeof(FIND_DATA_HANDLE
) +
949 sizeof(FIND_STREAM_DATA
));
952 Status
= STATUS_NO_MEMORY
;
956 FindDataHandle
->Type
= FindStream
;
957 FindDataHandle
->u
.FindStreamData
= (PFIND_STREAM_DATA
)(FindDataHandle
+ 1);
958 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
960 FindStreamData
->InfoLevel
= InfoLevel
;
961 FindStreamData
->FileStreamInfo
= NULL
;
962 FindStreamData
->CurrentInfo
= NULL
;
964 /* The critical section must always be initialized */
965 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
966 if (!NT_SUCCESS(Status
))
968 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
972 /* Capture all information about the streams */
975 BufferSize
+= 0x1000;
977 if (FindStreamData
->FileStreamInfo
== NULL
)
979 FindStreamData
->FileStreamInfo
= RtlAllocateHeap(RtlGetProcessHeap(),
982 if (FindStreamData
->FileStreamInfo
== NULL
)
984 Status
= STATUS_NO_MEMORY
;
990 PFILE_STREAM_INFORMATION pfsi
;
992 pfsi
= RtlReAllocateHeap(RtlGetProcessHeap(),
993 0, // HEAP_ZERO_MEMORY,
994 FindStreamData
->FileStreamInfo
,
998 Status
= STATUS_NO_MEMORY
;
1002 FindStreamData
->FileStreamInfo
= pfsi
;
1005 Status
= NtQueryInformationFile(FileHandle
,
1007 FindStreamData
->FileStreamInfo
,
1009 FileStreamInformation
);
1011 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
1013 if (NT_SUCCESS(Status
))
1015 /* Select the first stream and return the information */
1016 FindStreamData
->CurrentInfo
= FindStreamData
->FileStreamInfo
;
1017 CopyStreamData(FindStreamData
, lpFindStreamData
);
1020 Status
= STATUS_SUCCESS
;
1024 if (FindStreamData
->FileStreamInfo
)
1026 RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData
->FileStreamInfo
);
1029 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
1033 if (FileHandle
) NtClose(FileHandle
);
1035 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath
.Buffer
);
1037 if (NT_SUCCESS(Status
))
1039 return (HANDLE
)FindDataHandle
;
1043 BaseSetLastNTError(Status
);
1044 return INVALID_HANDLE_VALUE
;
1054 FindNextStreamW(IN HANDLE hFindStream
,
1055 OUT LPVOID lpFindStreamData
)
1057 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindStream
;
1058 PFIND_STREAM_DATA FindStreamData
;
1060 if (hFindStream
== NULL
|| hFindStream
== INVALID_HANDLE_VALUE
||
1061 FindDataHandle
->Type
!= FindStream
)
1063 SetLastError(ERROR_INVALID_HANDLE
);
1067 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
1069 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
1071 /* Select next stream if possible */
1072 if (FindStreamData
->CurrentInfo
->NextEntryOffset
!= 0)
1074 FindStreamData
->CurrentInfo
= (PFILE_STREAM_INFORMATION
)((ULONG_PTR
)FindStreamData
->FileStreamInfo
+
1075 FindStreamData
->CurrentInfo
->NextEntryOffset
);
1077 /* Return the information */
1078 CopyStreamData(FindStreamData
, lpFindStreamData
);
1080 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1085 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1087 SetLastError(ERROR_HANDLE_EOF
);