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