xref: /reactos/sdk/lib/ucrt/filesystem/findfile.cpp (revision fe11f7a2)
1 /***
2 *findfile.c - C find file functions
3 *
4 *       Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *       Defines _findfirst(), _findnext(), and _findclose().
8 *
9 *******************************************************************************/
10 
11 #include <corecrt_internal.h>
12 #include <errno.h>
13 #include <corecrt_internal_time.h>
14 #include <io.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <corecrt_internal_win32_buffer.h>
19 
20 
21 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22 //
23 // Utilities for working with the different file type and time data types
24 //
25 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26 template <typename CrtTime>
27 static CrtTime __cdecl convert_system_time_to_time_t(SYSTEMTIME const& st) throw();
28 
29 template <>
30 __time32_t __cdecl convert_system_time_to_time_t(SYSTEMTIME const& st) throw()
31 {
32     return __loctotime32_t(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, -1);
33 }
34 
35 template <>
36 __time64_t __cdecl convert_system_time_to_time_t(SYSTEMTIME const& st) throw()
37 {
38     return __loctotime64_t(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, -1);
39 }
40 
41 
42 
43 template <typename CrtTime>
44 static CrtTime __cdecl convert_file_time_to_time_t(FILETIME const& ft) throw()
45 {
46     // A FILETIME of 0 becomes a time_t of -1:
47     if (ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0)
48         return static_cast<CrtTime>(-1);
49 
50     SYSTEMTIME st_utc;
51     if (!FileTimeToSystemTime(&ft, &st_utc))
52         return static_cast<CrtTime>(-1);
53 
54     SYSTEMTIME st_local;
55     if (!SystemTimeToTzSpecificLocalTime(nullptr, &st_utc, &st_local))
56         return static_cast<CrtTime>(-1);
57 
58     return convert_system_time_to_time_t<CrtTime>(st_local);
59 }
60 
61 
62 
63 template <typename Integer>
64 static Integer convert_file_size_to_integer(DWORD const high, DWORD const low) throw();
65 
66 template <>
67 __int64 convert_file_size_to_integer(DWORD const high, DWORD const low) throw()
68 {
69     return static_cast<__int64>(high) * 0x100000000ll + static_cast<__int64>(low);
70 }
71 
72 template <>
73 unsigned long convert_file_size_to_integer(DWORD const high, DWORD const low) throw()
74 {
75     UNREFERENCED_PARAMETER(high);
76     return low;
77 }
78 
79 
80 
81 template <typename WideFileData, typename NarrowFileData>
82 _Success_(return)
83 static bool __cdecl copy_wide_to_narrow_find_data(WideFileData const& wfd, _Out_ NarrowFileData& fd, unsigned int const code_page) throw()
84 {
85     __crt_internal_win32_buffer<char> name;
86 
87     errno_t const cvt = __acrt_wcs_to_mbs_cp(wfd.name, name, code_page);
88 
89     if (cvt != 0) {
90         return false;
91     }
92 
93     _ERRCHECK(strcpy_s(fd.name, _countof(fd.name), name.data()));
94 
95     fd.attrib       = wfd.attrib;
96     fd.time_create  = wfd.time_create;
97     fd.time_access  = wfd.time_access;
98     fd.time_write   = wfd.time_write;
99     fd.size         = wfd.size;
100 
101     return true;
102 }
103 
104 
105 
106 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
107 //
108 // The _findfirst family of functions
109 //
110 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
111 // These functions find the first file matching the given wildcard pattern. If a
112 // file is found, information about that file is stored in the pointed-to result
113 // parameter.  The return value is a handle that identifies the group of files
114 // that match the pattern.  If no file is found, or if an error occurs, errno is
115 // set and -1 is returned.
116 //
117 // There are eight functions in this family, combining {wide name, narrow name}
118 // x {32-bit file size, 64-bit file size} x {32-bit time_t, 64-bit time_t}.
119 template <typename WideFileData>
120 _Success_(return != -1)
121 static intptr_t __cdecl common_find_first_wide(wchar_t const* const pattern, _Out_ WideFileData* const result) throw()
122 {
123     _VALIDATE_RETURN(result  != nullptr, EINVAL, -1);
124     _VALIDATE_RETURN(pattern != nullptr, EINVAL, -1);
125 
126     // Ensure the underlying WIN32_FIND_DATA's file name buffer is not larger
127     // than ours.
128     static_assert(sizeof(WideFileData().name) <= sizeof(WIN32_FIND_DATAW().cFileName), "");
129 
130     WIN32_FIND_DATAW wfd;
131     HANDLE const hFile = FindFirstFileExW(pattern, FindExInfoStandard, &wfd, FindExSearchNameMatch, nullptr, 0);
132     if (hFile == INVALID_HANDLE_VALUE)
133     {
134         DWORD const os_error = GetLastError();
135         switch (os_error)
136         {
137         case ERROR_NO_MORE_FILES:
138         case ERROR_FILE_NOT_FOUND:
139         case ERROR_PATH_NOT_FOUND:
140             errno = ENOENT;
141             break;
142 
143         case ERROR_NOT_ENOUGH_MEMORY:
144             errno = ENOMEM;
145             break;
146 
147         default:
148             errno = EINVAL;
149             break;
150         }
151 
152         return -1;
153     }
154 
155     result->attrib = wfd.dwFileAttributes == FILE_ATTRIBUTE_NORMAL
156         ? 0
157         : wfd.dwFileAttributes;
158 
159     typedef decltype(result->time_create) crt_time_type;
160     result->time_create = convert_file_time_to_time_t<crt_time_type>(wfd.ftCreationTime);
161     result->time_access = convert_file_time_to_time_t<crt_time_type>(wfd.ftLastAccessTime);
162     result->time_write  = convert_file_time_to_time_t<crt_time_type>(wfd.ftLastWriteTime);
163 
164     typedef decltype(result->size) file_size_type;
165     result->size = convert_file_size_to_integer<file_size_type>(wfd.nFileSizeHigh, wfd.nFileSizeLow);
166 
167     _ERRCHECK(wcscpy_s(result->name, _countof(result->name), wfd.cFileName));
168 
169     return reinterpret_cast<intptr_t>(hFile);
170 }
171 
172 template <typename WideFileData, typename NarrowFileData>
173 _Success_(return != -1)
174 static intptr_t __cdecl common_find_first_narrow(char const* const pattern, _Out_ NarrowFileData* const result, unsigned int const code_page) throw()
175 {
176     _VALIDATE_RETURN(result != nullptr, EINVAL, -1);
177 
178     __crt_internal_win32_buffer<wchar_t> wide_pattern;
179 
180     errno_t const cvt = __acrt_mbs_to_wcs_cp(pattern, wide_pattern, code_page);
181 
182     if (cvt != 0) {
183         return -1;
184     }
185 
186     WideFileData wide_result;
187     intptr_t const handle = common_find_first_wide(wide_pattern.data(), &wide_result);
188     if (handle == -1)
189         return -1;
190 
191     if (!copy_wide_to_narrow_find_data(wide_result, *result, code_page))
192         return -1;
193 
194     return handle;
195 }
196 
197 // Narrow name, 32-bit time_t, 32-bit size
198 extern "C" intptr_t __cdecl _findfirst32(char const* const pattern, _finddata32_t* const result)
199 {
200     return common_find_first_narrow<_wfinddata32_t>(pattern, result, __acrt_get_utf8_acp_compatibility_codepage());
201 }
202 
203 // Narrow name, 32-bit time_t, 64-bit size
204 extern "C" intptr_t __cdecl _findfirst32i64(char const* const pattern, _finddata32i64_t* const result)
205 {
206     return common_find_first_narrow<_wfinddata32i64_t>(pattern, result, __acrt_get_utf8_acp_compatibility_codepage());
207 }
208 
209 // Narrow name, 64-bit time_t, 32-bit size
210 extern "C" intptr_t __cdecl _findfirst64i32(char const* const pattern, _finddata64i32_t* const result)
211 {
212     return common_find_first_narrow<_wfinddata64i32_t>(pattern, result, __acrt_get_utf8_acp_compatibility_codepage());
213 }
214 
215 // Narrow name, 64-bit time_t, 64-bit size
216 extern "C" intptr_t __cdecl _findfirst64(char const* const pattern, __finddata64_t* const result)
217 {
218     return common_find_first_narrow<_wfinddata64_t>(pattern, result, __acrt_get_utf8_acp_compatibility_codepage());
219 }
220 
221 // Wide name, 32-bit time_t, 32-bit size
222 extern "C" intptr_t __cdecl _wfindfirst32(wchar_t const* const pattern, _wfinddata32_t* const result)
223 {
224     return common_find_first_wide(pattern, result);
225 }
226 
227 // Wide name, 32-bit time_t, 64-bit size
228 extern "C" intptr_t __cdecl _wfindfirst32i64(wchar_t const* const pattern, _wfinddata32i64_t* const result)
229 {
230     return common_find_first_wide(pattern, result);
231 }
232 
233 // Wide name, 64-bit time_t, 32-bit size
234 extern "C" intptr_t __cdecl _wfindfirst64i32(wchar_t const* const pattern, _wfinddata64i32_t* const result)
235 {
236     return common_find_first_wide(pattern, result);
237 }
238 
239 // Wide name, 64-bit time_t, 64-bit size
240 extern "C" intptr_t __cdecl _wfindfirst64(wchar_t const* const pattern, _wfinddata64_t* const result)
241 {
242     return common_find_first_wide(pattern, result);
243 }
244 
245 
246 
247 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248 //
249 // The _findnext family of functions
250 //
251 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
252 // These functions perform iteration over a set of files matching a wildcard
253 // pattern.  The handle argument must be a handle returned by a previous call to
254 // one of the _findfirst functions that completed successfully.  Each call to a
255 // _findnext function advances the internal iterator and returns information
256 // about the next file in the set.
257 //
258 // If iteration has not yet completed and a file is found, information about that
259 // file is stored in the pointed-to result parameter.  The return value is a
260 // handle that identifies the group of files that match the pattern.  If no file
261 // is found, or if an error occurs, errno is set and -1 is returned.
262 //
263 // There are eight functions in this family, combining {wide name, narrow name}
264 // x {32-bit file size, 64-bit file size} x {32-bit time_t, 64-bit time_t}.
265 template <typename WideFileData>
266 static int __cdecl common_find_next_wide(intptr_t const handle, WideFileData* const result) throw()
267 {
268     HANDLE const os_handle = reinterpret_cast<HANDLE>(handle);
269 
270     _VALIDATE_RETURN(os_handle != 0                   , EINVAL, -1);
271     _VALIDATE_RETURN(os_handle != INVALID_HANDLE_VALUE, EINVAL, -1);
272     _VALIDATE_RETURN(result != nullptr,                 EINVAL, -1);
273 
274     WIN32_FIND_DATAW wfd;
275     if (!FindNextFileW(os_handle, &wfd))
276     {
277         DWORD const os_error = GetLastError();
278         switch (os_error)
279         {
280         case ERROR_NO_MORE_FILES:
281         case ERROR_FILE_NOT_FOUND:
282         case ERROR_PATH_NOT_FOUND:
283             errno = ENOENT;
284             break;
285 
286         case ERROR_NOT_ENOUGH_MEMORY:
287             errno = ENOMEM;
288             break;
289 
290         default:
291             errno = EINVAL;
292             break;
293         }
294 
295         return -1;
296     }
297 
298     result->attrib = wfd.dwFileAttributes == FILE_ATTRIBUTE_NORMAL
299         ? 0
300         : wfd.dwFileAttributes;
301 
302     typedef decltype(result->time_create) crt_time_type;
303     result->time_create = convert_file_time_to_time_t<crt_time_type>(wfd.ftCreationTime);
304     result->time_access = convert_file_time_to_time_t<crt_time_type>(wfd.ftLastAccessTime);
305     result->time_write  = convert_file_time_to_time_t<crt_time_type>(wfd.ftLastWriteTime);
306 
307     typedef decltype(result->size) file_size_type;
308     result->size = convert_file_size_to_integer<file_size_type>(wfd.nFileSizeHigh, wfd.nFileSizeLow);
309 
310     _ERRCHECK(wcscpy_s(result->name, _countof(result->name), wfd.cFileName));
311 
312     return 0;
313 }
314 
315 template <typename WideFileData, typename NarrowFileData>
316 static int __cdecl common_find_next_narrow(intptr_t const pattern, NarrowFileData* const result, unsigned int const code_page) throw()
317 {
318     WideFileData wide_result;
319     int const return_value = common_find_next_wide(pattern, &wide_result);
320     if (return_value == -1)
321         return -1;
322 
323     if (!copy_wide_to_narrow_find_data(wide_result, *result, code_page))
324         return -1;
325 
326     return return_value;
327 }
328 
329 // Narrow name, 32-bit time_t, 32-bit size
330 extern "C" int __cdecl _findnext32(intptr_t const handle, _finddata32_t* const result)
331 {
332     return common_find_next_narrow<_wfinddata32_t>(handle, result, __acrt_get_utf8_acp_compatibility_codepage());
333 }
334 
335 // Narrow name, 32-bit time_t, 64-bit size
336 extern "C" int __cdecl _findnext32i64(intptr_t const handle, _finddata32i64_t* const result)
337 {
338     return common_find_next_narrow<_wfinddata32i64_t>(handle, result, __acrt_get_utf8_acp_compatibility_codepage());
339 }
340 
341 // Narrow name, 64-bit time_t, 32-bit size
342 extern "C" int __cdecl _findnext64i32(intptr_t const handle, _finddata64i32_t* const result)
343 {
344     return common_find_next_narrow<_wfinddata64i32_t>(handle, result, __acrt_get_utf8_acp_compatibility_codepage());
345 }
346 
347 // Narrow name, 64-bit time_t, 64-bit size
348 extern "C" int __cdecl _findnext64(intptr_t const handle, __finddata64_t* const result)
349 {
350     return common_find_next_narrow<_wfinddata64_t>(handle, result, __acrt_get_utf8_acp_compatibility_codepage());
351 }
352 
353 // Wide name, 32-bit time_t, 32-bit size
354 extern "C" int __cdecl _wfindnext32(intptr_t const handle, _wfinddata32_t* const result)
355 {
356     return common_find_next_wide(handle, result);
357 }
358 
359 // Wide name, 32-bit time_t, 64-bit size
360 extern "C" int __cdecl _wfindnext32i64(intptr_t const handle, _wfinddata32i64_t* const result)
361 {
362     return common_find_next_wide(handle, result);
363 }
364 
365 // Wide name, 64-bit time_t, 32-bit size
366 extern "C" int __cdecl _wfindnext64i32(intptr_t const handle, _wfinddata64i32_t* const result)
367 {
368     return common_find_next_wide(handle, result);
369 }
370 
371 // Wide name, 64-bit time_t, 64-bit size
372 extern "C" int __cdecl _wfindnext64(intptr_t const handle, _wfinddata64_t* const result)
373 {
374     return common_find_next_wide(handle, result);
375 }
376 
377 
378 
379 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 //
381 // The _findclose function
382 //
383 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 // This function releases resources associated with a _findfirst/_findnext
385 // iteration.  It must be called exactly once for each handle returned by
386 // _findfirst.  Returns 0 on success; -1 on failure.
387 extern "C" int __cdecl _findclose(intptr_t const handle)
388 {
389     if (!FindClose(reinterpret_cast<HANDLE>(handle)))
390     {
391         errno = EINVAL;
392         return -1;
393     }
394     return 0;
395 }
396