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
)
124 /* DeviceNameInfo == { USHORT Offset; USHORT Length } */
125 Length
= (SIZE_T
)(DeviceNameInfo
& 0xFFFF);
126 DeviceName
= (LPCWSTR
)((ULONG_PTR
)lpFileName
+ ((DeviceNameInfo
>> 16) & 0xFFFF));
128 /* Return the data */
129 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
130 lpFindFileData
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
131 RtlCopyMemory(lpFindFileData
->cFileName
,
135 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
144 CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData
,
145 IN FINDEX_INFO_LEVELS fInfoLevelId
,
146 IN DIR_INFORMATION DirInfo
)
148 #define ULARGE_INTEGER_2_FILETIME(ft, ul) \
150 (ft).dwHighDateTime = (ul).u.HighPart; \
151 (ft).dwLowDateTime = (ul).u.LowPart ; \
156 RtlZeroMemory(lpFindFileData
, sizeof(*lpFindFileData
));
158 lpFindFileData
->dwFileAttributes
= DirInfo
.FullDirInfo
->FileAttributes
;
160 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftCreationTime
, DirInfo
.FullDirInfo
->CreationTime
);
161 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftLastAccessTime
, DirInfo
.FullDirInfo
->LastAccessTime
);
162 ULARGE_INTEGER_2_FILETIME(lpFindFileData
->ftLastWriteTime
, DirInfo
.FullDirInfo
->LastWriteTime
);
164 lpFindFileData
->nFileSizeHigh
= DirInfo
.FullDirInfo
->EndOfFile
.u
.HighPart
;
165 lpFindFileData
->nFileSizeLow
= DirInfo
.FullDirInfo
->EndOfFile
.u
.LowPart
;
167 /* dwReserved0 contains the NTFS reparse point tag, if any. */
168 if (DirInfo
.FullDirInfo
->FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
169 lpFindFileData
->dwReserved0
= DirInfo
.FullDirInfo
->EaSize
;
171 lpFindFileData
->dwReserved0
= 0;
173 /* Unused dwReserved1 field */
174 lpFindFileData
->dwReserved1
= 0;
176 if (fInfoLevelId
== FindExInfoStandard
)
178 RtlCopyMemory(lpFindFileData
->cFileName
,
179 DirInfo
.BothDirInfo
->FileName
,
180 DirInfo
.BothDirInfo
->FileNameLength
);
181 lpFindFileData
->cFileName
[DirInfo
.BothDirInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
183 RtlCopyMemory(lpFindFileData
->cAlternateFileName
,
184 DirInfo
.BothDirInfo
->ShortName
,
185 DirInfo
.BothDirInfo
->ShortNameLength
);
186 lpFindFileData
->cAlternateFileName
[DirInfo
.BothDirInfo
->ShortNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
188 else if (fInfoLevelId
== FindExInfoBasic
)
190 RtlCopyMemory(lpFindFileData
->cFileName
,
191 DirInfo
.FullDirInfo
->FileName
,
192 DirInfo
.FullDirInfo
->FileNameLength
);
193 lpFindFileData
->cFileName
[DirInfo
.FullDirInfo
->FileNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
195 lpFindFileData
->cAlternateFileName
[0] = UNICODE_NULL
;
199 /* Invalid InfoLevelId */
203 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
212 CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData
,
213 OUT PWIN32_FIND_STREAM_DATA lpFindStreamData
)
217 ASSERT(FindStreamData
->CurrentInfo
);
219 switch (FindStreamData
->InfoLevel
)
221 case FindStreamInfoStandard
:
223 ULONG StreamNameLen
= min(FindStreamData
->CurrentInfo
->StreamNameLength
,
224 sizeof(lpFindStreamData
->cStreamName
) - sizeof(WCHAR
));
226 RtlZeroMemory(lpFindStreamData
, sizeof(*lpFindStreamData
));
228 lpFindStreamData
->StreamSize
.QuadPart
= FindStreamData
->CurrentInfo
->StreamSize
.QuadPart
;
229 RtlCopyMemory(lpFindStreamData
->cStreamName
,
230 FindStreamData
->CurrentInfo
->StreamName
,
232 lpFindStreamData
->cStreamName
[StreamNameLen
/ sizeof(WCHAR
)] = UNICODE_NULL
;
239 /* Invalid InfoLevel */
245 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
254 /* PUBLIC FUNCTIONS ***********************************************************/
261 FindFirstFileA(IN LPCSTR lpFileName
,
262 OUT LPWIN32_FIND_DATAA lpFindFileData
)
268 PUNICODE_STRING lpFileNameW
;
269 WIN32_FIND_DATAW FindFileDataW
;
271 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
272 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
274 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
277 FindExSearchNameMatch
,
279 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
281 RtlCopyMemory(lpFindFileData
,
283 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
285 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
286 Ansi
.Buffer
= lpFindFileData
->cFileName
;
288 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
289 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
290 if (!NT_SUCCESS(Status
))
293 BaseSetLastNTError(Status
);
294 return INVALID_HANDLE_VALUE
;
297 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
298 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
300 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
301 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
302 if (!NT_SUCCESS(Status
))
305 BaseSetLastNTError(Status
);
306 return INVALID_HANDLE_VALUE
;
318 FindFirstFileW(IN LPCWSTR lpFileName
,
319 OUT LPWIN32_FIND_DATAW lpFindFileData
)
321 return FindFirstFileExW(lpFileName
,
324 FindExSearchNameMatch
,
334 FindNextFileA(IN HANDLE hFindFile
,
335 OUT LPWIN32_FIND_DATAA lpFindFileData
)
340 WIN32_FIND_DATAW FindFileDataW
;
342 if (!FindNextFileW(hFindFile
, &FindFileDataW
))
345 RtlCopyMemory(lpFindFileData
,
347 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
349 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
350 Ansi
.Buffer
= lpFindFileData
->cFileName
;
352 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cFileName
);
353 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
354 if (!NT_SUCCESS(Status
))
356 BaseSetLastNTError(Status
);
360 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
361 Ansi
.Buffer
= lpFindFileData
->cAlternateFileName
;
363 Ansi
.MaximumLength
= sizeof(lpFindFileData
->cAlternateFileName
);
364 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
365 if (!NT_SUCCESS(Status
))
367 BaseSetLastNTError(Status
);
380 FindNextFileW(IN HANDLE hFindFile
,
381 OUT LPWIN32_FIND_DATAW lpFindFileData
)
383 NTSTATUS Status
= STATUS_SUCCESS
;
384 DIR_INFORMATION FoundFile
= {NULL
};
386 TRACE("FindNextFileW(%p, 0x%p)\n", hFindFile
, lpFindFileData
);
388 if (hFindFile
!= FIND_DEVICE_HANDLE
)
390 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindFile
;
391 PFIND_FILE_DATA FindFileData
;
392 FINDEX_INFO_LEVELS InfoLevel
;
393 IO_STATUS_BLOCK IoStatusBlock
;
394 DIR_INFORMATION DirInfo
= {NULL
}, NextDirInfo
= {NULL
};
396 if (hFindFile
== NULL
|| hFindFile
== INVALID_HANDLE_VALUE
||
397 FindDataHandle
->Type
!= FindFile
)
399 SetLastError(ERROR_INVALID_HANDLE
);
403 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
405 FindFileData
= FindDataHandle
->u
.FindFileData
;
406 InfoLevel
= FindFileData
->InfoLevel
;
410 if (FindFileData
->NextDirInfo
.DirInfo
== NULL
)
412 Status
= NtQueryDirectoryFile(FindFileData
->Handle
,
415 &FindFileData
->Buffer
,
416 sizeof(FindFileData
->Buffer
),
417 (InfoLevel
== FindExInfoStandard
418 ? FileBothDirectoryInformation
419 : FileFullDirectoryInformation
),
421 NULL
, /* Use the file pattern from the first call */
423 if (Status
== STATUS_BUFFER_OVERFLOW
)
425 FindFileData
->HasMoreData
= TRUE
;
426 Status
= STATUS_SUCCESS
;
430 if (!NT_SUCCESS(Status
)) break;
431 FindFileData
->HasMoreData
= FALSE
;
434 FindFileData
->NextDirInfo
.DirInfo
= &FindFileData
->Buffer
;
437 DirInfo
= FindFileData
->NextDirInfo
;
439 if (DirInfo
.FullDirInfo
->NextEntryOffset
!= 0)
441 ULONG_PTR BufferEnd
= (ULONG_PTR
)&FindFileData
->Buffer
+ sizeof(FindFileData
->Buffer
);
444 NextDirInfo
.DirInfo
= FindFileData
->NextDirInfo
.DirInfo
=
445 (PVOID
)((ULONG_PTR
)DirInfo
.DirInfo
+ DirInfo
.FullDirInfo
->NextEntryOffset
);
447 pFileName
= (InfoLevel
== FindExInfoStandard
448 ? NextDirInfo
.BothDirInfo
->FileName
449 : NextDirInfo
.FullDirInfo
->FileName
);
451 /* Be paranoid and make sure that the next entry is completely there */
452 if (BufferEnd
< (ULONG_PTR
)NextDirInfo
.DirInfo
||
453 BufferEnd
< (ULONG_PTR
)&NextDirInfo
.FullDirInfo
->FileNameLength
+ sizeof(NextDirInfo
.FullDirInfo
->FileNameLength
) ||
454 BufferEnd
<= (ULONG_PTR
)((ULONG_PTR
)pFileName
+ NextDirInfo
.FullDirInfo
->FileNameLength
))
456 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
461 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
464 if ((FindFileData
->SearchOp
!= FindExSearchLimitToDirectories
) ||
465 (DirInfo
.FullDirInfo
->FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
469 } while ( FoundFile
.DirInfo
== NULL
&& (FindFileData
->NextDirInfo
.DirInfo
|| FindFileData
->HasMoreData
) );
471 if (FoundFile
.DirInfo
!= NULL
)
473 /* Return the information */
474 CopyFindData(lpFindFileData
, InfoLevel
, FoundFile
);
477 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
480 if (!NT_SUCCESS(Status
))
482 BaseSetLastNTError(Status
);
485 else if (FoundFile
.DirInfo
== NULL
)
487 SetLastError(ERROR_NO_MORE_FILES
);
500 FindClose(HANDLE hFindFile
)
502 TRACE("FindClose(hFindFile %p)\n", hFindFile
);
504 if (hFindFile
== FIND_DEVICE_HANDLE
)
507 if (!hFindFile
|| hFindFile
== INVALID_HANDLE_VALUE
)
509 SetLastError(ERROR_INVALID_HANDLE
);
513 /* Protect with SEH against closing attempts on invalid handles. */
516 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindFile
;
518 switch (FindDataHandle
->Type
)
522 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
523 NtClose(FindDataHandle
->u
.FindFileData
->Handle
);
524 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
525 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
531 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
532 if (FindDataHandle
->u
.FindStreamData
->FileStreamInfo
!= NULL
)
534 RtlFreeHeap(RtlGetProcessHeap(), 0,
535 FindDataHandle
->u
.FindStreamData
->FileStreamInfo
);
537 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
538 RtlDeleteCriticalSection(&FindDataHandle
->Lock
);
544 SetLastError(ERROR_INVALID_HANDLE
);
545 _SEH2_YIELD(return FALSE
);
549 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
551 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
553 BaseSetLastNTError(_SEH2_GetExceptionCode());
554 _SEH2_YIELD(return FALSE
);
566 FindFirstFileExA(IN LPCSTR lpFileName
,
567 IN FINDEX_INFO_LEVELS fInfoLevelId
,
568 OUT LPVOID lpFindFileData
,
569 IN FINDEX_SEARCH_OPS fSearchOp
,
570 LPVOID lpSearchFilter
,
571 IN DWORD dwAdditionalFlags
)
577 PUNICODE_STRING lpFileNameW
;
578 WIN32_FIND_DATAW FindFileDataW
;
579 LPWIN32_FIND_DATAA lpFindFileDataA
= (LPWIN32_FIND_DATAA
)lpFindFileData
;
581 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
582 fSearchOp
== FindExSearchLimitToDevices
||
583 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
585 SetLastError(fSearchOp
== FindExSearchLimitToDevices
586 ? ERROR_NOT_SUPPORTED
587 : ERROR_INVALID_PARAMETER
);
588 return INVALID_HANDLE_VALUE
;
591 lpFileNameW
= Basep8BitStringToStaticUnicodeString(lpFileName
);
592 if (!lpFileNameW
) return INVALID_HANDLE_VALUE
;
594 hSearch
= FindFirstFileExW(lpFileNameW
->Buffer
,
600 if (hSearch
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
602 RtlCopyMemory(lpFindFileDataA
,
604 FIELD_OFFSET(WIN32_FIND_DATAA
, cFileName
));
606 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cFileName
);
607 Ansi
.Buffer
= lpFindFileDataA
->cFileName
;
609 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cFileName
);
610 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
611 if (!NT_SUCCESS(Status
))
614 BaseSetLastNTError(Status
);
615 return INVALID_HANDLE_VALUE
;
618 if (fInfoLevelId
!= FindExInfoBasic
)
620 RtlInitUnicodeString(&UTF8
, FindFileDataW
.cAlternateFileName
);
621 Ansi
.Buffer
= lpFindFileDataA
->cAlternateFileName
;
623 Ansi
.MaximumLength
= sizeof(lpFindFileDataA
->cAlternateFileName
);
624 Status
= BasepUnicodeStringTo8BitString(&Ansi
, &UTF8
, FALSE
);
625 if (!NT_SUCCESS(Status
))
628 BaseSetLastNTError(Status
);
629 return INVALID_HANDLE_VALUE
;
634 lpFindFileDataA
->cAlternateFileName
[0] = ANSI_NULL
;
646 FindFirstFileExW(IN LPCWSTR lpFileName
,
647 IN FINDEX_INFO_LEVELS fInfoLevelId
,
648 OUT LPVOID lpFindFileData
,
649 IN FINDEX_SEARCH_OPS fSearchOp
,
650 LPVOID lpSearchFilter
,
651 IN DWORD dwAdditionalFlags
)
653 TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName
);
655 if ((fInfoLevelId
!= FindExInfoStandard
&& fInfoLevelId
!= FindExInfoBasic
) ||
656 fSearchOp
== FindExSearchLimitToDevices
||
657 dwAdditionalFlags
& ~FIND_FIRST_EX_CASE_SENSITIVE
/* only supported flag for now */)
659 SetLastError(fSearchOp
== FindExSearchLimitToDevices
660 ? ERROR_NOT_SUPPORTED
661 : ERROR_INVALID_PARAMETER
);
662 return INVALID_HANDLE_VALUE
;
665 if (fSearchOp
== FindExSearchNameMatch
||
666 fSearchOp
== FindExSearchLimitToDirectories
)
668 LPWIN32_FIND_DATAW Win32FindData
= (LPWIN32_FIND_DATAW
)lpFindFileData
;
669 PFIND_DATA_HANDLE FindDataHandle
;
670 PFIND_FILE_DATA FindFileData
;
672 UNICODE_STRING NtPath
, FilePattern
, FileName
;
674 RTL_RELATIVE_NAME_U RelativePath
;
675 ULONG DeviceNameInfo
= 0;
678 OBJECT_ATTRIBUTES ObjectAttributes
;
679 IO_STATUS_BLOCK IoStatusBlock
;
680 HANDLE hDirectory
= NULL
;
682 BOOLEAN HadADot
= FALSE
;
685 * May represent many FILE_BOTH_DIR_INFORMATION
686 * or many FILE_FULL_DIR_INFORMATION structures.
688 BYTE DirectoryInfo
[FIND_DATA_SIZE
];
689 DIR_INFORMATION DirInfo
= {&DirectoryInfo
};
691 /* The search filter is always unused */
694 SetLastError(ERROR_INVALID_PARAMETER
);
695 return INVALID_HANDLE_VALUE
;
698 RtlInitUnicodeString(&FileName
, lpFileName
);
699 if (FileName
.Length
!= 0 && FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] == L
'.')
704 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
706 (PCWSTR
*)&FilePattern
.Buffer
,
709 SetLastError(ERROR_PATH_NOT_FOUND
);
710 return INVALID_HANDLE_VALUE
;
713 DPRINT("lpFileName = '%S'\n", lpFileName
);
714 DPRINT("FilePattern.Buffer = '%S'\n", FilePattern
.Buffer
);
715 DPRINT("RelativePath.RelativeName = '%wZ'\n", &RelativePath
.RelativeName
);
716 DPRINT("NtPath.Buffer = '%S'\n", NtPath
.Buffer
);
717 DPRINT("NtPath - Before = '%wZ'\n", &NtPath
);
719 /* Save the buffer pointer for later, we need to free it! */
720 NtPathBuffer
= NtPath
.Buffer
;
723 * Contrary to what Windows does, check NOW whether or not
724 * lpFileName is a DOS driver. Therefore we don't have to
725 * write broken code to check that.
727 if (!FilePattern
.Buffer
|| !*FilePattern
.Buffer
)
729 /* No file pattern specified, or DOS device */
731 DeviceNameInfo
= RtlIsDosDeviceName_U(lpFileName
);
732 if (DeviceNameInfo
!= 0)
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 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
799 /* Adjust the last error codes */
800 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
801 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
802 else if (Status
== STATUS_OBJECT_TYPE_MISMATCH
)
803 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
805 BaseSetLastNTError(Status
);
806 return INVALID_HANDLE_VALUE
;
810 * Fail if there is not any file pattern,
811 * since we are not looking for a device.
813 if (FilePattern
.Length
== 0)
816 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
818 SetLastError(ERROR_FILE_NOT_FOUND
);
819 return INVALID_HANDLE_VALUE
;
822 /* Change pattern: "*.*" --> "*" */
823 if (FilePattern
.Length
== 6 &&
824 RtlCompareMemory(FilePattern
.Buffer
, L
"*.*", 6) == 6)
826 FilePattern
.Length
= 2;
830 /* Translate wildcard from "real" world to DOS world for lower interpretation */
831 USHORT PatternIndex
= 0;
832 while (PatternIndex
< FilePattern
.Length
/ sizeof(WCHAR
))
834 if (PatternIndex
> 0)
836 if (FilePattern
.Buffer
[PatternIndex
] == L
'.' &&
837 FilePattern
.Buffer
[PatternIndex
- 1] == L
'*')
839 FilePattern
.Buffer
[PatternIndex
- 1] = L
'<';
843 if (FilePattern
.Buffer
[PatternIndex
] == L
'?')
845 FilePattern
.Buffer
[PatternIndex
] = L
'>';
846 if (PatternIndex
> 0)
848 if (FilePattern
.Buffer
[PatternIndex
- 1] == L
'.')
850 FilePattern
.Buffer
[PatternIndex
- 1] = L
'\"';
854 else if (FilePattern
.Buffer
[PatternIndex
] == L
'*')
856 if (PatternIndex
> 0)
858 if (FilePattern
.Buffer
[PatternIndex
- 1] == L
'.')
860 FilePattern
.Buffer
[PatternIndex
- 1] = L
'\"';
868 /* Handle partial wc if our last dot was eaten */
871 if (FilePattern
.Buffer
[FilePattern
.Length
/ sizeof(WCHAR
) - 1] == L
'*')
873 FilePattern
.Buffer
[FilePattern
.Length
/ sizeof(WCHAR
) - 1] = L
'<';
878 Status
= NtQueryDirectoryFile(hDirectory
,
881 DirInfo
.DirInfo
, // == &DirectoryInfo
882 sizeof(DirectoryInfo
),
883 (fInfoLevelId
== FindExInfoStandard
884 ? FileBothDirectoryInformation
885 : FileFullDirectoryInformation
),
886 TRUE
, /* Return a single entry */
890 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer
);
892 if (!NT_SUCCESS(Status
))
895 BaseSetLastNTError(Status
);
896 return INVALID_HANDLE_VALUE
;
899 ASSERT(DirInfo
.FullDirInfo
->NextEntryOffset
== 0);
901 /* Return the information */
902 CopyFindData(Win32FindData
, fInfoLevelId
, DirInfo
);
905 * Initialization of the search handle.
907 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
909 sizeof(FIND_DATA_HANDLE
) +
910 sizeof(FIND_FILE_DATA
));
914 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
915 return INVALID_HANDLE_VALUE
;
918 FindDataHandle
->Type
= FindFile
;
919 FindDataHandle
->u
.FindFileData
= (PFIND_FILE_DATA
)(FindDataHandle
+ 1);
920 FindFileData
= FindDataHandle
->u
.FindFileData
;
922 FindFileData
->Handle
= hDirectory
;
923 FindFileData
->InfoLevel
= fInfoLevelId
;
924 FindFileData
->SearchOp
= fSearchOp
;
925 FindFileData
->HasMoreData
= FALSE
;
926 FindFileData
->NextDirInfo
.DirInfo
= NULL
;
928 /* The critical section must always be initialized */
929 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
930 if (!NT_SUCCESS(Status
))
933 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
935 BaseSetLastNTError(Status
);
936 return INVALID_HANDLE_VALUE
;
939 return (HANDLE
)FindDataHandle
;
943 SetLastError(ERROR_NOT_SUPPORTED
);
944 return INVALID_HANDLE_VALUE
;
954 FindFirstStreamW(IN LPCWSTR lpFileName
,
955 IN STREAM_INFO_LEVELS InfoLevel
,
956 OUT LPVOID lpFindStreamData
,
959 PFIND_DATA_HANDLE FindDataHandle
= NULL
;
960 PFIND_STREAM_DATA FindStreamData
;
961 OBJECT_ATTRIBUTES ObjectAttributes
;
962 IO_STATUS_BLOCK IoStatusBlock
;
963 UNICODE_STRING NtFilePath
;
964 HANDLE FileHandle
= NULL
;
966 ULONG BufferSize
= 0;
968 if (dwFlags
!= 0 || InfoLevel
!= FindStreamInfoStandard
||
969 lpFindStreamData
== NULL
)
971 SetLastError(ERROR_INVALID_PARAMETER
);
972 return INVALID_HANDLE_VALUE
;
975 /* Validate and translate the filename */
976 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
980 SetLastError(ERROR_PATH_NOT_FOUND
);
981 return INVALID_HANDLE_VALUE
;
985 InitializeObjectAttributes(&ObjectAttributes
,
987 OBJ_CASE_INSENSITIVE
,
991 Status
= NtCreateFile(&FileHandle
,
996 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
999 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1002 * Initialization of the search handle.
1004 FindDataHandle
= RtlAllocateHeap(RtlGetProcessHeap(),
1006 sizeof(FIND_DATA_HANDLE
) +
1007 sizeof(FIND_STREAM_DATA
));
1008 if (!FindDataHandle
)
1010 Status
= STATUS_NO_MEMORY
;
1014 FindDataHandle
->Type
= FindStream
;
1015 FindDataHandle
->u
.FindStreamData
= (PFIND_STREAM_DATA
)(FindDataHandle
+ 1);
1016 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
1018 FindStreamData
->InfoLevel
= InfoLevel
;
1019 FindStreamData
->FileStreamInfo
= NULL
;
1020 FindStreamData
->CurrentInfo
= NULL
;
1022 /* The critical section must always be initialized */
1023 Status
= RtlInitializeCriticalSection(&FindDataHandle
->Lock
);
1024 if (!NT_SUCCESS(Status
))
1026 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
1030 /* Capture all information about the streams */
1033 BufferSize
+= 0x1000;
1035 if (FindStreamData
->FileStreamInfo
== NULL
)
1037 FindStreamData
->FileStreamInfo
= RtlAllocateHeap(RtlGetProcessHeap(),
1040 if (FindStreamData
->FileStreamInfo
== NULL
)
1042 Status
= STATUS_NO_MEMORY
;
1048 PFILE_STREAM_INFORMATION pfsi
;
1050 pfsi
= RtlReAllocateHeap(RtlGetProcessHeap(),
1051 0, // HEAP_ZERO_MEMORY,
1052 FindStreamData
->FileStreamInfo
,
1056 Status
= STATUS_NO_MEMORY
;
1060 FindStreamData
->FileStreamInfo
= pfsi
;
1063 Status
= NtQueryInformationFile(FileHandle
,
1065 FindStreamData
->FileStreamInfo
,
1067 FileStreamInformation
);
1069 } while (Status
== STATUS_BUFFER_TOO_SMALL
);
1071 if (NT_SUCCESS(Status
))
1073 /* Select the first stream and return the information */
1074 FindStreamData
->CurrentInfo
= FindStreamData
->FileStreamInfo
;
1075 CopyStreamData(FindStreamData
, lpFindStreamData
);
1078 Status
= STATUS_SUCCESS
;
1082 if (FindStreamData
->FileStreamInfo
)
1084 RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData
->FileStreamInfo
);
1087 RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle
);
1091 if (FileHandle
) NtClose(FileHandle
);
1093 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath
.Buffer
);
1095 if (NT_SUCCESS(Status
))
1097 return (HANDLE
)FindDataHandle
;
1101 BaseSetLastNTError(Status
);
1102 return INVALID_HANDLE_VALUE
;
1112 FindNextStreamW(IN HANDLE hFindStream
,
1113 OUT LPVOID lpFindStreamData
)
1115 PFIND_DATA_HANDLE FindDataHandle
= (PFIND_DATA_HANDLE
)hFindStream
;
1116 PFIND_STREAM_DATA FindStreamData
;
1118 if (hFindStream
== NULL
|| hFindStream
== INVALID_HANDLE_VALUE
||
1119 FindDataHandle
->Type
!= FindStream
)
1121 SetLastError(ERROR_INVALID_HANDLE
);
1125 RtlEnterCriticalSection(&FindDataHandle
->Lock
);
1127 FindStreamData
= FindDataHandle
->u
.FindStreamData
;
1129 /* Select next stream if possible */
1130 if (FindStreamData
->CurrentInfo
->NextEntryOffset
!= 0)
1132 FindStreamData
->CurrentInfo
= (PFILE_STREAM_INFORMATION
)((ULONG_PTR
)FindStreamData
->CurrentInfo
+
1133 FindStreamData
->CurrentInfo
->NextEntryOffset
);
1135 /* Return the information */
1136 CopyStreamData(FindStreamData
, lpFindStreamData
);
1138 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1143 RtlLeaveCriticalSection(&FindDataHandle
->Lock
);
1145 SetLastError(ERROR_HANDLE_EOF
);