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