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)
11 /* INCLUDES *******************************************************************/
14 #include <ntstrsafe.h>
18 DEBUG_CHANNEL(kernel32file
);
21 /* TYPES **********************************************************************/
23 #define FIND_DATA_SIZE 0x4000
24 #define FIND_DEVICE_HANDLE ((HANDLE)0x1)
26 typedef enum _FIND_DATA_TYPE
33 * FILE_FULL_DIR_INFORMATION and FILE_BOTH_DIR_INFORMATION structures layout.
36 * struct FILE_FULL_DIR_INFORMATION | struct FILE_BOTH_DIR_INFORMATION
37 * ------------------------------------+---------------------------------------
38 * ULONG NextEntryOffset; | ULONG NextEntryOffset;
39 * ULONG FileIndex; | ULONG FileIndex;
40 * LARGE_INTEGER CreationTime; | LARGE_INTEGER CreationTime;
41 * LARGE_INTEGER LastAccessTime; | LARGE_INTEGER LastAccessTime;
42 * LARGE_INTEGER LastWriteTime; | LARGE_INTEGER LastWriteTime;
43 * LARGE_INTEGER ChangeTime; | LARGE_INTEGER ChangeTime;
44 * LARGE_INTEGER EndOfFile; | LARGE_INTEGER EndOfFile;
45 * LARGE_INTEGER AllocationSize; | LARGE_INTEGER AllocationSize;
46 * ULONG FileAttributes; | ULONG FileAttributes;
47 * ULONG FileNameLength; | ULONG FileNameLength;
48 * ULONG EaSize; | ULONG EaSize;
49 * ------------------------------------+---------------------------------------
50 * WCHAR FileName[1]; | CCHAR ShortNameLength;
51 * | WCHAR ShortName[12];
52 * | WCHAR FileName[1];
54 * Therefore we can use pointers to FILE_FULL_DIR_INFORMATION when one doesn't
55 * want to refer to the ShortName* fields and FileName (useful for implementing
56 * the FindExInfoBasic functionality for FindFirstFileEx), however a cast to
57 * FILE_BOTH_DIR_INFORMATION is required when one wants to use FileName and
58 * ShortName* fields (needed for the FindExInfoStandard functionality).
61 typedef union _DIR_INFORMATION
64 PFILE_FULL_DIR_INFORMATION FullDirInfo
;
65 PFILE_BOTH_DIR_INFORMATION BothDirInfo
;
68 typedef struct _FIND_FILE_DATA
71 FINDEX_INFO_LEVELS InfoLevel
;
72 FINDEX_SEARCH_OPS SearchOp
;
75 * For handling STATUS_BUFFER_OVERFLOW errors emitted by
76 * NtQueryDirectoryFile in the FindNextFile function.
81 * "Pointer" to the next file info structure in the buffer.
82 * The type is defined by the 'InfoLevel' parameter.
84 DIR_INFORMATION NextDirInfo
;
86 BYTE Buffer
[FIND_DATA_SIZE
];
87 } FIND_FILE_DATA
, *PFIND_FILE_DATA
;
89 typedef struct _FIND_STREAM_DATA
91 STREAM_INFO_LEVELS InfoLevel
;
92 PFILE_STREAM_INFORMATION FileStreamInfo
;
93 PFILE_STREAM_INFORMATION CurrentInfo
;
94 } FIND_STREAM_DATA
, *PFIND_STREAM_DATA
;
96 typedef struct _FIND_DATA_HANDLE
99 RTL_CRITICAL_SECTION Lock
;
102 * Pointer to the following finding data, located at
103 * (this + 1). The type is defined by the 'Type' parameter.
107 PFIND_FILE_DATA FindFileData
;
108 PFIND_STREAM_DATA FindStreamData
;
111 } FIND_DATA_HANDLE
, *PFIND_DATA_HANDLE
;
114 /* PRIVATE FUNCTIONS **********************************************************/
117 CopyDeviceFindData(OUT LPWIN32_FIND_DATAW lpFindFileData
,
118 IN LPCWSTR lpFileName
,
119 IN ULONG DeviceNameInfo
)
126 /* DeviceNameInfo == { USHORT Offset; USHORT Length } */
127 Length
= (SIZE_T
)(DeviceNameInfo
& 0xFFFF);
128 DeviceName
= (LPCWSTR
)((ULONG_PTR
)lpFileName
+ ((DeviceNameInfo
>> 16) & 0xFFFF));
130 /* Return the data */
131 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
132 lpFindFileData
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
133 RtlStringCbCopyNW(lpFindFileData
->cFileName
,
134 sizeof(lpFindFileData
->cFileName
),
137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
146 CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData
,
147 IN FINDEX_INFO_LEVELS fInfoLevelId
,
148 IN DIR_INFORMATION DirInfo
)
150 #define ULARGE_INTEGER_2_FILETIME(ft, ul) \
152 (ft).dwHighDateTime = (ul).u.HighPart; \
153 (ft).dwLowDateTime = (ul).u.LowPart ; \
158 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
160 lpFindFileData
->dwFileAttributes
= DirInfo
.FullDirInfo
->FileAttributes
;
162 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftCreationTime
, DirInfo
.FullDirInfo
->CreationTime
);
163 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftLastAccessTime
, DirInfo
.FullDirInfo
->LastAccessTime
);
164 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftLastWriteTime
, DirInfo
.FullDirInfo
->LastWriteTime
);
166 lpFindFileData
->nFileSizeHigh
= DirInfo
.FullDirInfo
->EndOfFile
.u
.HighPart
;
167 lpFindFileData
->nFileSizeLow
= DirInfo
.FullDirInfo
->EndOfFile
.u
.LowPart
;
169 /* dwReserved0 contains the NTFS reparse point tag, if any. */
170 if (DirInfo
.FullDirInfo
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
171 lpFindFileData
->dwReserved0
= DirInfo
.FullDirInfo
->EaSize
;
173 lpFindFileData
->dwReserved0
= 0;
175 /* Unused dwReserved1 field */
176 lpFindFileData
->dwReserved1
= 0;
178 if (fInfoLevelId
== FindExInfoStandard
)
180 RtlStringCbCopyNW(lpFindFileData
->cFileName
,
181 sizeof(lpFindFileData
->cFileName
),
182 DirInfo
.BothDirInfo
->FileName
,
183 DirInfo
.BothDirInfo
->FileNameLength
);
185 RtlStringCbCopyNW(lpFindFileData
->cAlternateFileName
,
186 sizeof(lpFindFileData
->cAlternateFileName
),
187 DirInfo
.BothDirInfo
->ShortName
,
188 DirInfo
.BothDirInfo
->ShortNameLength
);
190 else if (fInfoLevelId
== FindExInfoBasic
)
192 RtlStringCbCopyNW(lpFindFileData
->cFileName
,
193 sizeof(lpFindFileData
->cFileName
),
194 DirInfo
.FullDirInfo
->FileName
,
195 DirInfo
.FullDirInfo
->FileNameLength
);
197 lpFindFileData
->cAlternateFileName
[0] = UNICODE_NULL
;
201 /* Invalid InfoLevelId */
205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
214 CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData
,
215 OUT PWIN32_FIND_STREAM_DATA lpFindStreamData
)
219 ASSERT(FindStreamData
->CurrentInfo
);
221 switch (FindStreamData
->InfoLevel
)
223 case FindStreamInfoStandard
:
225 ULONG StreamNameLen
= min(FindStreamData
->CurrentInfo
->StreamNameLength
,
226 sizeof(lpFindStreamData
->cStreamName
) - sizeof(WCHAR
));
228 RtlZeroMemory(lpFindStreamData
, sizeof(*lpFindStreamData
));
230 lpFindStreamData
->StreamSize
.QuadPart
= FindStreamData
->CurrentInfo
->StreamSize
.QuadPart
;
231 RtlCopyMemory(lpFindStreamData
->cStreamName
,
232 FindStreamData
->CurrentInfo
->StreamName
,
234 lpFindStreamData
->cStreamName
[StreamNameLen
/ sizeof(WCHAR
)] = UNICODE_NULL
;
241 /* Invalid InfoLevel */
247 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
256 /* PUBLIC FUNCTIONS ***********************************************************/
263 FindFirstFileA(IN LPCSTR lpFileName
,
264 OUT LPWIN32_FIND_DATAA lpFindFileData
)
270 PUNICODE_STRING lpFileNameW
;
271 WIN32_FIND_DATAW FindFileDataW
;
273 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
274 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
276 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
279 FindExSearchNameMatch
,
281 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
283 RtlCopyMemory(lpFindFileData
,
285 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
287 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
288 Ansi
.Buffer
= lpFindFileData
->cFileName
;
290 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
291 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
292 if (!NT_SUCCESS(Status
))
295 BaseSetLastNTError(Status
);
296 return INVALID_HANDLE_VALUE
;
299 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
300 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
302 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
303 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
304 if (!NT_SUCCESS(Status
))
307 BaseSetLastNTError(Status
);
308 return INVALID_HANDLE_VALUE
;
320 FindFirstFileW(IN LPCWSTR lpFileName
,
321 OUT LPWIN32_FIND_DATAW lpFindFileData
)
323 return FindFirstFileExW(lpFileName
,
326 FindExSearchNameMatch
,
336 FindNextFileA(IN HANDLE hFindFile
,
337 OUT LPWIN32_FIND_DATAA lpFindFileData
)
342 WIN32_FIND_DATAW FindFileDataW
;
344 if (!FindNextFileW(hFindFile
, &FindFileDataW
))
347 RtlCopyMemory(lpFindFileData
,
349 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
351 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
352 Ansi
.Buffer
= lpFindFileData
->cFileName
;
354 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
355 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
356 if (!NT_SUCCESS(Status
))
358 BaseSetLastNTError(Status
);
362 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
363 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
365 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
366 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
367 if (!NT_SUCCESS(Status
))
369 BaseSetLastNTError(Status
);
382 FindNextFileW(IN HANDLE hFindFile
,
383 OUT LPWIN32_FIND_DATAW lpFindFileData
)
385 NTSTATUS Status
= STATUS_SUCCESS
;
386 DIR_INFORMATION FoundFile
= {NULL
};
388 TRACE("FindNextFileW(%p, 0x%p)\n", hFindFile
, lpFindFileData
);
390 if (hFindFile
!= FIND_DEVICE_HANDLE
)
392 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindFile
;
393 PFIND_FILE_DATA FindFileData
;
394 FINDEX_INFO_LEVELS InfoLevel
;
395 IO_STATUS_BLOCK IoStatusBlock
;
396 DIR_INFORMATION DirInfo
= {NULL
}, NextDirInfo
= {NULL
};
398 if (hFindFile
== NULL
|| hFindFile
== INVALID_HANDLE_VALUE
||
399 FindDataHandle
->Type
!= FindFile
)
401 SetLastError(ERROR_INVALID_HANDLE
);
405 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
407 FindFileData
= FindDataHandle
->u
.FindFileData
;
408 InfoLevel
= FindFileData
->InfoLevel
;
412 if (FindFileData
->NextDirInfo
.DirInfo
== NULL
)
414 Status
= NtQueryDirectoryFile(FindFileData
->Handle
,
417 &FindFileData
->Buffer
,
418 sizeof(FindFileData
->Buffer
),
419 (InfoLevel
== FindExInfoStandard
420 ? FileBothDirectoryInformation
421 : FileFullDirectoryInformation
),
423 NULL
, /* Use the file pattern from the first call */
425 if (Status
== STATUS_BUFFER_OVERFLOW
)
427 FindFileData
->HasMoreData
= TRUE
;
428 Status
= STATUS_SUCCESS
;
432 if (!NT_SUCCESS(Status
)) break;
433 FindFileData
->HasMoreData
= FALSE
;
436 FindFileData
->NextDirInfo
.DirInfo
= &FindFileData
->Buffer
;
439 DirInfo
= FindFileData
->NextDirInfo
;
441 if (DirInfo
.FullDirInfo
->NextEntryOffset
!= 0)
443 ULONG_PTR BufferEnd
= (ULONG_PTR
)&FindFileData
->Buffer
+ sizeof(FindFileData
->Buffer
);
446 NextDirInfo
.DirInfo
= FindFileData
->NextDirInfo
.DirInfo
=
447 (PVOID
)((ULONG_PTR
)DirInfo
.DirInfo
+ DirInfo
.FullDirInfo
->NextEntryOffset
);
449 pFileName
= (InfoLevel
== FindExInfoStandard
450 ? NextDirInfo
.BothDirInfo
->FileName
451 : NextDirInfo
.FullDirInfo
->FileName
);
453 /* Be paranoid and make sure that the next entry is completely there */
454 if (BufferEnd
< (ULONG_PTR
)NextDirInfo
.DirInfo
||
455 BufferEnd
< (ULONG_PTR
)&NextDirInfo
.FullDirInfo
->FileNameLength
+ sizeof(NextDirInfo
.FullDirInfo
->FileNameLength
) ||
456 BufferEnd
<= (ULONG_PTR
)((ULONG_PTR
)pFileName
+ NextDirInfo
.FullDirInfo
->FileNameLength
))
458 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
463 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
466 if ((FindFileData
->SearchOp
!= FindExSearchLimitToDirectories
) ||
467 (DirInfo
.FullDirInfo
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
471 } while ( FoundFile
.DirInfo
== NULL
&& (FindFileData
->NextDirInfo
.DirInfo
|| FindFileData
->HasMoreData
) );
473 if (FoundFile
.DirInfo
!= NULL
)
475 /* Return the information */
476 CopyFindData(lpFindFileData
, InfoLevel
, FoundFile
);
479 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
482 if (!NT_SUCCESS(Status
))
484 BaseSetLastNTError(Status
);
487 else if (FoundFile
.DirInfo
== NULL
)
489 SetLastError(ERROR_NO_MORE_FILES
);
502 FindClose(HANDLE hFindFile
)
504 TRACE("FindClose(hFindFile %p)\n", hFindFile
);
506 if (hFindFile
== FIND_DEVICE_HANDLE
)
509 if (!hFindFile
|| hFindFile
== INVALID_HANDLE_VALUE
)
511 SetLastError(ERROR_INVALID_HANDLE
);
515 /* Protect with SEH against closing attempts on invalid handles. */
518 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindFile
;
520 switch (FindDataHandle
->Type
)
524 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
525 NtClose(FindDataHandle
->u
.FindFileData
->Handle
);
526 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
527 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
533 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
534 if (FindDataHandle
->u
.FindStreamData
->FileStreamInfo
!= NULL
)
536 RtlFreeHeap(RtlGetProcessHeap(), 0,
537 FindDataHandle
->u
.FindStreamData
->FileStreamInfo
);
539 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
540 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
546 SetLastError(ERROR_INVALID_HANDLE
);
547 _SEH2_YIELD(return FALSE
);
551 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
553 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
555 BaseSetLastNTError(_SEH2_GetExceptionCode());
556 _SEH2_YIELD(return FALSE
);
569 FindFirstFileExA(IN LPCSTR lpFileName
,
570 IN FINDEX_INFO_LEVELS fInfoLevelId
,
571 OUT LPVOID lpFindFileData
,
572 IN FINDEX_SEARCH_OPS fSearchOp
,
573 LPVOID lpSearchFilter
,
574 IN DWORD dwAdditionalFlags
)
580 PUNICODE_STRING lpFileNameW
;
581 WIN32_FIND_DATAW FindFileDataW
;
582 LPWIN32_FIND_DATAA lpFindFileDataA
= (LPWIN32_FIND_DATAA
)lpFindFileData
;
584 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
585 fSearchOp
== FindExSearchLimitToDevices
||
586 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
588 SetLastError(fSearchOp
== FindExSearchLimitToDevices
589 ? ERROR_NOT_SUPPORTED
590 : ERROR_INVALID_PARAMETER
);
591 return INVALID_HANDLE_VALUE
;
594 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
595 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
597 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
603 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
605 RtlCopyMemory(lpFindFileDataA
,
607 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
609 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
610 Ansi
.Buffer
= lpFindFileDataA
->cFileName
;
612 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cFileName
);
613 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
614 if (!NT_SUCCESS(Status
))
617 BaseSetLastNTError(Status
);
618 return INVALID_HANDLE_VALUE
;
621 if (fInfoLevelId
!= FindExInfoBasic
)
623 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
624 Ansi
.Buffer
= lpFindFileDataA
->cAlternateFileName
;
626 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cAlternateFileName
);
627 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
628 if (!NT_SUCCESS(Status
))
631 BaseSetLastNTError(Status
);
632 return INVALID_HANDLE_VALUE
;
637 lpFindFileDataA
->cAlternateFileName
[0] = ANSI_NULL
;
649 FindFirstFileExW(IN LPCWSTR lpFileName
,
650 IN FINDEX_INFO_LEVELS fInfoLevelId
,
651 OUT LPVOID lpFindFileData
,
652 IN FINDEX_SEARCH_OPS fSearchOp
,
653 LPVOID lpSearchFilter
,
654 IN DWORD dwAdditionalFlags
)
656 UNREFERENCED_PARAMETER(lpSearchFilter
);
658 TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName
);
660 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
661 fSearchOp
== FindExSearchLimitToDevices
||
662 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
664 SetLastError(fSearchOp
== FindExSearchLimitToDevices
665 ? ERROR_NOT_SUPPORTED
666 : ERROR_INVALID_PARAMETER
);
667 return INVALID_HANDLE_VALUE
;
670 if (fSearchOp
== FindExSearchNameMatch
||
671 fSearchOp
== FindExSearchLimitToDirectories
)
673 LPWIN32_FIND_DATAW Win32FindData
= (LPWIN32_FIND_DATAW
)lpFindFileData
;
674 PFIND_DATA_HANDLE FindDataHandle
;
675 PFIND_FILE_DATA FindFileData
;
677 UNICODE_STRING NtPath
, FilePattern
, FileName
;
679 RTL_RELATIVE_NAME_U RelativePath
;
680 ULONG DeviceNameInfo
= 0;
683 OBJECT_ATTRIBUTES ObjectAttributes
;
684 IO_STATUS_BLOCK IoStatusBlock
;
685 HANDLE hDirectory
= NULL
;
687 BOOLEAN HadADot
= FALSE
;
690 * May represent many FILE_BOTH_DIR_INFORMATION
691 * or many FILE_FULL_DIR_INFORMATION structures.
692 * NOTE: NtQueryDirectoryFile requires the buffer to be ULONG-aligned
694 DECLSPEC_ALIGN(4) BYTE DirectoryInfo
[FIND_DATA_SIZE
];
695 DIR_INFORMATION DirInfo
= { .DirInfo
= &DirectoryInfo
};
697 RtlInitUnicodeString(&FileName
, lpFileName
);
698 if (FileName
.Length
!= 0 && FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] == L
'.')
703 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
705 (PCWSTR
*)&FilePattern
.Buffer
,
708 SetLastError(ERROR_PATH_NOT_FOUND
);
709 return INVALID_HANDLE_VALUE
;
712 DPRINT("lpFileName = '%S'\n", lpFileName
);
713 DPRINT("FilePattern.Buffer = '%S'\n", FilePattern
.Buffer
);
714 DPRINT("RelativePath.RelativeName = '%wZ'\n", &RelativePath
.RelativeName
);
715 DPRINT("NtPath.Buffer = '%S'\n", NtPath
.Buffer
);
716 DPRINT("NtPath - Before = '%wZ'\n", &NtPath
);
718 /* Save the buffer pointer for later, we need to free it! */
719 NtPathBuffer
= NtPath
.Buffer
;
722 * Contrary to what Windows does, check NOW whether or not
723 * lpFileName is a DOS driver. Therefore we don't have to
724 * write broken code to check that.
726 if (!FilePattern
.Buffer
|| !*FilePattern
.Buffer
)
728 /* No file pattern specified, or DOS device */
730 DeviceNameInfo
= RtlIsDosDeviceName_U(lpFileName
);
731 if (DeviceNameInfo
!= 0)
733 RtlReleaseRelativeName(&RelativePath
);
734 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
736 /* OK, it's really a DOS device */
737 CopyDeviceFindData(Win32FindData
, lpFileName
, DeviceNameInfo
);
738 return FIND_DEVICE_HANDLE
;
742 /* A file pattern was specified, or it was not a DOS device */
744 /* If there is a file pattern then determine its length */
745 if (FilePattern
.Buffer
!= NULL
)
747 FilePattern
.Length
= NtPath
.Length
-
748 (USHORT
)((ULONG_PTR
)FilePattern
.Buffer
- (ULONG_PTR
)NtPath
.Buffer
);
752 FilePattern
.Length
= 0;
754 FilePattern
.MaximumLength
= FilePattern
.Length
;
756 if (RelativePath
.RelativeName
.Length
!= 0 &&
757 RelativePath
.RelativeName
.Buffer
!= FilePattern
.Buffer
)
759 if (FilePattern
.Buffer
!= NULL
)
761 /* This is a relative path to RelativePath.ContainingDirectory, adjust NtPath! */
762 NtPath
.Length
= NtPath
.MaximumLength
=
763 (USHORT
)((ULONG_PTR
)FilePattern
.Buffer
- (ULONG_PTR
)RelativePath
.RelativeName
.Buffer
);
764 NtPath
.Buffer
= RelativePath
.RelativeName
.Buffer
;
769 /* This is an absolute path, NtPath receives the full path */
770 RelativePath
.ContainingDirectory
= NULL
;
771 if (FilePattern
.Buffer
!= NULL
)
773 NtPath
.Length
= NtPath
.MaximumLength
=
774 (USHORT
)((ULONG_PTR
)FilePattern
.Buffer
- (ULONG_PTR
)NtPath
.Buffer
);
778 DPRINT("NtPath - After = '%wZ'\n", &NtPath
);
779 DPRINT("FilePattern = '%wZ'\n", &FilePattern
);
780 DPRINT("RelativeTo = 0x%p\n", RelativePath
.ContainingDirectory
);
782 InitializeObjectAttributes(&ObjectAttributes
,
784 (dwAdditionalFlags
& FIND_FIRST_EX_CASE_SENSITIVE
) ? 0 : OBJ_CASE_INSENSITIVE
,
785 RelativePath
.ContainingDirectory
,
788 Status
= NtOpenFile(&hDirectory
,
789 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
792 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
793 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
795 if (!NT_SUCCESS(Status
))
797 RtlReleaseRelativeName(&RelativePath
);
798 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
800 /* Adjust the last error codes */
801 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
802 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
803 else if (Status
== STATUS_OBJECT_TYPE_MISMATCH
)
804 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
806 BaseSetLastNTError(Status
);
807 return INVALID_HANDLE_VALUE
;
811 * Fail if there is not any file pattern,
812 * since we are not looking for a device.
814 if (FilePattern
.Length
== 0)
817 RtlReleaseRelativeName(&RelativePath
);
818 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
820 SetLastError(ERROR_FILE_NOT_FOUND
);
821 return INVALID_HANDLE_VALUE
;
824 /* Change pattern: "*.*" --> "*" */
825 if (FilePattern
.Length
== 6 &&
826 RtlCompareMemory(FilePattern
.Buffer
, L
"*.*", 6) == 6)
828 FilePattern
.Length
= 2;
832 /* Translate wildcard from "real" world to DOS world for lower interpretation */
833 USHORT PatternIndex
= 0;
834 while (PatternIndex
< FilePattern
.Length
/ sizeof(WCHAR
))
836 if (PatternIndex
> 0)
838 if (FilePattern
.Buffer
[PatternIndex
] == L
'.' &&
839 FilePattern
.Buffer
[PatternIndex
- 1] == L
'*')
841 FilePattern
.Buffer
[PatternIndex
- 1] = L
'<';
845 if (FilePattern
.Buffer
[PatternIndex
] == L
'?')
847 FilePattern
.Buffer
[PatternIndex
] = L
'>';
848 if (PatternIndex
> 0)
850 if (FilePattern
.Buffer
[PatternIndex
- 1] == L
'.')
852 FilePattern
.Buffer
[PatternIndex
- 1] = L
'\"';
856 else if (FilePattern
.Buffer
[PatternIndex
] == L
'*')
858 if (PatternIndex
> 0)
860 if (FilePattern
.Buffer
[PatternIndex
- 1] == L
'.')
862 FilePattern
.Buffer
[PatternIndex
- 1] = L
'\"';
870 /* Handle partial wc if our last dot was eaten */
873 if (FilePattern
.Buffer
[FilePattern
.Length
/ sizeof(WCHAR
) - 1] == L
'*')
875 FilePattern
.Buffer
[FilePattern
.Length
/ sizeof(WCHAR
) - 1] = L
'<';
880 Status
= NtQueryDirectoryFile(hDirectory
,
883 DirInfo
.DirInfo
, // == &DirectoryInfo
884 sizeof(DirectoryInfo
),
885 (fInfoLevelId
== FindExInfoStandard
886 ? FileBothDirectoryInformation
887 : FileFullDirectoryInformation
),
888 TRUE
, /* Return a single entry */
892 RtlReleaseRelativeName(&RelativePath
);
893 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
895 if (!NT_SUCCESS(Status
))
898 BaseSetLastNTError(Status
);
899 return INVALID_HANDLE_VALUE
;
902 ASSERT(DirInfo
.FullDirInfo
->NextEntryOffset
== 0);
904 /* Return the information */
905 CopyFindData(Win32FindData
, fInfoLevelId
, DirInfo
);
908 * Initialization of the search handle.
910 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
912 sizeof(FIND_DATA_HANDLE
) +
913 sizeof(FIND_FILE_DATA
));
917 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
918 return INVALID_HANDLE_VALUE
;
921 FindDataHandle
->Type
= FindFile
;
922 FindDataHandle
->u
.FindFileData
= (PFIND_FILE_DATA
)(FindDataHandle
+ 1);
923 FindFileData
= FindDataHandle
->u
.FindFileData
;
925 FindFileData
->Handle
= hDirectory
;
926 FindFileData
->InfoLevel
= fInfoLevelId
;
927 FindFileData
->SearchOp
= fSearchOp
;
928 FindFileData
->HasMoreData
= FALSE
;
929 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
931 /* The critical section must always be initialized */
932 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
933 if (!NT_SUCCESS(Status
))
936 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
938 BaseSetLastNTError(Status
);
939 return INVALID_HANDLE_VALUE
;
942 return (HANDLE
)FindDataHandle
;
946 SetLastError(ERROR_NOT_SUPPORTED
);
947 return INVALID_HANDLE_VALUE
;
957 FindFirstStreamW(IN LPCWSTR lpFileName
,
958 IN STREAM_INFO_LEVELS InfoLevel
,
959 OUT LPVOID lpFindStreamData
,
962 PFIND_DATA_HANDLE FindDataHandle
= NULL
;
963 PFIND_STREAM_DATA FindStreamData
;
964 OBJECT_ATTRIBUTES ObjectAttributes
;
965 IO_STATUS_BLOCK IoStatusBlock
;
966 UNICODE_STRING NtFilePath
;
967 HANDLE FileHandle
= NULL
;
969 ULONG BufferSize
= 0;
971 if (dwFlags
!= 0 || InfoLevel
!= FindStreamInfoStandard
||
972 lpFindStreamData
== NULL
)
974 SetLastError(ERROR_INVALID_PARAMETER
);
975 return INVALID_HANDLE_VALUE
;
978 /* Validate and translate the filename */
979 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
983 SetLastError(ERROR_PATH_NOT_FOUND
);
984 return INVALID_HANDLE_VALUE
;
988 InitializeObjectAttributes(&ObjectAttributes
,
990 OBJ_CASE_INSENSITIVE
,
994 Status
= NtCreateFile(&FileHandle
,
999 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1002 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1005 * Initialization of the search handle.
1007 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
1009 sizeof(FIND_DATA_HANDLE
) +
1010 sizeof(FIND_STREAM_DATA
));
1011 if (!FindDataHandle
)
1013 Status
= STATUS_NO_MEMORY
;
1017 FindDataHandle
->Type
= FindStream
;
1018 FindDataHandle
->u
.FindStreamData
= (PFIND_STREAM_DATA
)(FindDataHandle
+ 1);
1019 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
1021 FindStreamData
->InfoLevel
= InfoLevel
;
1022 FindStreamData
->FileStreamInfo
= NULL
;
1023 FindStreamData
->CurrentInfo
= NULL
;
1025 /* The critical section must always be initialized */
1026 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
1027 if (!NT_SUCCESS(Status
))
1029 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
1033 /* Capture all information about the streams */
1036 BufferSize
+= 0x1000;
1038 if (FindStreamData
->FileStreamInfo
== NULL
)
1040 FindStreamData
->FileStreamInfo
= RtlAllocateHeap(RtlGetProcessHeap(),
1043 if (FindStreamData
->FileStreamInfo
== NULL
)
1045 Status
= STATUS_NO_MEMORY
;
1051 PFILE_STREAM_INFORMATION pfsi
;
1053 pfsi
= RtlReAllocateHeap(RtlGetProcessHeap(),
1054 0, // HEAP_ZERO_MEMORY,
1055 FindStreamData
->FileStreamInfo
,
1059 Status
= STATUS_NO_MEMORY
;
1063 FindStreamData
->FileStreamInfo
= pfsi
;
1066 Status
= NtQueryInformationFile(FileHandle
,
1068 FindStreamData
->FileStreamInfo
,
1070 FileStreamInformation
);
1072 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
1074 if (NT_SUCCESS(Status
))
1076 /* Select the first stream and return the information */
1077 FindStreamData
->CurrentInfo
= FindStreamData
->FileStreamInfo
;
1078 CopyStreamData(FindStreamData
, lpFindStreamData
);
1081 Status
= STATUS_SUCCESS
;
1085 if (FindStreamData
->FileStreamInfo
)
1087 RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData
->FileStreamInfo
);
1090 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
1094 if (FileHandle
) NtClose(FileHandle
);
1096 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath
.Buffer
);
1098 if (NT_SUCCESS(Status
))
1100 return (HANDLE
)FindDataHandle
;
1104 BaseSetLastNTError(Status
);
1105 return INVALID_HANDLE_VALUE
;
1115 FindNextStreamW(IN HANDLE hFindStream
,
1116 OUT LPVOID lpFindStreamData
)
1118 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindStream
;
1119 PFIND_STREAM_DATA FindStreamData
;
1121 if (hFindStream
== NULL
|| hFindStream
== INVALID_HANDLE_VALUE
||
1122 FindDataHandle
->Type
!= FindStream
)
1124 SetLastError(ERROR_INVALID_HANDLE
);
1128 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
1130 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
1132 /* Select next stream if possible */
1133 if (FindStreamData
->CurrentInfo
->NextEntryOffset
!= 0)
1135 FindStreamData
->CurrentInfo
= (PFILE_STREAM_INFORMATION
)((ULONG_PTR
)FindStreamData
->CurrentInfo
+
1136 FindStreamData
->CurrentInfo
->NextEntryOffset
);
1138 /* Return the information */
1139 CopyStreamData(FindStreamData
, lpFindStreamData
);
1141 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1146 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1148 SetLastError(ERROR_HANDLE_EOF
);