kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }