| 870 | } |
| 871 | |
| 872 | static int yaml_write_atomic(const char *path, const char *data, size_t len, |
| 873 | const char *expected_data, size_t expected_len, |
| 874 | const yaml_file_snapshot_t *expected_snapshot) { |
| 875 | size_t path_len = 0U; |
| 876 | if (yaml_bounded_strlen(path, YAML_OUTPUT_MAX, &path_len) != 0 || |
| 877 | path_len > SIZE_MAX - YAML_TMP_SUFFIX_MAX - YAML_UNIT) { |
| 878 | return YAML_ERROR; |
| 879 | } |
| 880 | size_t capacity = path_len + YAML_TMP_SUFFIX_MAX + YAML_UNIT; |
| 881 | char *temp_path = (char *)malloc(capacity); |
| 882 | if (!temp_path) { |
| 883 | return YAML_ERROR; |
| 884 | } |
| 885 | |
| 886 | FILE *file = NULL; |
| 887 | for (unsigned attempt = 0U; attempt < YAML_TMP_ATTEMPTS; attempt++) { |
| 888 | unsigned sequence = |
| 889 | atomic_fetch_add_explicit(&yaml_temp_sequence, YAML_UNIT, memory_order_relaxed); |
| 890 | int written = snprintf(temp_path, capacity, "%s.cbm-yaml-%ld-%u.tmp", path, |
| 891 | (long)YAML_PROCESS_ID(), sequence); |
| 892 | if (written < 0 || (size_t)written >= capacity) { |
| 893 | free(temp_path); |
| 894 | return YAML_ERROR; |
| 895 | } |
| 896 | errno = 0; |
| 897 | #ifdef _WIN32 |
| 898 | file = cbm_fopen(temp_path, "wbx"); |
| 899 | #else |
| 900 | int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW; |
| 901 | #ifdef O_CLOEXEC |
| 902 | flags |= O_CLOEXEC; |
| 903 | #endif |
| 904 | int descriptor = open(temp_path, flags, YAML_NEW_FILE_MODE); |
| 905 | if (descriptor >= 0) { |
| 906 | file = fdopen(descriptor, "wb"); |
| 907 | if (!file) { |
| 908 | (void)close(descriptor); |
| 909 | (void)cbm_unlink(temp_path); |
| 910 | free(temp_path); |
| 911 | return YAML_ERROR; |
| 912 | } |
| 913 | } |
| 914 | #endif |
| 915 | if (file) { |
| 916 | break; |
| 917 | } |
| 918 | if (errno != EEXIST) { |
| 919 | free(temp_path); |
| 920 | return YAML_ERROR; |
| 921 | } |
| 922 | } |
| 923 | if (!file) { |
| 924 | free(temp_path); |
| 925 | return YAML_ERROR; |
| 926 | } |
| 927 | |
| 928 | bool failed = fwrite(data, YAML_UNIT, len, file) != len; |
| 929 | if (!failed && fflush(file) != 0) { |
no test coverage detected