| 129 | } |
| 130 | |
| 131 | char *cbm_mkdtemp(char *tmpl) { |
| 132 | /* Per-call storage is required: daemon sessions invoke mkdtemp concurrently. |
| 133 | * A process-global buffer lets one request overwrite another request's path |
| 134 | * between expansion, creation, and the copy back to its caller. */ |
| 135 | char buf[CBM_SZ_512]; |
| 136 | int written; |
| 137 | if (strncmp(tmpl, "/tmp/", 5) == 0) { |
| 138 | const char *tmp = getenv("TEMP"); |
| 139 | if (!tmp) |
| 140 | tmp = getenv("TMP"); |
| 141 | if (!tmp) |
| 142 | tmp = "."; |
| 143 | written = snprintf(buf, sizeof(buf), "%s\\%s", tmp, tmpl + 5); |
| 144 | } else { |
| 145 | written = snprintf(buf, sizeof(buf), "%s", tmpl); |
| 146 | } |
| 147 | if (written < 0 || (size_t)written >= sizeof(buf)) { |
| 148 | errno = ENAMETOOLONG; |
| 149 | return NULL; |
| 150 | } |
| 151 | |
| 152 | size_t length = strlen(buf); |
| 153 | if (length < 6 || strcmp(buf + length - 6, "XXXXXX") != 0) { |
| 154 | errno = EINVAL; |
| 155 | return NULL; |
| 156 | } |
| 157 | |
| 158 | static const char alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
| 159 | bool created = false; |
| 160 | for (int attempt = 0; attempt < 128; attempt++) { |
| 161 | unsigned char random_suffix[6]; |
| 162 | if (!cbm_secure_random(random_suffix, sizeof(random_suffix))) { |
| 163 | errno = EIO; |
| 164 | return NULL; |
| 165 | } |
| 166 | for (size_t index = 0; index < sizeof(random_suffix); index++) { |
| 167 | buf[length - sizeof(random_suffix) + index] = |
| 168 | alphabet[random_suffix[index] % (sizeof(alphabet) - 1)]; |
| 169 | } |
| 170 | |
| 171 | if (win_mkdtemp_private_create(buf)) { |
| 172 | created = true; |
| 173 | break; |
| 174 | } |
| 175 | |
| 176 | /* Keep the existing compatibility fallback when an explicit private |
| 177 | * descriptor is unavailable. A name collision is retried; any other |
| 178 | * filesystem refusal is returned to the caller immediately. */ |
| 179 | DWORD create_error = GetLastError(); |
| 180 | wchar_t *wide_directory = cbm_utf8_to_wide(buf); |
| 181 | errno = 0; |
| 182 | int mkdir_result = wide_directory ? _wmkdir(wide_directory) : -1; |
| 183 | int mkdir_error = errno; |
| 184 | free(wide_directory); |
| 185 | if (mkdir_result == 0) { |
| 186 | static volatile LONG fallback_reported; |
| 187 | if (InterlockedCompareExchange(&fallback_reported, 1, 0) == 0) { |
| 188 | (void)fprintf(stderr, |