Reimplemented FindFirstFileEx and removed some overhead.
[reactos.git] / reactos / lib / kernel32 / file / find.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/find.c
6 * PURPOSE: Find functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <k32.h>
15
16 #define NDEBUG
17 #include "../include/debug.h"
18
19
20 /* TYPES ********************************************************************/
21
22 #ifndef offsetof
23 #define offsetof(TYPE, MEMBER) ((size_t) &( ((TYPE *) 0)->MEMBER ))
24 #endif
25
26 #define FIND_DATA_SIZE (16*1024)
27
28 typedef struct _KERNEL32_FIND_FILE_DATA
29 {
30 HANDLE DirectoryHandle;
31 BOOLEAN DirectoryOnly;
32 PFILE_BOTH_DIR_INFORMATION pFileInfo;
33 } KERNEL32_FIND_FILE_DATA, *PKERNEL32_FIND_FILE_DATA;
34
35
36 /* FUNCTIONS ****************************************************************/
37
38 VOID
39 InternalCopyFindDataW(LPWIN32_FIND_DATAW lpFindFileData,
40 PFILE_BOTH_DIR_INFORMATION lpFileInfo)
41 {
42 lpFindFileData->dwFileAttributes = lpFileInfo->FileAttributes;
43
44 lpFindFileData->ftCreationTime.dwHighDateTime = lpFileInfo->CreationTime.u.HighPart;
45 lpFindFileData->ftCreationTime.dwLowDateTime = lpFileInfo->CreationTime.u.LowPart;
46
47 lpFindFileData->ftLastAccessTime.dwHighDateTime = lpFileInfo->LastAccessTime.u.HighPart;
48 lpFindFileData->ftLastAccessTime.dwLowDateTime = lpFileInfo->LastAccessTime.u.LowPart;
49
50 lpFindFileData->ftLastWriteTime.dwHighDateTime = lpFileInfo->LastWriteTime.u.HighPart;
51 lpFindFileData->ftLastWriteTime.dwLowDateTime = lpFileInfo->LastWriteTime.u.LowPart;
52
53 lpFindFileData->nFileSizeHigh = lpFileInfo->EndOfFile.u.HighPart;
54 lpFindFileData->nFileSizeLow = lpFileInfo->EndOfFile.u.LowPart;
55
56 memcpy (lpFindFileData->cFileName, lpFileInfo->FileName, lpFileInfo->FileNameLength);
57 lpFindFileData->cFileName[lpFileInfo->FileNameLength / sizeof(WCHAR)] = 0;
58
59 memcpy (lpFindFileData->cAlternateFileName, lpFileInfo->ShortName, lpFileInfo->ShortNameLength);
60 lpFindFileData->cAlternateFileName[lpFileInfo->ShortNameLength / sizeof(WCHAR)] = 0;
61 }
62
63 VOID
64 InternalCopyFindDataA(LPWIN32_FIND_DATAA lpFindFileData,
65 PFILE_BOTH_DIR_INFORMATION lpFileInfo)
66 {
67 UNICODE_STRING FileNameU;
68 ANSI_STRING FileNameA;
69
70 lpFindFileData->dwFileAttributes = lpFileInfo->FileAttributes;
71
72 lpFindFileData->ftCreationTime.dwHighDateTime = lpFileInfo->CreationTime.u.HighPart;
73 lpFindFileData->ftCreationTime.dwLowDateTime = lpFileInfo->CreationTime.u.LowPart;
74
75 lpFindFileData->ftLastAccessTime.dwHighDateTime = lpFileInfo->LastAccessTime.u.HighPart;
76 lpFindFileData->ftLastAccessTime.dwLowDateTime = lpFileInfo->LastAccessTime.u.LowPart;
77
78 lpFindFileData->ftLastWriteTime.dwHighDateTime = lpFileInfo->LastWriteTime.u.HighPart;
79 lpFindFileData->ftLastWriteTime.dwLowDateTime = lpFileInfo->LastWriteTime.u.LowPart;
80
81 lpFindFileData->nFileSizeHigh = lpFileInfo->EndOfFile.u.HighPart;
82 lpFindFileData->nFileSizeLow = lpFileInfo->EndOfFile.u.LowPart;
83
84 FileNameU.Length = FileNameU.MaximumLength = lpFileInfo->FileNameLength;
85 FileNameU.Buffer = lpFileInfo->FileName;
86
87 FileNameA.MaximumLength = sizeof(lpFindFileData->cFileName) - sizeof(CHAR);
88 FileNameA.Buffer = lpFindFileData->cFileName;
89
90 /* convert unicode string to ansi (or oem) */
91 if (bIsFileApiAnsi)
92 RtlUnicodeStringToAnsiString (&FileNameA, &FileNameU, FALSE);
93 else
94 RtlUnicodeStringToOemString (&FileNameA, &FileNameU, FALSE);
95
96 FileNameA.Buffer[FileNameA.Length] = 0;
97
98 DPRINT("lpFileInfo->ShortNameLength %d\n", lpFileInfo->ShortNameLength);
99
100 FileNameU.Length = FileNameU.MaximumLength = lpFileInfo->ShortNameLength;
101 FileNameU.Buffer = lpFileInfo->ShortName;
102
103 FileNameA.MaximumLength = sizeof(lpFindFileData->cAlternateFileName) - sizeof(CHAR);
104 FileNameA.Buffer = lpFindFileData->cAlternateFileName;
105
106 /* convert unicode string to ansi (or oem) */
107 if (bIsFileApiAnsi)
108 RtlUnicodeStringToAnsiString (&FileNameA, &FileNameU, FALSE);
109 else
110 RtlUnicodeStringToOemString (&FileNameA, &FileNameU, FALSE);
111
112 FileNameA.Buffer[FileNameA.Length] = 0;
113 }
114
115 /*
116 * @implemented
117 */
118 BOOL
119 STDCALL
120 InternalFindNextFile (
121 HANDLE hFindFile,
122 PUNICODE_STRING SearchPattern
123 )
124 {
125 PKERNEL32_FIND_FILE_DATA IData;
126 IO_STATUS_BLOCK IoStatusBlock;
127 NTSTATUS Status;
128
129 DPRINT("InternalFindNextFile(%lx)\n", hFindFile);
130
131 IData = (PKERNEL32_FIND_FILE_DATA)hFindFile;
132
133 while (1)
134 {
135 if (IData->pFileInfo->NextEntryOffset != 0)
136 {
137 IData->pFileInfo = (PVOID)((ULONG_PTR)IData->pFileInfo + IData->pFileInfo->NextEntryOffset);
138 }
139 else
140 {
141 IData->pFileInfo = (PVOID)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA));
142 IData->pFileInfo->FileIndex = 0;
143 Status = NtQueryDirectoryFile (IData->DirectoryHandle,
144 NULL,
145 NULL,
146 NULL,
147 &IoStatusBlock,
148 (PVOID)IData->pFileInfo,
149 FIND_DATA_SIZE,
150 FileBothDirectoryInformation,
151 SearchPattern ? TRUE : FALSE,
152 SearchPattern,
153 SearchPattern ? TRUE : FALSE);
154 SearchPattern = NULL;
155 if (!NT_SUCCESS(Status))
156 {
157 SetLastErrorByStatus (Status);
158 return FALSE;
159 }
160 }
161 if (!IData->DirectoryOnly || IData->pFileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
162 {
163 DPRINT("Found %.*S\n",IData->pFileInfo->FileNameLength/sizeof(WCHAR), IData->pFileInfo->FileName);
164 return TRUE;
165 }
166 }
167 }
168
169
170 /*
171 * @implemented
172 */
173 HANDLE
174 STDCALL
175 InternalFindFirstFile (
176 LPCWSTR lpFileName,
177 BOOLEAN DirectoryOnly
178 )
179 {
180 OBJECT_ATTRIBUTES ObjectAttributes;
181 PKERNEL32_FIND_FILE_DATA IData;
182 IO_STATUS_BLOCK IoStatusBlock;
183 UNICODE_STRING NtPathU;
184 UNICODE_STRING PatternStr = RTL_CONSTANT_STRING(L"*");
185 NTSTATUS Status;
186 PWSTR e1, e2;
187 WCHAR CurrentDir[256];
188 PWCHAR SlashlessFileName;
189 PWSTR SearchPath;
190 PWCHAR SearchPattern;
191 ULONG Length;
192 BOOLEAN bResult;
193
194 DPRINT("FindFirstFileW(lpFileName %S)\n",
195 lpFileName);
196
197 Length = wcslen(lpFileName);
198 if (L'\\' == lpFileName[Length - 1])
199 {
200 SlashlessFileName = RtlAllocateHeap(hProcessHeap,
201 0,
202 Length * sizeof(WCHAR));
203 if (NULL == SlashlessFileName)
204 {
205 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
206 return NULL;
207 }
208 memcpy(SlashlessFileName, lpFileName, (Length - 1) * sizeof(WCHAR));
209 SlashlessFileName[Length - 1] = L'\0';
210 lpFileName = SlashlessFileName;
211 }
212 else
213 {
214 SlashlessFileName = NULL;
215 }
216
217 e1 = wcsrchr(lpFileName, L'/');
218 e2 = wcsrchr(lpFileName, L'\\');
219 SearchPattern = max(e1, e2);
220 SearchPath = CurrentDir;
221
222 if (NULL == SearchPattern)
223 {
224 CHECKPOINT;
225 SearchPattern = (PWCHAR)lpFileName;
226 Length = GetCurrentDirectoryW(sizeof(CurrentDir) / sizeof(WCHAR), SearchPath);
227 if (0 == Length)
228 {
229 if (NULL != SlashlessFileName)
230 {
231 RtlFreeHeap(hProcessHeap,
232 0,
233 SlashlessFileName);
234 }
235 return NULL;
236 }
237 if (Length > sizeof(CurrentDir) / sizeof(WCHAR))
238 {
239 SearchPath = RtlAllocateHeap(hProcessHeap,
240 HEAP_ZERO_MEMORY,
241 Length * sizeof(WCHAR));
242 if (NULL == SearchPath)
243 {
244 if (NULL != SlashlessFileName)
245 {
246 RtlFreeHeap(hProcessHeap,
247 0,
248 SlashlessFileName);
249 }
250 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
251 return NULL;
252 }
253 GetCurrentDirectoryW(Length, SearchPath);
254 }
255 }
256 else
257 {
258 CHECKPOINT;
259 SearchPattern++;
260 Length = SearchPattern - lpFileName;
261 if (Length + 1 > sizeof(CurrentDir) / sizeof(WCHAR))
262 {
263 SearchPath = RtlAllocateHeap(hProcessHeap,
264 HEAP_ZERO_MEMORY,
265 (Length + 1) * sizeof(WCHAR));
266 if (NULL == SearchPath)
267 {
268 if (NULL != SlashlessFileName)
269 {
270 RtlFreeHeap(hProcessHeap,
271 0,
272 SlashlessFileName);
273 }
274 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
275 return NULL;
276 }
277 }
278 memcpy(SearchPath, lpFileName, Length * sizeof(WCHAR));
279 SearchPath[Length] = 0;
280 }
281
282 bResult = RtlDosPathNameToNtPathName_U ((LPWSTR)SearchPath,
283 &NtPathU,
284 NULL,
285 NULL);
286 if (SearchPath != CurrentDir)
287 {
288 RtlFreeHeap(hProcessHeap,
289 0,
290 SearchPath);
291 }
292 if (FALSE == bResult)
293 {
294 if (NULL != SlashlessFileName)
295 {
296 RtlFreeHeap(hProcessHeap,
297 0,
298 SlashlessFileName);
299 }
300 return NULL;
301 }
302
303 DPRINT("NtPathU \'%S\'\n", NtPathU.Buffer);
304
305 IData = RtlAllocateHeap (hProcessHeap,
306 HEAP_ZERO_MEMORY,
307 sizeof(KERNEL32_FIND_FILE_DATA) + FIND_DATA_SIZE);
308 if (NULL == IData)
309 {
310 RtlFreeHeap (hProcessHeap,
311 0,
312 NtPathU.Buffer);
313 if (NULL != SlashlessFileName)
314 {
315 RtlFreeHeap(hProcessHeap,
316 0,
317 SlashlessFileName);
318 }
319 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
320 return NULL;
321 }
322
323 /* change pattern: "*.*" --> "*" */
324 if (wcscmp (SearchPattern, L"*.*"))
325 {
326 RtlInitUnicodeString(&PatternStr, SearchPattern);
327 }
328
329 DPRINT("NtPathU \'%S\' Pattern \'%S\'\n",
330 NtPathU.Buffer, PatternStr.Buffer);
331
332 InitializeObjectAttributes (&ObjectAttributes,
333 &NtPathU,
334 0,
335 NULL,
336 NULL);
337
338 Status = NtOpenFile (&IData->DirectoryHandle,
339 FILE_LIST_DIRECTORY,
340 &ObjectAttributes,
341 &IoStatusBlock,
342 FILE_SHARE_READ|FILE_SHARE_WRITE,
343 FILE_DIRECTORY_FILE);
344
345 RtlFreeHeap (hProcessHeap,
346 0,
347 NtPathU.Buffer);
348
349 if (!NT_SUCCESS(Status))
350 {
351 RtlFreeHeap (hProcessHeap, 0, IData);
352 if (NULL != SlashlessFileName)
353 {
354 RtlFreeHeap(hProcessHeap,
355 0,
356 SlashlessFileName);
357 }
358 SetLastErrorByStatus (Status);
359 return(NULL);
360 }
361 IData->pFileInfo = (PVOID)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA));
362 IData->pFileInfo->FileIndex = 0;
363 IData->DirectoryOnly = DirectoryOnly;
364
365 bResult = InternalFindNextFile((HANDLE)IData, &PatternStr);
366 if (NULL != SlashlessFileName)
367 {
368 RtlFreeHeap(hProcessHeap,
369 0,
370 SlashlessFileName);
371 }
372
373 if (!bResult)
374 {
375 FindClose((HANDLE)IData);
376 return NULL;
377 }
378
379 return IData;
380 }
381
382
383 /*
384 * @implemented
385 */
386 HANDLE
387 STDCALL
388 FindFirstFileA (
389 LPCSTR lpFileName,
390 LPWIN32_FIND_DATAA lpFindFileData
391 )
392 {
393 PKERNEL32_FIND_FILE_DATA IData;
394 UNICODE_STRING FileNameU;
395 ANSI_STRING FileName;
396
397 RtlInitAnsiString (&FileName,
398 (LPSTR)lpFileName);
399
400 /* convert ansi (or oem) string to unicode */
401 if (bIsFileApiAnsi)
402 RtlAnsiStringToUnicodeString (&FileNameU,
403 &FileName,
404 TRUE);
405 else
406 RtlOemStringToUnicodeString (&FileNameU,
407 &FileName,
408 TRUE);
409
410 IData = InternalFindFirstFile (FileNameU.Buffer, FALSE);
411
412 RtlFreeUnicodeString (&FileNameU);
413
414 if (IData == NULL)
415 {
416 DPRINT("Failing request\n");
417 return INVALID_HANDLE_VALUE;
418 }
419
420 DPRINT("IData->pFileInfo->FileNameLength %d\n",
421 IData->pFileInfo->FileNameLength);
422
423 /* copy data into WIN32_FIND_DATA structure */
424 InternalCopyFindDataA(lpFindFileData, IData->pFileInfo);
425
426
427 return (HANDLE)IData;
428 }
429
430
431 /*
432 * @implemented
433 */
434 BOOL
435 STDCALL
436 FindNextFileA (
437 HANDLE hFindFile,
438 LPWIN32_FIND_DATAA lpFindFileData)
439 {
440 PKERNEL32_FIND_FILE_DATA IData;
441
442 if (hFindFile == INVALID_HANDLE_VALUE)
443 {
444 SetLastError (ERROR_INVALID_HANDLE);
445 DPRINT("Failing request\n");
446 return FALSE;
447 }
448
449 IData = (PKERNEL32_FIND_FILE_DATA)hFindFile;
450 if (!InternalFindNextFile (hFindFile, NULL))
451 {
452 DPRINT("InternalFindNextFile() failed\n");
453 return FALSE;
454 }
455
456 DPRINT("IData->pFileInfo->FileNameLength %d\n",
457 IData->pFileInfo->FileNameLength);
458
459 /* copy data into WIN32_FIND_DATA structure */
460 InternalCopyFindDataA(lpFindFileData, IData->pFileInfo);
461
462 return TRUE;
463 }
464
465
466 /*
467 * @implemented
468 */
469 BOOL
470 STDCALL
471 FindClose (
472 HANDLE hFindFile
473 )
474 {
475 PKERNEL32_FIND_FILE_DATA IData;
476
477 DPRINT("FindClose(hFindFile %x)\n",hFindFile);
478
479 if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
480 {
481 SetLastError (ERROR_INVALID_HANDLE);
482 return FALSE;
483 }
484
485 IData = (PKERNEL32_FIND_FILE_DATA)hFindFile;
486
487 CloseHandle (IData->DirectoryHandle);
488 RtlFreeHeap (hProcessHeap, 0, IData);
489
490 return TRUE;
491 }
492
493
494 /*
495 * @implemented
496 */
497 HANDLE
498 STDCALL
499 FindFirstFileW (
500 LPCWSTR lpFileName,
501 LPWIN32_FIND_DATAW lpFindFileData
502 )
503 {
504
505 return FindFirstFileExW (lpFileName,
506 FindExInfoStandard,
507 (LPVOID)lpFindFileData,
508 FindExSearchNameMatch,
509 NULL,
510 0);
511 }
512
513 /*
514 * @implemented
515 */
516 BOOL
517 STDCALL
518 FindNextFileW (
519 HANDLE hFindFile,
520 LPWIN32_FIND_DATAW lpFindFileData
521 )
522 {
523 PKERNEL32_FIND_FILE_DATA IData;
524
525 if (hFindFile == INVALID_HANDLE_VALUE)
526 {
527 SetLastError (ERROR_INVALID_HANDLE);
528 DPRINT("Failing request\n");
529 return FALSE;
530 }
531
532 IData = (PKERNEL32_FIND_FILE_DATA)hFindFile;
533 if (!InternalFindNextFile(hFindFile, NULL))
534 {
535 DPRINT("Failing request\n");
536 return FALSE;
537 }
538
539 /* copy data into WIN32_FIND_DATA structure */
540 InternalCopyFindDataW(lpFindFileData, IData->pFileInfo);
541
542 return TRUE;
543 }
544
545
546 /*
547 * @unimplemented
548 */
549 HANDLE
550 STDCALL
551 FindFirstFileExW (LPCWSTR lpFileName,
552 FINDEX_INFO_LEVELS fInfoLevelId,
553 LPVOID lpFindFileData,
554 FINDEX_SEARCH_OPS fSearchOp,
555 LPVOID lpSearchFilter,
556 DWORD dwAdditionalFlags)
557 {
558 PKERNEL32_FIND_FILE_DATA IData;
559
560 if (fInfoLevelId != FindExInfoStandard)
561 {
562 SetLastError(ERROR_INVALID_PARAMETER);
563 return INVALID_HANDLE_VALUE;
564 }
565 if (fSearchOp == FindExSearchNameMatch || fSearchOp == FindExSearchLimitToDirectories)
566 {
567 if (lpSearchFilter)
568 {
569 SetLastError(ERROR_INVALID_PARAMETER);
570 return INVALID_HANDLE_VALUE;
571 }
572
573 IData = InternalFindFirstFile (lpFileName, fSearchOp == FindExSearchLimitToDirectories ? TRUE : FALSE);
574 if (IData == NULL)
575 {
576 DPRINT("Failing request\n");
577 return INVALID_HANDLE_VALUE;
578 }
579
580 /* copy data into WIN32_FIND_DATA structure */
581 InternalCopyFindDataW((LPWIN32_FIND_DATAW)lpFindFileData, IData->pFileInfo);
582
583 return (HANDLE)IData;
584 }
585 SetLastError(ERROR_INVALID_PARAMETER);
586 return INVALID_HANDLE_VALUE;
587 }
588
589 /*
590 * @unimplemented
591 */
592 HANDLE
593 STDCALL
594 FindFirstFileExA (
595 LPCSTR lpFileName,
596 FINDEX_INFO_LEVELS fInfoLevelId,
597 LPVOID lpFindFileData,
598 FINDEX_SEARCH_OPS fSearchOp,
599 LPVOID lpSearchFilter,
600 DWORD dwAdditionalFlags
601 )
602 {
603 PKERNEL32_FIND_FILE_DATA IData;
604 UNICODE_STRING FileNameU;
605 ANSI_STRING FileNameA;
606
607 if (fInfoLevelId != FindExInfoStandard)
608 {
609 SetLastError(ERROR_INVALID_PARAMETER);
610 return INVALID_HANDLE_VALUE;
611 }
612 if (fSearchOp == FindExSearchNameMatch || fSearchOp == FindExSearchLimitToDirectories)
613 {
614 if (lpSearchFilter)
615 {
616 SetLastError(ERROR_INVALID_PARAMETER);
617 return INVALID_HANDLE_VALUE;
618 }
619
620 RtlInitAnsiString (&FileNameA, (LPSTR)lpFileName);
621
622 /* convert ansi (or oem) string to unicode */
623 if (bIsFileApiAnsi)
624 RtlAnsiStringToUnicodeString (&FileNameU, &FileNameA, TRUE);
625 else
626 RtlOemStringToUnicodeString (&FileNameU, &FileNameA, TRUE);
627
628 IData = InternalFindFirstFile (FileNameU.Buffer, FALSE);
629
630 RtlFreeUnicodeString (&FileNameU);
631
632 if (IData == NULL)
633 {
634 DPRINT("Failing request\n");
635 return INVALID_HANDLE_VALUE;
636 }
637
638 /* copy data into WIN32_FIND_DATA structure */
639 InternalCopyFindDataA(lpFindFileData, IData->pFileInfo);
640 }
641 SetLastError(ERROR_INVALID_PARAMETER);
642 return INVALID_HANDLE_VALUE;
643 }
644
645
646 /* EOF */