mhdt_tmpfile.c (23910B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2026 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/tests/client_server/mhdt_tmpfile.c 41 * @brief MHD test framework temporary file helpers 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 45 #include "mhd_sys_options.h" 46 47 #include <stdint.h> 48 #include <string.h> 49 #include <stdlib.h> 50 #include <fcntl.h> 51 #include <errno.h> 52 53 #if !defined(_WIN32) || defined(__CYGWIN__) 54 # ifdef HAVE_UNISTD_H 55 # include <unistd.h> 56 # endif 57 # include <stdio.h> /* for P_tmpdir */ 58 #else 59 # define mhdt_USE_W32 1 60 # ifndef WIN32_LEAN_AND_MEAN 61 # define WIN32_LEAN_AND_MEAN 1 62 # endif 63 # include <windows.h> 64 # include <wchar.h> 65 # include <sys/types.h> 66 # include <sys/stat.h> 67 # include <io.h> 68 #endif 69 70 #include "mhdt_tmpfile.h" 71 72 #ifndef mhdt_ARR_CNT 73 # define mhdt_ARR_CNT(arr) (sizeof(arr)/sizeof(arr[0])) 74 #endif 75 76 #ifndef MHDT_TMP_FILE_BASENAME_PREFIX 77 # define MHDT_TMP_FILE_BASENAME_PREFIX "mhdt-tmp-" 78 #endif 79 80 /* The length of the unique file suffix */ 81 #ifndef MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN 82 # define MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN 6 83 #endif 84 85 #if MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN < 6 86 # error MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN is less than 6 87 #endif 88 89 /* Maximum length of a normalised temporary directory name, 90 including the trailing slash but excluding the terminating NUL. */ 91 #if !defined(MHDT_FILENAME_MAX) 92 # if defined(FILENAME_MAX) 93 # define MHDT_FILENAME_MAX FILENAME_MAX 94 # elif defined(MAX_PATH) 95 # define MHDT_FILENAME_MAX MAX_PATH 96 # endif 97 #endif 98 99 #if defined(MHDT_FILENAME_MAX) && (MHDT_FILENAME_MAX + 0) > 4096 100 # undef MHDT_FILENAME_MAX 101 #endif 102 103 #ifndef MHDT_FILENAME_MAX 104 # define MHDT_FILENAME_MAX 4096 105 #endif 106 107 #ifndef mhdt_MAX_TRIES 108 # define mhdt_MAX_TRIES 127 109 #endif 110 111 #define MHDTL_UINT32_MASK 0xFFFFFFFFu 112 113 114 static inline uint_least32_t 115 mhdtl_rotl32 (uint_least32_t val, 116 unsigned int rot) 117 { 118 uint_fast32_t v; 119 120 rot &= 31u; 121 v = ((uint_fast32_t)val) & MHDTL_UINT32_MASK; 122 123 return (uint_least32_t) 124 (((v << rot) 125 | (v >> ((32u - rot) & 31u))) 126 & MHDTL_UINT32_MASK); 127 } 128 129 130 /** 131 * Mix bits of two 32-bit values and produce a pseudo-random 32-bit value 132 * based on them. 133 * @param a the first value to mix 134 * @param b the second value to mix 135 * @return the pseudo-random value produced from the input values 136 */ 137 static inline uint_least32_t 138 mhdtl_mix32 (uint_fast32_t a, 139 uint_fast32_t b) 140 { 141 uint_fast32_t res; 142 143 /* Combine the inputs: multiplication by the Golden Ratio's fractional 144 * part diffuses the bits of 'a' before they meet the bits of 'b', 145 * while the rotation breaks the symmetry between the two inputs. */ 146 res = 147 ((((a & MHDTL_UINT32_MASK) * 0x9E3779B9u) & MHDTL_UINT32_MASK) 148 ^ mhdtl_rotl32 ((uint_least32_t)b, 13u)); 149 /* Re-mix bits better. 150 * The shifts and the multipliers are the MurmurHash3 32-bit finaliser. */ 151 res ^= res >> 16u; 152 res = (res * 0x85EBCA6Bu) & MHDTL_UINT32_MASK; 153 res ^= res >> 13u; 154 res = (res * 0xC2B2AE35u) & MHDTL_UINT32_MASK; 155 res ^= res >> 16u; 156 157 return (uint_least32_t)res; 158 } 159 160 161 #ifndef mhdt_USE_W32 162 163 /** 164 * Try to add the specified directory to the list of the directories used 165 * for temporary files. 166 * 167 * The candidate name is normalised before use: a trailing slash is appended 168 * if the name does not end with a slash already. 169 * The candidate is silently ignored if the name is NULL or empty, if 170 * the normalised name is too long to fit into the list entry, or if 171 * the normalised name duplicates any name already present in the list. 172 * The @p tmp_dirs array must have a free entry at the index 173 * @p *dirs_have_ptr. 174 * 175 * @param tmp_dir_name the name of the candidate directory, can be NULL 176 * @param[in,out] tmp_dirs the array of the names of the directories used 177 * for temporary files 178 * @param[in,out] dirs_have_ptr the pointer to the variable with the number 179 * of the used entries in the @p tmp_dirs array, 180 * the value is incremented if the candidate 181 * is added 182 */ 183 static void 184 tmpdirs_try_add (const char *tmp_dir_name, 185 char (*tmp_dirs)[MHDT_FILENAME_MAX + 1], 186 unsigned int *dirs_have_ptr) 187 { 188 unsigned int i; 189 char tmp_dir[MHDT_FILENAME_MAX + 1]; 190 size_t tmp_dir_name_len; 191 192 if (NULL == tmp_dir_name) 193 return; 194 tmp_dir_name_len = strlen (tmp_dir_name); 195 if (0u == tmp_dir_name_len) 196 return; 197 if (((size_t)MHDT_FILENAME_MAX) < tmp_dir_name_len) 198 return; /* String 'tmp_dir_name' is too long to use */ 199 200 memcpy (tmp_dir, 201 tmp_dir_name, 202 tmp_dir_name_len + 1); 203 204 if ('/' != tmp_dir[tmp_dir_name_len - 1u]) 205 { 206 if (((size_t)MHDT_FILENAME_MAX) == tmp_dir_name_len) 207 return; /* String 'tmp_dir_name' is too long to use */ 208 tmp_dir[tmp_dir_name_len++] = '/'; 209 tmp_dir[tmp_dir_name_len] = '\0'; 210 } 211 212 for (i = 0; i < *dirs_have_ptr; ++i) 213 if (0 == strcmp (tmp_dir, tmp_dirs[i])) 214 return; /* Found duplicate */ 215 216 memcpy (tmp_dirs[*dirs_have_ptr], tmp_dir, tmp_dir_name_len + 1u); 217 ++(*dirs_have_ptr); 218 } 219 220 221 /** 222 * Fill the buffer with the hexadecimal representation of the number. 223 * 224 * Exactly @p buf_size - 1 lower hexadecimal digits of the @p number are 225 * written (zero-padded), followed by the terminating NUL. 226 * @param buf_size the size of the @p buf buffer in characters, 227 * must not be zero 228 * @param[out] buf the buffer to fill 229 * @param number the number to convert 230 */ 231 static void 232 mhdt_num_to_hex (size_t buf_size, 233 char *buf, 234 uint_least32_t number) 235 { 236 static const char hex_map[] = "0123456789abcdef"; 237 size_t p = buf_size - 1u; 238 if (p > buf_size) 239 abort (); 240 buf[p] = '\0'; 241 while (--p < buf_size) 242 { 243 buf[p] = hex_map[number & 0xFu]; 244 number >>= 4u; 245 } 246 } 247 248 249 /** 250 * Get a directory for temporary files 251 * @param fallback_num the number of the fallback location, 252 * zero for the main location 253 * @return the temporary directory string (always ends with a slash, the length 254 * of the string is not greater than #MHDT_FILENAME_MAX), 255 * NULL if @p fallback_num is too high 256 */ 257 static const char * 258 mhdt_get_tmpfile_dir (unsigned int fallback_num) 259 { 260 /* 'volatile' does not provide thread-safety, but the stored values are 261 identical for every initialisation. 262 This simplification is considered acceptable for the testing code. */ 263 static volatile unsigned int dirs_have = 0u; 264 static char tmp_dirs[7][MHDT_FILENAME_MAX + 1] = 265 { "", "", "", "", "", "", "" }; 266 267 if (0u == dirs_have) 268 { 269 unsigned int dirs_found = 0u; 270 271 tmpdirs_try_add (getenv ("TMPDIR"), tmp_dirs, &dirs_found); 272 273 tmpdirs_try_add (getenv ("TMP"), tmp_dirs, &dirs_found); 274 275 tmpdirs_try_add (getenv ("TEMP"), tmp_dirs, &dirs_found); 276 277 tmpdirs_try_add (".", tmp_dirs, &dirs_found); 278 279 # ifdef P_tmpdir 280 tmpdirs_try_add (P_tmpdir, tmp_dirs, &dirs_found); 281 # endif 282 283 tmpdirs_try_add ("/tmp", tmp_dirs, &dirs_found); 284 285 if (sizeof(tmp_dirs) / sizeof(tmp_dirs[0]) < dirs_found) 286 abort (); 287 288 if (0u == dirs_found) 289 abort (); 290 291 dirs_have = dirs_found; 292 } 293 294 if (dirs_have <= fallback_num) 295 return NULL; 296 297 return tmp_dirs[fallback_num]; 298 } 299 300 301 /** 302 * Create a temporary file that is deleted automatically when the FD is 303 * closed. 304 * 305 * The file is opened for both reading and writing. 306 * The close-on-exec flag is set for the FD (if supported by the platform). 307 * 308 * The locations for temporary files are tried in the following order: 309 * + the directories set by the "TMPDIR", the "TMP" and the "TEMP" 310 * environment variables, 311 * + the current directory, 312 * + the directory specified by the P_tmpdir macro (if defined), 313 * + and the "/tmp" directory. 314 * The locations that are unset, too long or duplicated are skipped. 315 * 316 * @return the FD of the created file on success, 317 * -1 if the file cannot be created in any of the locations 318 */ 319 static int 320 mhdt_create_tmpfile_posix (void) 321 { 322 static const char basename_prfx[] = MHDT_TMP_FILE_BASENAME_PREFIX; 323 const char *tmp_dir; 324 unsigned int i; 325 326 for (i = 0u; NULL != (tmp_dir = mhdt_get_tmpfile_dir (i)); ++i) 327 { 328 char tmp_filename[MHDT_FILENAME_MAX 329 + mhdt_ARR_CNT (basename_prfx) 330 + MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN]; 331 int tmpfile_fd; 332 int need_cloexec; 333 int need_unlink; 334 size_t tmp_dir_len; 335 336 tmp_dir_len = strlen (tmp_dir); 337 memcpy (tmp_filename, tmp_dir, tmp_dir_len); 338 memcpy (tmp_filename + tmp_dir_len, 339 basename_prfx, 340 mhdt_ARR_CNT (basename_prfx) - 1u); 341 memset (tmp_filename + tmp_dir_len + mhdt_ARR_CNT (basename_prfx) - 1u, 342 'X', 343 MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN); 344 tmp_filename[tmp_dir_len + mhdt_ARR_CNT (basename_prfx) - 1u 345 + MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN] = '\0'; 346 347 tmpfile_fd = -1; 348 need_cloexec = !0; 349 need_unlink = !0; 350 351 # ifdef O_TMPFILE 352 if (0 > tmpfile_fd) 353 { 354 int flags; 355 flags = O_RDWR | O_TMPFILE; 356 # ifdef O_EXCL 357 flags |= O_EXCL; 358 # else 359 /* The missing O_EXCL support cannot be replaced with anything. 360 Open file without O_EXCL as this is only the testing code. */ 361 # endif 362 # ifdef O_CLOEXEC 363 flags |= O_CLOEXEC; 364 # endif 365 # if defined(O_CLOFORK) && !defined(__linux__) 366 flags |= O_CLOFORK; 367 # endif 368 369 tmpfile_fd = open (tmp_dir, 370 flags, 371 0600); 372 if (0 <= tmpfile_fd) 373 { 374 need_unlink = 0; 375 # ifdef O_CLOEXEC 376 need_cloexec = 0; 377 # endif 378 } 379 } 380 # endif 381 382 # ifdef HAVE_MKOSTEMP 383 if (0 > tmpfile_fd) 384 { 385 int flags; 386 flags = 0; 387 388 # ifdef O_CLOEXEC 389 flags |= O_CLOEXEC; 390 # endif 391 # if defined(O_CLOFORK) && !defined(__linux__) 392 flags |= O_CLOFORK; 393 # endif 394 tmpfile_fd = mkostemp (tmp_filename, 395 flags); 396 # ifdef O_CLOEXEC 397 if (0 <= tmpfile_fd) 398 need_cloexec = 0; 399 # endif 400 } 401 # elif defined(HAVE_MKSTEMP) 402 if (0 > tmpfile_fd) 403 tmpfile_fd = mkstemp (tmp_filename); 404 # endif 405 406 /* Last resort */ 407 if (0 > tmpfile_fd) 408 { 409 static volatile unsigned int cnt = 0u; 410 int flags; 411 int t; 412 uint_least32_t suff_num; 413 414 # if defined(HAVE_MKOSTEMP) || defined(HAVE_MKSTEMP) 415 /* Restore the filename template in case it was broken */ 416 memcpy (tmp_filename, tmp_dir, tmp_dir_len); 417 memcpy (tmp_filename + tmp_dir_len, 418 basename_prfx, 419 mhdt_ARR_CNT (basename_prfx) - 1u); 420 # endif /* HAVE_MKOSTEMP || HAVE_MKSTEMP */ 421 422 flags = O_CREAT | O_RDWR; 423 /* Last resort: do not try O_CLOFORK as it may be unsupported */ 424 # ifdef O_EXCL 425 flags |= O_EXCL; 426 # else 427 /* The missing O_EXCL support cannot be replaced with anything. 428 Open file without O_EXCL as this is only the testing code. */ 429 # endif 430 # ifdef O_CLOEXEC 431 flags |= O_CLOEXEC; 432 # endif 433 suff_num = mhdtl_mix32 ((uint_least32_t)getpid (), 434 (uint_least32_t)mhd_PTR_TO_INT (tmp_filename)); 435 for (t = 0; mhdt_MAX_TRIES > t; ++t) 436 { 437 char *suff_ptr; 438 suff_num = mhdtl_mix32 (suff_num, (uint_least32_t)cnt++); 439 suff_ptr = 440 tmp_filename + tmp_dir_len + mhdt_ARR_CNT (basename_prfx) - 1u; 441 442 mhdt_num_to_hex (MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN + 1u, 443 suff_ptr, 444 suff_num); 445 446 tmpfile_fd = open (tmp_filename, 447 flags, 448 0600); 449 if (0 <= tmpfile_fd) 450 { 451 # ifdef O_CLOEXEC 452 need_cloexec = 0; 453 # endif 454 break; 455 } 456 else if ((EEXIST != errno) && (EINTR != errno)) 457 break; /* Avoid useless retries */ 458 } 459 } 460 461 if (0 > tmpfile_fd) 462 continue; /* Try next directory */ 463 464 /* Best effort to set CLOEXEC */ 465 # ifdef FD_CLOEXEC 466 if (need_cloexec) 467 { 468 int flags; 469 470 flags = fcntl (tmpfile_fd, 471 F_GETFD); 472 if (0 <= flags) 473 { 474 flags |= FD_CLOEXEC; 475 # if defined(FD_CLOFORK) 476 if (0 == fcntl (tmpfile_fd, 477 F_SETFD, 478 flags | FD_CLOFORK)) 479 (void)0; /* Success. Do nothing. */ 480 else 481 # endif 482 if (!0) 483 { /* FD_CLOFORK may be unsupported */ 484 int res; 485 res = fcntl (tmpfile_fd, 486 F_SETFD, 487 flags); 488 (void)res; /* Ignore result */ 489 } 490 } 491 } 492 # else 493 (void)need_cloexec; /* Use without CLOEXEC */ 494 # endif 495 496 if (need_unlink) 497 { 498 if (0 != unlink (tmp_filename)) 499 { 500 int res; 501 /* The opened file cannot be unlinked. */ 502 res = close (tmpfile_fd); 503 (void)res; /* Ignore result */ 504 res = unlink (tmp_filename); /* Retry the removal with closed FD */ 505 (void)res; /* Ignore result */ 506 continue; /* Try next directory */ 507 } 508 } 509 510 return tmpfile_fd; 511 } 512 513 return -1; 514 } 515 516 517 #else /* W32 */ 518 519 /** 520 * Add a temporary-directory candidate to the list. 521 * 522 * The candidate is normalised in place to end with a backslash. It is ignored 523 * if NULL, empty, too long, or an exact duplicate of an existing entry. 524 * @param tmp_dir_name_len length of @p tmp_dir_name, excluding the NUL 525 * @param[in,out] tmp_dir_name writable NUL-terminated buffer of 526 * #MHDT_FILENAME_MAX + 1 characters 527 * @param[in,out] tmp_dirs destination array with a free entry at 528 * @p *dirs_have_ptr 529 * @param[in,out] dirs_have_ptr number of used entries, incremented on addition 530 */ 531 static void 532 tmpdirs_try_add_w32 (unsigned long tmp_dir_name_len, 533 wchar_t tmp_dir_name[MHDT_FILENAME_MAX + 1], 534 wchar_t (*tmp_dirs)[MHDT_FILENAME_MAX + 1], 535 unsigned int *dirs_have_ptr) 536 { 537 unsigned int i; 538 size_t tmp_dir_name_len_sz; 539 540 if (NULL == tmp_dir_name) 541 return; 542 if (0u == tmp_dir_name_len) 543 return; 544 if (((unsigned long)MHDT_FILENAME_MAX) < tmp_dir_name_len) 545 return; /* String 'tmp_dir_name' cannot fit the buffer */ 546 tmp_dir_name_len_sz = (size_t)tmp_dir_name_len; 547 548 549 if (L'/' == tmp_dir_name[tmp_dir_name_len_sz - 1u]) 550 tmp_dir_name[tmp_dir_name_len_sz - 1u] = L'\\'; 551 552 if (L'\\' != tmp_dir_name[tmp_dir_name_len_sz - 1u]) 553 { 554 if (((size_t)MHDT_FILENAME_MAX) == tmp_dir_name_len_sz) 555 return; /* String 'tmp_dir_name' is too long to use */ 556 /* This converts a drive-relative path "C:" to the root dir "C:\" which is 557 the desired effect (a drive-relative path is a bad choice). */ 558 tmp_dir_name[tmp_dir_name_len_sz++] = L'\\'; 559 tmp_dir_name[tmp_dir_name_len_sz] = L'\0'; 560 } 561 562 /* Simplified naive deduplication: not all slashes are normalised and 563 characters are not case-folded. 564 The filesystems on W32 can be case-sensitive (even with a per-directory 565 attribute). */ 566 for (i = 0; i < *dirs_have_ptr; ++i) 567 if (0 == wcscmp (tmp_dir_name, tmp_dirs[i])) 568 return; /* Found duplicate */ 569 570 wmemcpy (tmp_dirs[*dirs_have_ptr], 571 tmp_dir_name, 572 tmp_dir_name_len_sz + 1u); 573 ++(*dirs_have_ptr); 574 } 575 576 577 /** 578 * Get a directory for temporary files 579 * @param fallback_num the number of the fallback location, 580 * zero for the main location 581 * @return the temporary directory string (always ends with a backslash, 582 * the length of the string is not greater than #MHDT_FILENAME_MAX), 583 * NULL if @p fallback_num is too high 584 */ 585 static const wchar_t * 586 mhdt_get_tmpfile_dir_w32 (unsigned int fallback_num) 587 { 588 /* 'volatile' does not provide thread-safety, but the stored values are 589 identical for every initialisation. 590 This simplification is considered acceptable for the testing code. */ 591 static volatile unsigned int dirs_have = 0u; 592 static wchar_t tmp_dirs[6][MHDT_FILENAME_MAX + 1] = 593 { L"", L"", L"", L"", L"", L"" }; 594 wchar_t var_buf[MHDT_FILENAME_MAX + 1]; 595 const unsigned int var_buf_size = (sizeof(var_buf) / sizeof(var_buf[0])); 596 597 if (0u == dirs_have) 598 { 599 unsigned int dirs_found = 0u; 600 601 tmpdirs_try_add_w32 (GetEnvironmentVariableW (L"TMP", 602 var_buf, 603 var_buf_size), 604 var_buf, 605 tmp_dirs, 606 &dirs_found); 607 608 tmpdirs_try_add_w32 (GetEnvironmentVariableW (L"TEMP", 609 var_buf, 610 var_buf_size), 611 var_buf, 612 tmp_dirs, 613 &dirs_found); 614 615 wmemcpy (var_buf, L".\\", 2u + 1u); 616 tmpdirs_try_add_w32 (2u, 617 var_buf, 618 tmp_dirs, 619 &dirs_found); 620 621 tmpdirs_try_add_w32 (GetEnvironmentVariableW (L"USERPROFILE", 622 var_buf, 623 var_buf_size), 624 var_buf, 625 tmp_dirs, 626 &dirs_found); 627 628 /* This is mostly a duplication of one of the previous values */ 629 tmpdirs_try_add_w32 (GetTempPathW (var_buf_size, 630 var_buf), 631 var_buf, 632 tmp_dirs, 633 &dirs_found); 634 635 if (sizeof(tmp_dirs) / sizeof(tmp_dirs[0]) < dirs_found) 636 abort (); 637 638 if (0u == dirs_found) 639 abort (); 640 641 dirs_have = dirs_found; 642 } 643 644 if (dirs_have <= fallback_num) 645 return NULL; 646 647 return tmp_dirs[fallback_num]; 648 } 649 650 651 /** 652 * Fill the buffer with the hexadecimal representation of the number. 653 * 654 * Exactly @p buf_size - 1 lower hexadecimal digits of the @p number are 655 * written (zero-padded), followed by the terminating NUL. 656 * @param buf_size the size of the @p buf buffer in characters, 657 * must not be zero 658 * @param[out] buf the buffer to fill 659 * @param number the number to convert 660 */ 661 static void 662 mhdt_num_to_hex_w (size_t buf_size, 663 wchar_t *buf, 664 uint_least32_t number) 665 { 666 static const wchar_t hex_map[] = L"0123456789abcdef"; 667 size_t p = buf_size - 1u; 668 if (p > buf_size) 669 abort (); 670 buf[p] = L'\0'; 671 while (--p < buf_size) 672 { 673 buf[p] = hex_map[number & 0xFu]; 674 number >>= 4u; 675 } 676 } 677 678 679 # define mhdt_WSTR_(str) L ## str 680 # define mhdt_WSTR(str) mhdt_WSTR_(str) 681 682 # define MHDT_TMP_FILE_BASENAME_PREFIX_L\ 683 mhdt_WSTR(MHDT_TMP_FILE_BASENAME_PREFIX) 684 685 /** 686 * Create a temporary file that is deleted automatically when the FD is 687 * closed. 688 * 689 * The file is opened for both reading and writing in binary mode. 690 * The FD is not inherited by child processes. 691 * 692 * The locations for temporary files are tried in the following order: 693 * + the directories set by the "TMP" and the "TEMP" environment variables, 694 * + the current directory, 695 * + the directory set by "USERPROFILE", 696 * + and the system default directory for temporary files. 697 * The locations that are unset, too long or duplicated are skipped. 698 * 699 * @return the FD of the created file on success, 700 * -1 if the file cannot be created in any of the locations 701 */ 702 static int 703 mhdt_create_tmpfile_w32 (void) 704 { 705 static const wchar_t basename_prfx[] = MHDT_TMP_FILE_BASENAME_PREFIX_L; 706 const wchar_t *tmp_dir; 707 unsigned int i; 708 709 for (i = 0u; NULL != (tmp_dir = mhdt_get_tmpfile_dir_w32 (i)); ++i) 710 { 711 wchar_t tmp_filename[MHDT_FILENAME_MAX 712 + mhdt_ARR_CNT (basename_prfx) 713 + MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN]; 714 int tmpfile_fd; 715 size_t tmp_dir_len; 716 static volatile unsigned int cnt = 0u; 717 int t; 718 int dir_access_checked; 719 uint_least32_t suff_num; 720 721 tmp_dir_len = wcslen (tmp_dir); 722 wmemcpy (tmp_filename, 723 tmp_dir, 724 tmp_dir_len); 725 wmemcpy (tmp_filename + tmp_dir_len, 726 basename_prfx, 727 mhdt_ARR_CNT (basename_prfx)); 728 729 tmpfile_fd = -1; 730 dir_access_checked = 0; 731 732 suff_num = mhdtl_mix32 ((uint_least32_t)GetCurrentProcessId (), 733 (uint_least32_t)mhd_PTR_TO_INT (tmp_filename)); 734 for (t = 0; mhdt_MAX_TRIES > t; ++t) 735 { 736 wchar_t *suff_ptr; 737 suff_num = mhdtl_mix32 (suff_num, 738 (uint_least32_t)cnt++); 739 suff_ptr = tmp_filename + tmp_dir_len + mhdt_ARR_CNT (basename_prfx) - 1u; 740 741 mhdt_num_to_hex_w (MHDT_TMP_FILE_UNIQUE_SUFFIX_LEN + 1u, 742 suff_ptr, 743 suff_num); 744 745 tmpfile_fd = _wopen (tmp_filename, 746 _O_CREAT | _O_EXCL | _O_RDWR | _O_BINARY 747 | _O_TEMPORARY | _O_SHORT_LIVED | _O_NOINHERIT, 748 _S_IREAD | _S_IWRITE); 749 if (0 <= tmpfile_fd) 750 break; 751 if (EEXIST == errno) 752 continue; /* Filename collision, try another filename */ 753 if (EACCES == errno) 754 { 755 HANDLE h; 756 if (dir_access_checked) 757 continue; 758 759 h = CreateFileW (tmp_dir, 760 FILE_ADD_FILE, 761 FILE_SHARE_READ | FILE_SHARE_WRITE 762 | FILE_SHARE_DELETE, 763 NULL, 764 OPEN_EXISTING, 765 FILE_FLAG_BACKUP_SEMANTICS, /* Required to open a directory */ 766 NULL); 767 if (INVALID_HANDLE_VALUE == h) 768 break; /* Directory not accessible, try next directory */ 769 (void)CloseHandle (h); 770 dir_access_checked = !0; 771 continue; /* Conflict with a file being deleted, try another filename */ 772 } 773 break; /* Avoid useless retries */ 774 } 775 776 if (0 > tmpfile_fd) 777 continue; /* Try next directory */ 778 779 return tmpfile_fd; 780 } 781 782 return -1; 783 } 784 785 786 #endif /* W32 */ 787 788 int 789 MHDT_create_tmpfile (void) 790 { 791 #ifndef mhdt_USE_W32 792 return mhdt_create_tmpfile_posix (); 793 #else 794 return mhdt_create_tmpfile_w32 (); 795 #endif 796 }