build_host_windows.c (15364B)
1 #include "env_build.h" 2 3 #ifndef WIN32_LEAN_AND_MEAN 4 #define WIN32_LEAN_AND_MEAN 5 #endif 6 #ifndef _WIN32_WINNT 7 #define _WIN32_WINNT 0x0601 8 #endif 9 #include <windows.h> 10 11 #include "dist_host.h" 12 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #define DRIVER_BUILD_FRAME_MAX 65536u 19 #define DRIVER_BUILD_PIPE_TIMEOUT_MS 10u 20 21 struct KitBuildListener { 22 char name[KIT_BUILD_PATH_MAX]; 23 HANDLE pipe; 24 HANDLE event; 25 OVERLAPPED ov; 26 int connected; 27 }; 28 29 struct KitBuildConn { 30 HANDLE h; 31 }; 32 33 static wchar_t* db_widen(const char* utf8) { 34 int need; 35 wchar_t* w; 36 if (!utf8 || !*utf8) return NULL; 37 need = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); 38 if (need <= 0) return NULL; 39 w = (wchar_t*)malloc((size_t)need * sizeof(*w)); 40 if (!w) return NULL; 41 if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, w, need) <= 0) { 42 free(w); 43 return NULL; 44 } 45 return w; 46 } 47 48 static char* db_narrow(const wchar_t* wide) { 49 int need; 50 char* out; 51 if (!wide) return NULL; 52 need = WideCharToMultiByte(CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL); 53 if (need <= 0) return NULL; 54 out = (char*)malloc((size_t)need); 55 if (!out) return NULL; 56 if (WideCharToMultiByte(CP_UTF8, 0, wide, -1, out, need, NULL, NULL) <= 0) { 57 free(out); 58 return NULL; 59 } 60 return out; 61 } 62 63 static char* db_slice_dup(DriverEnv* env, KitSlice s) { 64 char* out = (char*)driver_alloc(env, s.len + 1u); 65 if (!out) return NULL; 66 if (s.len) memcpy(out, s.s, s.len); 67 out[s.len] = '\0'; 68 return out; 69 } 70 71 static int db_path_copy(char* out, size_t cap, KitSlice s) { 72 if (!out || cap == 0u || !s.s || s.len + 1u > cap) return 1; 73 memcpy(out, s.s, s.len); 74 out[s.len] = '\0'; 75 return 0; 76 } 77 78 static int db_is_abs_path(const char* path) { 79 if (!path || !path[0]) return 0; 80 if ((path[0] == '/' || path[0] == '\\')) return 1; 81 return ((path[0] >= 'A' && path[0] <= 'Z') || 82 (path[0] >= 'a' && path[0] <= 'z')) && 83 path[1] == ':'; 84 } 85 86 static int db_store_rename(void* user, KitSlice from, KitSlice to) { 87 char f[KIT_BUILD_PATH_MAX], t[KIT_BUILD_PATH_MAX]; 88 wchar_t *wf, *wt; 89 BOOL ok; 90 (void)user; 91 if (db_path_copy(f, sizeof f, from) || db_path_copy(t, sizeof t, to)) 92 return 1; 93 wf = db_widen(f); 94 wt = db_widen(t); 95 if (!wf || !wt) { 96 free(wf); 97 free(wt); 98 return 1; 99 } 100 ok = MoveFileExW(wf, wt, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); 101 free(wf); 102 free(wt); 103 return ok ? 0 : 1; 104 } 105 106 static int db_store_remove(void* user, KitSlice path, int recursive) { 107 char p[KIT_BUILD_PATH_MAX]; 108 wchar_t* wp; 109 BOOL ok; 110 DWORD err; 111 (void)user; 112 if (db_path_copy(p, sizeof p, path)) return 1; 113 if (recursive) return driver_remove_tree(p); 114 wp = db_widen(p); 115 if (!wp) return 1; 116 ok = DeleteFileW(wp); 117 if (!ok) ok = RemoveDirectoryW(wp); 118 err = ok ? 0 : GetLastError(); 119 free(wp); 120 return ok || err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND ? 0 121 : 1; 122 } 123 124 static int db_store_make_temp_dir(void* user, KitSlice parent, char* out, 125 size_t cap) { 126 DriverBuildHost* bh = (DriverBuildHost*)user; 127 char p[KIT_BUILD_PATH_MAX]; 128 size_t i, n; 129 if (!bh || !bh->env || !out || cap == 0u || 130 db_path_copy(p, sizeof p, parent)) 131 return 1; 132 n = strlen(p); 133 for (i = 0; i < 128u; ++i) { 134 wchar_t* wout; 135 int len = snprintf(out, cap, "%s%stmp.%lu.%llu", p, 136 n && p[n - 1u] != '/' && p[n - 1u] != '\\' ? "\\" : "", 137 (unsigned long)GetCurrentProcessId(), 138 (unsigned long long)bh->next_endpoint++); 139 if (len < 0 || (size_t)len >= cap) return 1; 140 wout = db_widen(out); 141 if (!wout) return 1; 142 if (CreateDirectoryW(wout, NULL)) { 143 free(wout); 144 return 0; 145 } 146 if (GetLastError() != ERROR_ALREADY_EXISTS) { 147 free(wout); 148 return 1; 149 } 150 free(wout); 151 } 152 return 1; 153 } 154 155 static int db_store_sync_path(void* user, KitSlice path) { 156 char p[KIT_BUILD_PATH_MAX]; 157 wchar_t* wp; 158 HANDLE h; 159 int rc = 0; 160 (void)user; 161 if (db_path_copy(p, sizeof p, path)) return 1; 162 wp = db_widen(p); 163 if (!wp) return 1; 164 h = CreateFileW(wp, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | 165 FILE_SHARE_DELETE, 166 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 167 free(wp); 168 if (h == INVALID_HANDLE_VALUE) return 0; 169 if (!FlushFileBuffers(h)) rc = 1; 170 CloseHandle(h); 171 return rc; 172 } 173 174 static int db_store_list_dir(void* user, KitSlice path, KitBuildDirFn cb, 175 void* cb_user) { 176 char p[KIT_BUILD_PATH_MAX]; 177 char pattern[KIT_BUILD_PATH_MAX]; 178 wchar_t* wpattern; 179 WIN32_FIND_DATAW fd; 180 HANDLE h; 181 DWORD last; 182 (void)user; 183 if (!cb || db_path_copy(p, sizeof p, path)) return 1; 184 if ((size_t)snprintf(pattern, sizeof pattern, "%s\\*", p) >= 185 sizeof pattern) 186 return 1; 187 wpattern = db_widen(pattern); 188 if (!wpattern) return 1; 189 h = FindFirstFileW(wpattern, &fd); 190 free(wpattern); 191 if (h == INVALID_HANDLE_VALUE) { 192 last = GetLastError(); 193 return last == ERROR_FILE_NOT_FOUND ? 0 : 1; 194 } 195 for (;;) { 196 char* name = db_narrow(fd.cFileName); 197 KitSlice s; 198 if (!name) { 199 FindClose(h); 200 return 1; 201 } 202 if (driver_streq(name, ".") || driver_streq(name, "..")) { 203 free(name); 204 goto next; 205 } 206 s.s = name; 207 s.len = strlen(name); 208 if (cb(cb_user, s)) { 209 free(name); 210 FindClose(h); 211 return 1; 212 } 213 free(name); 214 next: 215 if (!FindNextFileW(h, &fd)) break; 216 } 217 last = GetLastError(); 218 FindClose(h); 219 return last == ERROR_NO_MORE_FILES ? 0 : 1; 220 } 221 222 static int db_pipe_start(KitBuildListener* l) { 223 wchar_t* wname; 224 BOOL ok; 225 DWORD err; 226 if (!l || l->pipe != INVALID_HANDLE_VALUE) return 1; 227 wname = db_widen(l->name); 228 if (!wname) return 1; 229 l->event = CreateEventW(NULL, TRUE, FALSE, NULL); 230 if (!l->event) { 231 free(wname); 232 return 1; 233 } 234 memset(&l->ov, 0, sizeof l->ov); 235 l->ov.hEvent = l->event; 236 l->connected = 0; 237 l->pipe = CreateNamedPipeW( 238 wname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 239 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 240 DRIVER_BUILD_FRAME_MAX + 4u, DRIVER_BUILD_FRAME_MAX + 4u, 0, NULL); 241 free(wname); 242 if (l->pipe == INVALID_HANDLE_VALUE) { 243 CloseHandle(l->event); 244 l->event = NULL; 245 return 1; 246 } 247 ok = ConnectNamedPipe(l->pipe, &l->ov); 248 err = ok ? 0 : GetLastError(); 249 if (ok) { 250 l->connected = 1; 251 SetEvent(l->event); 252 return 0; 253 } 254 if (err == ERROR_IO_PENDING) return 0; 255 if (err == ERROR_PIPE_CONNECTED) { 256 l->connected = 1; 257 SetEvent(l->event); 258 return 0; 259 } 260 CloseHandle(l->pipe); 261 CloseHandle(l->event); 262 l->pipe = INVALID_HANDLE_VALUE; 263 l->event = NULL; 264 return 1; 265 } 266 267 static int db_pipe_finish(DriverBuildHost* bh, KitBuildListener* l, 268 DWORD timeout, KitBuildConn** out, int* ready) { 269 DWORD r; 270 DWORD got = 0; 271 KitBuildConn* c; 272 if (!bh || !l || !out || !ready) return 1; 273 *out = NULL; 274 *ready = 0; 275 if (l->pipe == INVALID_HANDLE_VALUE && db_pipe_start(l) != 0) return 1; 276 r = WaitForSingleObject(l->event, timeout); 277 if (r == WAIT_TIMEOUT) return 0; 278 if (r != WAIT_OBJECT_0) return 1; 279 if (!l->connected && 280 !GetOverlappedResult(l->pipe, &l->ov, &got, FALSE)) 281 return 1; 282 c = (KitBuildConn*)driver_alloc_zeroed(bh->env, sizeof *c); 283 if (!c) return 1; 284 c->h = l->pipe; 285 l->pipe = INVALID_HANDLE_VALUE; 286 if (l->event) CloseHandle(l->event); 287 l->event = NULL; 288 memset(&l->ov, 0, sizeof l->ov); 289 l->connected = 0; 290 *out = c; 291 *ready = 1; 292 return 0; 293 } 294 295 static int db_transport_listen(void* user, char* name_out, size_t cap, 296 KitBuildListener** out) { 297 DriverBuildHost* bh = (DriverBuildHost*)user; 298 KitBuildListener* l; 299 int n; 300 if (!bh || !bh->env || !name_out || cap == 0u || !out) return 1; 301 *out = NULL; 302 l = (KitBuildListener*)driver_alloc_zeroed(bh->env, sizeof *l); 303 if (!l) return 1; 304 l->pipe = INVALID_HANDLE_VALUE; 305 n = snprintf(l->name, sizeof l->name, "\\\\.\\pipe\\kit-build-%lu-%llu", 306 (unsigned long)GetCurrentProcessId(), 307 (unsigned long long)bh->next_endpoint++); 308 if (n < 0 || (size_t)n >= sizeof l->name || (size_t)n + 1u > cap) 309 goto err; 310 if (db_pipe_start(l) != 0) goto err; 311 memcpy(name_out, l->name, (size_t)n + 1u); 312 *out = l; 313 return 0; 314 315 err: 316 if (l->pipe != INVALID_HANDLE_VALUE) CloseHandle(l->pipe); 317 if (l->event) CloseHandle(l->event); 318 driver_free(bh->env, l, sizeof *l); 319 return 1; 320 } 321 322 static int db_transport_accept(void* user, KitBuildListener* lst, 323 KitBuildConn** out) { 324 DriverBuildHost* bh = (DriverBuildHost*)user; 325 int ready = 0; 326 if (db_pipe_finish(bh, lst, INFINITE, out, &ready) != 0) return 1; 327 return ready ? 0 : 1; 328 } 329 330 static int db_transport_try_accept(void* user, KitBuildListener* lst, 331 KitBuildConn** out, int* ready) { 332 DriverBuildHost* bh = (DriverBuildHost*)user; 333 return db_pipe_finish(bh, lst, DRIVER_BUILD_PIPE_TIMEOUT_MS, out, ready); 334 } 335 336 static void db_transport_close_listener(void* user, KitBuildListener* lst) { 337 DriverBuildHost* bh = (DriverBuildHost*)user; 338 if (!bh || !lst) return; 339 if (lst->pipe != INVALID_HANDLE_VALUE) { 340 CancelIo(lst->pipe); 341 CloseHandle(lst->pipe); 342 } 343 if (lst->event) CloseHandle(lst->event); 344 driver_free(bh->env, lst, sizeof *lst); 345 } 346 347 static int db_transport_dial(void* user, KitSlice endpoint, KitBuildConn** out) { 348 DriverBuildHost* bh = (DriverBuildHost*)user; 349 char name[KIT_BUILD_PATH_MAX]; 350 wchar_t* wname; 351 HANDLE h = INVALID_HANDLE_VALUE; 352 KitBuildConn* c; 353 DWORD i; 354 if (!bh || !bh->env || !out || db_path_copy(name, sizeof name, endpoint)) 355 return 1; 356 *out = NULL; 357 wname = db_widen(name); 358 if (!wname) return 1; 359 for (i = 0; i < 500u; ++i) { 360 h = CreateFileW(wname, GENERIC_READ | GENERIC_WRITE, 0, NULL, 361 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 362 if (h != INVALID_HANDLE_VALUE) break; 363 if (GetLastError() == ERROR_PIPE_BUSY) { 364 WaitNamedPipeW(wname, 50u); 365 } else if (GetLastError() == ERROR_FILE_NOT_FOUND || 366 GetLastError() == ERROR_PATH_NOT_FOUND) { 367 Sleep(10u); 368 } else { 369 free(wname); 370 return 1; 371 } 372 } 373 free(wname); 374 if (h == INVALID_HANDLE_VALUE) return 1; 375 c = (KitBuildConn*)driver_alloc_zeroed(bh->env, sizeof *c); 376 if (!c) { 377 CloseHandle(h); 378 return 1; 379 } 380 c->h = h; 381 *out = c; 382 return 0; 383 } 384 385 static int db_pipe_transfer(HANDLE h, int write, uint8_t* buf, size_t n) { 386 size_t off = 0; 387 while (off < n) { 388 DWORD chunk = n - off > 0x40000000u ? 0x40000000u : (DWORD)(n - off); 389 DWORD done = 0; 390 OVERLAPPED ov; 391 HANDLE ev; 392 BOOL ok; 393 ev = CreateEventW(NULL, TRUE, FALSE, NULL); 394 if (!ev) return 1; 395 memset(&ov, 0, sizeof ov); 396 ov.hEvent = ev; 397 ok = write ? WriteFile(h, buf + off, chunk, &done, &ov) 398 : ReadFile(h, buf + off, chunk, &done, &ov); 399 if (!ok && GetLastError() == ERROR_IO_PENDING) 400 ok = GetOverlappedResult(h, &ov, &done, TRUE); 401 CloseHandle(ev); 402 if (!ok || done == 0u) return 1; 403 off += done; 404 } 405 return 0; 406 } 407 408 static int db_transport_read(void* user, KitBuildConn* conn, uint8_t* buf, 409 size_t cap, size_t* n) { 410 uint8_t hdr[4]; 411 uint32_t len; 412 (void)user; 413 if (!conn || !buf || !n) return 1; 414 if (db_pipe_transfer(conn->h, 0, hdr, sizeof hdr) != 0) return 1; 415 len = (uint32_t)hdr[0] | ((uint32_t)hdr[1] << 8) | 416 ((uint32_t)hdr[2] << 16) | ((uint32_t)hdr[3] << 24); 417 if (len > cap || len > DRIVER_BUILD_FRAME_MAX) return 1; 418 if (len && db_pipe_transfer(conn->h, 0, buf, len) != 0) return 1; 419 *n = len; 420 return 0; 421 } 422 423 static int db_transport_write(void* user, KitBuildConn* conn, 424 const uint8_t* buf, size_t n) { 425 uint8_t hdr[4]; 426 (void)user; 427 if (!conn || (!buf && n) || n > DRIVER_BUILD_FRAME_MAX) return 1; 428 hdr[0] = (uint8_t)(n & 0xffu); 429 hdr[1] = (uint8_t)((n >> 8) & 0xffu); 430 hdr[2] = (uint8_t)((n >> 16) & 0xffu); 431 hdr[3] = (uint8_t)((n >> 24) & 0xffu); 432 return db_pipe_transfer(conn->h, 1, hdr, sizeof hdr) || 433 (n ? db_pipe_transfer(conn->h, 1, (uint8_t*)buf, n) : 0); 434 } 435 436 static void db_transport_close(void* user, KitBuildConn* conn) { 437 DriverBuildHost* bh = (DriverBuildHost*)user; 438 if (!bh || !conn) return; 439 if (conn->h != INVALID_HANDLE_VALUE) { 440 DisconnectNamedPipe(conn->h); 441 CloseHandle(conn->h); 442 } 443 driver_free(bh->env, conn, sizeof *conn); 444 } 445 446 static int db_fetch_url(void* user, KitSlice url, KitSlice dest) { 447 DriverBuildHost* bh = (DriverBuildHost*)user; 448 char* url_s; 449 char* dest_s; 450 int rc; 451 if (!bh || !bh->env) return 1; 452 url_s = db_slice_dup(bh->env, url); 453 dest_s = db_slice_dup(bh->env, dest); 454 if (!url_s || !dest_s) { 455 if (url_s) driver_free(bh->env, url_s, strlen(url_s) + 1u); 456 if (dest_s) driver_free(bh->env, dest_s, strlen(dest_s) + 1u); 457 return 1; 458 } 459 rc = driver_fetch_url(url_s, dest_s); 460 driver_free(bh->env, url_s, strlen(url_s) + 1u); 461 driver_free(bh->env, dest_s, strlen(dest_s) + 1u); 462 return rc; 463 } 464 465 int driver_build_host_init(DriverBuildHost* out, DriverEnv* env) { 466 if (!out || !env) return 1; 467 memset(out, 0, sizeof *out); 468 out->env = env; 469 out->cas_host = driver_cas_host(env); 470 out->store_io.rename = db_store_rename; 471 out->store_io.remove = db_store_remove; 472 out->store_io.make_temp_dir = db_store_make_temp_dir; 473 out->store_io.sync_path = db_store_sync_path; 474 out->store_io.list_dir = db_store_list_dir; 475 out->store_io.user = out; 476 out->exec = driver_exec(env); 477 out->transport.listen = db_transport_listen; 478 out->transport.accept = db_transport_accept; 479 out->transport.try_accept = db_transport_try_accept; 480 out->transport.close_listener = db_transport_close_listener; 481 out->transport.dial = db_transport_dial; 482 out->transport.read_frame = db_transport_read; 483 out->transport.write_frame = db_transport_write; 484 out->transport.close = db_transport_close; 485 out->transport.user = out; 486 out->fetch.fetch_url = db_fetch_url; 487 out->fetch.user = out; 488 out->host.cas_host = &out->cas_host; 489 out->host.store_io = &out->store_io; 490 out->host.exec = &out->exec; 491 out->host.transport = &out->transport; 492 out->host.fetch = &out->fetch; 493 out->next_endpoint = 1; 494 return 0; 495 } 496 497 void driver_build_host_fini(DriverBuildHost* host) { (void)host; } 498 499 char* driver_build_host_abs_path(DriverEnv* env, const char* path, 500 size_t* out_size) { 501 wchar_t* wpath; 502 wchar_t* wbuf; 503 char* narrowed; 504 char* out; 505 DWORD need; 506 size_t size; 507 if (!env || !path) return NULL; 508 wpath = db_widen(path); 509 if (!wpath) return NULL; 510 need = GetFullPathNameW(wpath, 0, NULL, NULL); 511 if (need == 0) { 512 free(wpath); 513 return NULL; 514 } 515 wbuf = (wchar_t*)malloc((size_t)need * sizeof(*wbuf)); 516 if (!wbuf || GetFullPathNameW(wpath, need, wbuf, NULL) == 0) { 517 free(wbuf); 518 free(wpath); 519 return NULL; 520 } 521 free(wpath); 522 narrowed = db_narrow(wbuf); 523 free(wbuf); 524 if (!narrowed) return NULL; 525 size = strlen(narrowed) + 1u; 526 out = (char*)driver_alloc(env, size); 527 if (out) { 528 memcpy(out, narrowed, size); 529 if (out_size) *out_size = size; 530 } 531 free(narrowed); 532 return out; 533 }