x86-64: delete fp_regs_32 from asm-x86.inc - this is not really
[ajla.git] / os_win32.c
blobacf41f26da912ca2d5f54e16414f96e4e13e96f9
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #ifdef OS_WIN32
23 #include "str.h"
24 #include "obj_reg.h"
25 #include "addrlock.h"
26 #include "thread.h"
27 #include "timer.h"
28 #include "os_util.h"
30 #include "os.h"
31 #include "iomux.h"
33 #include <stdio.h>
34 #include <time.h>
37 #define WIN32_BUFFER_SIZE 4096
38 #define WIN32_LINE_BUFFER_SIZE 4096
39 #define WIN32_IO_THREAD_STACK_SIZE 4096
41 #define SOCKADDR_MAX_LEN 512
42 #define SOCKADDR_ALIGN 16
44 #define CONNECT_TIMEOUT 500000
46 struct win32_io_thread {
47 handle_t h;
48 HANDLE thread;
49 char *buffer;
50 unsigned buffer_pos;
51 unsigned buffer_len;
52 char *line_buffer;
53 unsigned line_buffer_pos;
54 unsigned line_buffer_size;
55 DWORD err;
56 bool line_buffer_eof;
57 bool eof;
58 bool should_close;
59 bool need_terminate;
60 HANDLE data_event;
61 HANDLE startup_event;
62 HANDLE terminate_mutex;
63 HANDLE terminate_event;
64 atomic_type uchar_efficient_t is_packet_console;
65 bool packet_is_queued;
66 struct console_read_packet packet;
67 unsigned last_buttons;
70 struct win32_handle {
71 HANDLE h;
72 SOCKET s;
73 DWORD type;
74 int flags;
75 bool is_console;
76 bool is_overlapped;
78 int tc_flags;
80 char utf8_buffer[5];
81 uint8_t utf8_buffer_size;
83 struct list wait_list[2];
85 struct win32_io_thread *rd;
86 struct win32_io_thread *wr;
88 bool connect_in_progress;
89 struct list socket_entry[2];
91 struct list deferred_entry;
93 char file_name[FLEXIBLE_ARRAY];
96 struct fdx_set {
97 u_int fd_count;
98 SOCKET fd_array[1];
102 #include "os_com.inc"
105 static HMODULE handle_iphlpa;
107 static LPCSTR (WINAPI *fn_GetEnvironmentStrings)(void);
108 static LPCSTR (WINAPI *fn_GetEnvironmentStringsA)(void);
109 static LPCWSTR (WINAPI *fn_GetEnvironmentStringsW)(void);
110 static BOOL (WINAPI *fn_FreeEnvironmentStringsA)(LPCSTR env);
111 static BOOL (WINAPI *fn_FreeEnvironmentStringsW)(LPCWSTR env);
112 static LONG (NTAPI *fn_RtlGetVersion)(POSVERSIONINFOW lpVersionInformation);
113 static VOID (NTAPI *fn_RtlFreeUserThreadStack)(HANDLE ProcessHandle, HANDLE ThreadHandle);
114 static BOOL (WINAPI *fn_GetDiskFreeSpaceExA)(LPCSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes);
115 static BOOL (WINAPI *fn_GetDiskFreeSpaceExW)(LPWSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes);
116 static BOOL (WINAPI *fn_CancelIo)(HANDLE hFile);
117 static BOOL (WINAPI *fn_CancelIoEx)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
118 static BOOL (WINAPI *fn_MoveFileExA)(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags);
119 static BOOL (WINAPI *fn_MoveFileExW)(LPWSTR lpExistingFileName, LPWSTR lpNewFileName, DWORD dwFlags);
120 static BOOL (WINAPI *fn_FlushInstructionCache)(HANDLE hProcess, LPCVOID *lpBaseAddress, SIZE_T dwSize);
121 static DWORD (WINAPI *fn_GetNetworkParams)(char *, PULONG pOutBufLen);
122 static uint64_t (WINAPI *fn_GetTickCount64)(void);
124 /* QPC is broken, it sometimes jumps backwards, so don't use it by default */
125 #ifdef USER_QPC
126 static BOOL (WINAPI *fn_QueryPerformanceFrequency)(LARGE_INTEGER *lpPerformanceCount);
127 static BOOL (WINAPI *fn_QueryPerformanceCounter)(LARGE_INTEGER *lpPerformanceCount);
128 static long double perf_multiplier;
129 #endif
131 typedef struct addrinfo {
132 int ai_flags;
133 int ai_family;
134 int ai_socktype;
135 int ai_protocol;
136 size_t ai_addrlen;
137 char *ai_canonname;
138 struct sockaddr *ai_addr;
139 struct addrinfo *ai_next;
140 } ADDRINFOA, *PADDRINFOA;
142 typedef ADDRINFOA ADDRINFO, *LPADDRINFO;
144 #define EAI_NONAME WSAHOST_NOT_FOUND
146 static int (WINAPI *fn_getaddrinfo)(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
147 static void (WINAPI *fn_freeaddrinfo)(LPADDRINFO pAddrInfo);
148 static int (WINAPI *fn_getnameinfo)(const struct sockaddr *sockaddr, socklen_t sockaddr_len, char *node, DWORD node_size, char *service, DWORD service_size, int flags);
151 static bool os_threads_initialized;
153 static handle_t win32_std_handles[3];
155 dir_handle_t os_cwd;
157 static struct list deferred_write_list;
158 static struct list deferred_closed_list;
159 static mutex_t deferred_mutex;
161 static bool winsock_supported;
162 static struct list socket_list[2];
163 static mutex_t socket_list_mutex;
166 struct system_error_table_entry {
167 unsigned short errn;
168 unsigned short sys_error;
171 static const struct system_error_table_entry win32_error_to_system_error[] = {
172 { ERROR_INVALID_FUNCTION, SYSTEM_ERROR_EINVAL },
173 { ERROR_FILE_NOT_FOUND, SYSTEM_ERROR_ENOENT },
174 { ERROR_PATH_NOT_FOUND, SYSTEM_ERROR_ENOENT },
175 { ERROR_TOO_MANY_OPEN_FILES, SYSTEM_ERROR_EMFILE },
176 { ERROR_ACCESS_DENIED, SYSTEM_ERROR_EACCES },
177 { ERROR_INVALID_HANDLE, SYSTEM_ERROR_EBADF },
178 { ERROR_NOT_ENOUGH_MEMORY, SYSTEM_ERROR_ENOMEM },
179 { ERROR_OUTOFMEMORY, SYSTEM_ERROR_ENOMEM },
180 { ERROR_INVALID_DRIVE, SYSTEM_ERROR_ENOENT },
181 { ERROR_NOT_SAME_DEVICE, SYSTEM_ERROR_EXDEV },
182 { ERROR_WRITE_PROTECT, SYSTEM_ERROR_EROFS },
183 { ERROR_NOT_READY, SYSTEM_ERROR_ENOMEDIUM },
184 { ERROR_CRC, SYSTEM_ERROR_EIO },
185 { ERROR_SECTOR_NOT_FOUND, SYSTEM_ERROR_EIO },
186 { ERROR_SHARING_VIOLATION, SYSTEM_ERROR_EBUSY },
187 { ERROR_HANDLE_DISK_FULL, SYSTEM_ERROR_ENOSPC },
188 { ERROR_NOT_SUPPORTED, SYSTEM_ERROR_EOPNOTSUPP },
189 { ERROR_DEV_NOT_EXIST, SYSTEM_ERROR_ENXIO },
190 { ERROR_NETWORK_ACCESS_DENIED, SYSTEM_ERROR_EACCES },
191 { ERROR_FILE_EXISTS, SYSTEM_ERROR_EEXIST },
192 { ERROR_DRIVE_LOCKED, SYSTEM_ERROR_EBUSY },
193 { ERROR_BROKEN_PIPE, SYSTEM_ERROR_EPIPE },
194 { ERROR_OPEN_FAILED, SYSTEM_ERROR_ENOENT },
195 { ERROR_BUFFER_OVERFLOW, SYSTEM_ERROR_ENAMETOOLONG },
196 { ERROR_DISK_FULL, SYSTEM_ERROR_ENOSPC },
197 { ERROR_INVALID_NAME, SYSTEM_ERROR_EINVAL },
198 { ERROR_NEGATIVE_SEEK, SYSTEM_ERROR_EINVAL },
199 { ERROR_SEEK_ON_DEVICE, SYSTEM_ERROR_ESPIPE },
200 { ERROR_DIR_NOT_EMPTY, SYSTEM_ERROR_ENOTEMPTY },
201 { ERROR_BUSY, SYSTEM_ERROR_EBUSY },
202 { ERROR_ALREADY_EXISTS, SYSTEM_ERROR_EEXIST },
203 { ERROR_FILENAME_EXCED_RANGE, SYSTEM_ERROR_ENAMETOOLONG },
204 { /*ERROR_FILE_TOO_LARGE*/ 223, SYSTEM_ERROR_EOVERFLOW },
205 { ERROR_NO_DATA, SYSTEM_ERROR_EPIPE },
206 { ERROR_DIRECTORY, SYSTEM_ERROR_ENOTDIR },
207 { 567, SYSTEM_ERROR_ENOMEM },
208 { WSAEINTR, SYSTEM_ERROR_EINTR },
209 { WSAEBADF, SYSTEM_ERROR_EBADF },
210 { WSAEACCES, SYSTEM_ERROR_EACCES },
211 { WSAEFAULT, SYSTEM_ERROR_EFAULT },
212 { WSAEINVAL, SYSTEM_ERROR_EINVAL },
213 { WSAEMFILE, SYSTEM_ERROR_EMFILE },
214 { WSAEWOULDBLOCK, SYSTEM_ERROR_EAGAIN },
215 { WSAEINPROGRESS, SYSTEM_ERROR_EINPROGRESS },
216 { WSAEALREADY, SYSTEM_ERROR_EALREADY },
217 { WSAENOTSOCK, SYSTEM_ERROR_ENOTSOCK },
218 { WSAEDESTADDRREQ, SYSTEM_ERROR_EDESTADDRREQ },
219 { WSAEMSGSIZE, SYSTEM_ERROR_EMSGSIZE },
220 { WSAEPROTOTYPE, SYSTEM_ERROR_EPROTOTYPE },
221 { WSAENOPROTOOPT, SYSTEM_ERROR_ENOPROTOOPT },
222 { WSAEPROTONOSUPPORT, SYSTEM_ERROR_EPROTONOSUPPORT },
223 { WSAESOCKTNOSUPPORT, SYSTEM_ERROR_ESOCKTNOSUPPORT },
224 { WSAEOPNOTSUPP, SYSTEM_ERROR_EOPNOTSUPP },
225 { WSAEPFNOSUPPORT, SYSTEM_ERROR_EPFNOSUPPORT },
226 { WSAEAFNOSUPPORT, SYSTEM_ERROR_EAFNOSUPPORT },
227 { WSAEADDRINUSE, SYSTEM_ERROR_EADDRINUSE },
228 { WSAEADDRNOTAVAIL, SYSTEM_ERROR_EADDRNOTAVAIL },
229 { WSAENETDOWN, SYSTEM_ERROR_ENETDOWN },
230 { WSAENETUNREACH, SYSTEM_ERROR_ENETUNREACH },
231 { WSAENETRESET, SYSTEM_ERROR_ENETRESET },
232 { WSAECONNABORTED, SYSTEM_ERROR_ECONNABORTED },
233 { WSAECONNRESET, SYSTEM_ERROR_ECONNRESET },
234 { WSAENOBUFS, SYSTEM_ERROR_ENOBUFS },
235 { WSAEISCONN, SYSTEM_ERROR_EISCONN },
236 { WSAENOTCONN, SYSTEM_ERROR_ENOTCONN },
237 { WSAESHUTDOWN, SYSTEM_ERROR_ESHUTDOWN },
238 { WSAETOOMANYREFS, SYSTEM_ERROR_ETOOMANYREFS },
239 { WSAETIMEDOUT, SYSTEM_ERROR_ETIMEDOUT },
240 { WSAECONNREFUSED, SYSTEM_ERROR_ECONNREFUSED },
241 { WSAELOOP, SYSTEM_ERROR_ELOOP },
242 { WSAENAMETOOLONG, SYSTEM_ERROR_ENAMETOOLONG },
243 { WSAEHOSTDOWN, SYSTEM_ERROR_EHOSTDOWN },
244 { WSAEHOSTUNREACH, SYSTEM_ERROR_EHOSTUNREACH },
245 { WSAENOTEMPTY, SYSTEM_ERROR_ENOTEMPTY },
246 { WSAEUSERS, SYSTEM_ERROR_EUSERS },
247 { WSAEDQUOT, SYSTEM_ERROR_EDQUOT },
248 { WSAESTALE, SYSTEM_ERROR_ESTALE },
249 { WSAEREMOTE, SYSTEM_ERROR_EREMOTE },
250 { WSAEDISCON, SYSTEM_ERROR_EPIPE },
253 #ifndef INVALID_FILE_ATTRIBUTES
254 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
255 #endif
257 #ifndef FILE_READ_ONLY
258 #define FILE_READ_ONLY 8
259 #endif
261 static ajla_error_t error_from_win32(int ec, DWORD rc)
263 size_t r;
264 binary_search(size_t, n_array_elements(win32_error_to_system_error), r, win32_error_to_system_error[r].errn == rc, win32_error_to_system_error[r].errn < rc, return error_ajla_aux(ec, AJLA_ERROR_WIN32, rc));
265 return error_ajla_aux(ec, AJLA_ERROR_SYSTEM, win32_error_to_system_error[r].sys_error);
268 static ajla_error_t error_from_win32_socket(int errn)
270 if (unlikely(!winsock_supported)) {
271 return error_ajla(EC_SYSCALL, AJLA_ERROR_NOT_SUPPORTED);
273 return error_from_win32(EC_SYSCALL, errn);
276 uint32_t os_get_last_error(void)
278 return GetLastError();
281 uint32_t os_get_last_socket_error(void)
283 return WSAGetLastError();
286 #ifdef BIT64
288 #define is_winnt() true
290 #else
292 static bool is_winnt(void)
294 return (GetVersion() & 0x80000000U) == 0;
297 #endif
299 static WCHAR *utf8_to_wchar(const char *str, ajla_error_t *err)
301 WCHAR *r;
302 size_t l;
303 ajla_error_t e;
305 if (unlikely(!array_init_mayfail(WCHAR, &r, &l, err)))
306 return NULL;
308 while (*str) {
309 unsigned char c = *str++;
310 unsigned char d, e, f;
311 uint32_t u;
312 if (likely(c < 0x80)) {
313 u = c;
314 } else if (unlikely(c < 0xc0)) {
315 goto invalid;
316 } else if (likely(c < 0xe0)) {
317 d = *str++;
318 if (unlikely(d < 0x80) || unlikely(d >= 0xc0))
319 goto invalid;
320 u = ((uint32_t)(c & 0x1f) << 6) | (d & 0x3f);
321 if (unlikely(u < 0x80))
322 goto invalid;
323 } else if (likely(c < 0xf0)) {
324 d = *str++;
325 if (unlikely(d < 0x80) || unlikely(d >= 0xc0))
326 goto invalid;
327 e = *str++;
328 if (unlikely(e < 0x80) || unlikely(e >= 0xc0))
329 goto invalid;
330 u = ((uint32_t)(c & 0xf) << 12) | ((uint32_t)(d & 0x3f) << 6) | (e & 0x3f);
331 if (unlikely(u < 0x800))
332 goto invalid;
333 } else if (likely(c < 0xf8)) {
334 d = *str++;
335 if (unlikely(d < 0x80) || unlikely(d >= 0xc0))
336 goto invalid;
337 e = *str++;
338 if (unlikely(e < 0x80) || unlikely(e >= 0xc0))
339 goto invalid;
340 f = *str++;
341 if (unlikely(f < 0x80) || unlikely(f >= 0xc0))
342 goto invalid;
343 u = ((uint32_t)(c & 0x7) << 18) | ((uint32_t)(d & 0x3f) << 12) | ((uint32_t)(e & 0x3f) << 6) | (f & 0x3f);
344 if (unlikely(u < 0x10000) || unlikely(u >= 0x110000))
345 goto invalid;
346 } else {
347 goto invalid;
349 if (u < 0x10000) {
350 if (unlikely(u >= 0xd800) && unlikely(u < 0xe000))
351 goto invalid;
352 if (unlikely(!array_add_mayfail(WCHAR, &r, &l, u, NULL, err)))
353 return NULL;
354 } else {
355 uint16_t u1, u2;
356 u -= 0x10000;
357 u1 = (u >> 10) | 0xd800;
358 u2 = (u & 0x3ff) | 0xdc00;
359 if (unlikely(!array_add_mayfail(WCHAR, &r, &l, u1, NULL, err)))
360 return NULL;
361 if (unlikely(!array_add_mayfail(WCHAR, &r, &l, u2, NULL, err)))
362 return NULL;
366 if (unlikely(!array_add_mayfail(WCHAR, &r, &l, 0, NULL, err)))
367 return false;
369 return r;
371 invalid:
372 mem_free(r);
373 e = error_from_win32(EC_SYSCALL, AJLA_ERROR_INVALID_OPERATION);
374 fatal_mayfail(e, err, "invalid utf-8");
375 return NULL;
378 static char *wchar_to_utf8(char *result, const WCHAR *str, ajla_error_t *err)
380 char *r;
381 size_t l;
382 ajla_error_t e;
384 if (likely(!result)) {
385 if (unlikely(!array_init_mayfail(char, &r, &l, err)))
386 return NULL;
387 } else {
388 r = result;
389 l = 0;
392 #define emit_char(ch) \
393 do { \
394 if (likely(!result)) { \
395 if (unlikely(!array_add_mayfail(char, &r, &l, ch, NULL, err)))\
396 return NULL; \
397 } else { \
398 result[l++] = (ch); \
400 } while (0)
402 while (*str) {
403 uint32_t w = (uint16_t)*str++;
404 if (unlikely((w & 0xfc00) == 0xd800)) {
405 uint32_t hi = (w & 0x3ff) << 10;
406 uint32_t lo = (uint16_t)*str++;
407 if (unlikely((lo & 0xfc00) != 0xdc00))
408 goto invalid;
409 lo &= 0x3ff;
410 w = hi + lo + 0x10000;
412 if (likely(w < 0x80)) {
413 emit_char(w);
414 } else if (likely(w < 0x800)) {
415 emit_char(0xc0 | (w >> 6));
416 emit_char(0x80 | (w & 0x3f));
417 } else if (likely(w < 0x10000)) {
418 emit_char(0xe0 | (w >> 12));
419 emit_char(0x80 | ((w >> 6) & 0x3f));
420 emit_char(0x80 | (w & 0x3f));
421 } else if (likely(w < 0x110000)) {
422 emit_char(0xf0 | (w >> 18));
423 emit_char(0x80 | ((w >> 12) & 0x3f));
424 emit_char(0x80 | ((w >> 6) & 0x3f));
425 emit_char(0x80 | (w & 0x3f));
426 } else {
427 goto invalid;
431 emit_char(0);
433 #undef emit_char
435 if (likely(!result)) {
436 array_finish(char, &r, &l);
439 return r;
441 invalid:
442 if (likely(!result))
443 mem_free(r);
444 e = error_from_win32(EC_SYSCALL, AJLA_ERROR_INVALID_OPERATION);
445 fatal_mayfail(e, err, "invalid utf-16");
446 return NULL;
450 void os_block_signals(sig_state_t attr_unused *set)
454 void os_unblock_signals(const sig_state_t attr_unused *set)
458 void os_stop(void)
460 warning("stop not supported on Windows");
463 void os_background(void)
467 bool os_foreground(void)
469 return true;
473 static void win32_close_handle(HANDLE h)
475 if (unlikely(!CloseHandle(h)))
476 internal(file_line, "CloseHandle failed: %u", GetLastError());
479 static void win32_close_change_notification_handle(HANDLE h)
481 if (unlikely(!FindCloseChangeNotification(h)))
482 internal(file_line, "FindCloseChangeNotification failed: %u", GetLastError());
485 static void win32_close_socket(SOCKET s)
487 if (unlikely(closesocket(s) == SOCKET_ERROR))
488 warning("closesocket returned an error: %u", WSAGetLastError());
491 static void win32_set_event(HANDLE h)
493 if (unlikely(!SetEvent(h)))
494 internal(file_line, "SetEvent failed: %u", GetLastError());
498 static handle_t win32_hfile_to_handle(HANDLE hfile, int flags, bool overlapped, char *file_name, ajla_error_t *err)
500 size_t file_name_len;
501 handle_t h;
502 DWORD type, gle, cmode;
504 SetLastError(0);
505 type = GetFileType(hfile);
506 if (unlikely(type == FILE_TYPE_UNKNOWN) && (gle = GetLastError())) {
507 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
508 fatal_mayfail(e, err, "can't get file type: %s", error_decode(e));
509 win32_close_handle(hfile);
510 return NULL;
512 type &= ~FILE_TYPE_REMOTE;
514 file_name_len = strlen(file_name);
516 h = struct_alloc_array_mayfail(mem_calloc_mayfail, struct win32_handle, file_name, file_name_len + 1, err);
517 if (unlikely(!h)) {
518 win32_close_handle(hfile);
519 return NULL;
522 memcpy(h->file_name, file_name, file_name_len + 1);
524 if (GetConsoleMode(hfile, &cmode)) {
525 h->is_console = true;
528 h->is_overlapped = overlapped;
530 h->h = hfile;
531 h->s = INVALID_SOCKET;
532 h->type = type;
533 if (type == FILE_TYPE_DISK)
534 flags &= ~O_NONBLOCK;
535 h->flags = flags;
537 list_init(&h->wait_list[0]);
538 list_init(&h->wait_list[1]);
540 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
542 return h;
545 static handle_t win32_socket_to_handle(SOCKET sock, ajla_error_t *err)
547 handle_t h;
548 u_long one = 1;
550 if (unlikely(ioctlsocket(sock, FIONBIO, &one) == SOCKET_ERROR)) {
551 fatal_mayfail(error_from_win32_socket(WSAGetLastError()), err, "could not set socket non-blocking");
552 win32_close_socket(sock);
553 return NULL;
556 h = struct_alloc_array_mayfail(mem_calloc_mayfail, struct win32_handle, file_name, 1, err);
557 if (unlikely(!h)) {
558 win32_close_socket(sock);
559 return NULL;
562 h->h = INVALID_HANDLE_VALUE;
563 h->s = sock;
564 h->flags = O_RDWR | O_NONBLOCK;
566 list_init(&h->wait_list[0]);
567 list_init(&h->wait_list[1]);
569 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
571 return h;
574 static inline bool handle_is_socket(handle_t h)
576 return h->s != INVALID_SOCKET;
579 uintptr_t os_handle_to_number(handle_t h)
581 if (handle_is_socket(h))
582 return h->s;
583 else
584 return ptr_to_num(h->h);
587 handle_t os_number_to_handle(uintptr_t n, bool sckt, ajla_error_t *err)
589 if (!sckt) {
590 return win32_hfile_to_handle(num_to_ptr(n), O_RDWR, false, "", err);
591 } else {
592 if (unlikely(!winsock_supported)) {
593 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not configured");
594 return NULL;
596 return win32_socket_to_handle(n, err);
600 static void win32_clean_up_handles(void);
602 handle_t os_open(dir_handle_t dir, const char *path, int flags, int mode, ajla_error_t *err)
604 char *joined;
605 DWORD access, disposition, attrs, share_mode;
606 HANDLE hfile;
607 DWORD gle;
608 handle_t h;
610 win32_clean_up_handles();
612 joined = os_join_paths(dir, path, false, err);
613 if (unlikely(!joined))
614 return NULL;
616 switch (flags & 3) {
617 case O_RDONLY: access = GENERIC_READ; break;
618 case O_WRONLY: access = GENERIC_WRITE; break;
619 case O_RDWR: access = GENERIC_READ | GENERIC_WRITE; break;
620 default: internal(file_line, "os_open: invalid flags %x", flags); return NULL;
623 switch (flags & (O_CREAT | O_EXCL | O_TRUNC)) {
624 case 0: disposition = OPEN_EXISTING; break;
625 case O_CREAT: disposition = OPEN_ALWAYS; break;
626 case O_CREAT | O_EXCL: disposition = CREATE_NEW; break;
627 case O_TRUNC: disposition = TRUNCATE_EXISTING; break;
628 case O_TRUNC | O_CREAT: disposition = CREATE_ALWAYS; break;
629 case O_TRUNC | O_CREAT | O_EXCL: disposition = CREATE_NEW; break;
630 default: internal(file_line, "os_open: invalid flags %x", flags); return NULL;
633 attrs = 0;
634 if (!(mode & 0222))
635 attrs |= FILE_READ_ONLY;
637 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
638 retry:
639 if (is_winnt()) {
640 WCHAR *w = utf8_to_wchar(joined, err);
641 if (unlikely(!w)) {
642 mem_free(joined);
643 return NULL;
645 hfile = CreateFileW(w, access, share_mode, NULL, disposition, attrs, NULL);
646 gle = GetLastError();
647 mem_free(w);
648 } else {
649 hfile = CreateFileA(joined, access, share_mode, NULL, disposition, attrs, NULL);
650 gle = GetLastError();
652 if (unlikely(hfile == INVALID_HANDLE_VALUE)) {
653 ajla_error_t e;
654 if (gle == ERROR_INVALID_PARAMETER && share_mode & FILE_SHARE_DELETE) {
655 share_mode &= ~FILE_SHARE_DELETE;
656 goto retry;
658 e = error_from_win32(EC_SYSCALL, gle);
659 fatal_mayfail(e, err, "can't open file '%s': %s", joined, error_decode(e));
660 mem_free(joined);
661 return NULL;
663 h = win32_hfile_to_handle(hfile, flags, false, joined, err);
664 mem_free(joined);
665 return h;
668 static mutex_t pipe_count_mutex;
669 static uint64_t pipe_count;
671 bool os_pipe(handle_t result[2], int nonblock_flags, ajla_error_t *err)
673 HANDLE h1, h2;
674 bool overlapped = false;
676 win32_clean_up_handles();
678 if (unlikely(!fn_CancelIoEx) && likely(fn_CancelIo != NULL)) {
679 char name[256];
680 uint64_t pc;
681 retry:
682 if (likely(os_threads_initialized))
683 mutex_lock(&pipe_count_mutex);
684 pc = pipe_count++;
685 if (likely(os_threads_initialized))
686 mutex_unlock(&pipe_count_mutex);
687 sprintf(name, "\\\\.\\pipe\\ajla-%x-%08x%08x", (unsigned)GetCurrentProcessId(), (unsigned)(pc >> 32), (unsigned)pc);
688 h1 = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 0, 0, NULL);
689 if (unlikely(h1 == INVALID_HANDLE_VALUE)) {
690 ajla_error_t e;
691 DWORD gle = GetLastError();
692 if (gle == ERROR_CALL_NOT_IMPLEMENTED)
693 goto unnamed_pipe;
694 if (gle == ERROR_PIPE_BUSY)
695 goto retry;
696 e = error_from_win32(EC_SYSCALL, gle);
697 fatal_mayfail(e, err, "can't create named pipe: %s", error_decode(e));
698 return false;
700 h2 = CreateFileA(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
701 if (unlikely(h2 == INVALID_HANDLE_VALUE)) {
702 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
703 fatal_mayfail(e, err, "can't connect to create named pipe: %s", error_decode(e));
704 win32_close_handle(h1);
705 return false;
707 overlapped = true;
708 } else {
709 unnamed_pipe:
710 if (unlikely(!CreatePipe(&h1, &h2, NULL, 0))) {
711 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
712 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
713 return false;
717 result[0] = win32_hfile_to_handle(h1, O_RDONLY | (nonblock_flags & 1 ? O_NONBLOCK : 0), overlapped, "", err);
718 if (unlikely(!result[0])) {
719 win32_close_handle(h1);
720 return false;
722 result[1] = win32_hfile_to_handle(h2, O_WRONLY | (nonblock_flags & 2 ? O_NONBLOCK : 0), overlapped, "", err);
723 if (unlikely(!result[1])) {
724 os_close(result[0]);
725 return false;
728 return true;
731 static void win32_terminate_io_thread(struct win32_io_thread *thr);
733 static void os_free_handle(handle_t h, bool should_close)
735 ajla_assert_lo(list_is_empty(&h->wait_list[0]), (file_line, "os_free_handle: freeing handle when there are processes waiting for read"));
736 ajla_assert_lo(list_is_empty(&h->wait_list[1]), (file_line, "os_free_handle: freeing handle when there are processes waiting for write"));
737 obj_registry_remove(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
738 if (h->rd) {
739 win32_terminate_io_thread(h->rd);
741 if (h->wr) {
742 address_lock(h, DEPTH_THUNK);
743 if (h->wr->buffer_len != 0 && !h->wr->err) {
744 h->wr->eof = true;
745 h->wr->should_close = should_close;
746 mutex_lock(&deferred_mutex);
747 list_add(&deferred_write_list, &h->deferred_entry);
748 mutex_unlock(&deferred_mutex);
749 address_unlock(h, DEPTH_THUNK);
750 return;
752 address_unlock(h, DEPTH_THUNK);
753 win32_terminate_io_thread(h->wr);
755 if (likely(should_close)) {
756 if (handle_is_socket(h)) {
757 mutex_lock(&socket_list_mutex);
758 if (h->socket_entry[0].next != NULL)
759 list_del(&h->socket_entry[0]);
760 if (h->socket_entry[1].next != NULL)
761 list_del(&h->socket_entry[1]);
762 mutex_unlock(&socket_list_mutex);
763 win32_close_socket(h->s);
764 } else {
765 win32_close_handle(h->h);
768 mem_free(h);
771 void os_close(handle_t h)
773 os_free_handle(h, true);
776 unsigned os_n_std_handles(void)
778 return 3;
781 handle_t os_get_std_handle(unsigned h)
783 return win32_std_handles[h];
786 static HANDLE get_std_handle(unsigned u)
788 DWORD s;
789 HANDLE h;
790 switch (u) {
791 case 0: s = STD_INPUT_HANDLE; break;
792 case 1: s = STD_OUTPUT_HANDLE; break;
793 case 2: s = STD_ERROR_HANDLE; break;
794 default: internal(file_line, "get_std_handle: invalid handle %u", u);
796 h = GetStdHandle(s);
797 if (unlikely(h == INVALID_HANDLE_VALUE)) {
798 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
799 fatal("can't get standard handle %u: %s", u, error_decode(e));
801 return h;
804 static void win32_clean_up_handles(void)
806 if (!list_is_empty(&deferred_closed_list)) {
807 mutex_lock(&deferred_mutex);
808 while (!list_is_empty(&deferred_closed_list)) {
809 handle_t h = get_struct(deferred_closed_list.prev, struct win32_handle, deferred_entry);
810 list_del(&h->deferred_entry);
811 obj_registry_insert(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
812 os_free_handle(h, false);
814 mutex_unlock(&deferred_mutex);
819 static void wait_for_event(HANDLE event)
821 DWORD r = WaitForSingleObject(event, INFINITE);
822 if (likely(r == WAIT_OBJECT_0))
823 return;
824 if (r == WAIT_FAILED)
825 internal(file_line, "WaitForSingleObject failed: %u", GetLastError());
826 internal(file_line, "WaitForSingleObject returned: %x", r);
829 static bool wait_for_event_timeout(HANDLE event)
831 DWORD r = WaitForSingleObject(event, 1);
832 if (likely(r == WAIT_OBJECT_0))
833 return true;
834 if (likely(r == WAIT_TIMEOUT))
835 return false;
836 if (r == WAIT_FAILED)
837 internal(file_line, "WaitForSingleObject failed: %u", GetLastError());
838 internal(file_line, "WaitForSingleObject returned: %x", r);
839 return false;
842 static unsigned wait_for_2_events(HANDLE event1, HANDLE event2)
844 DWORD r;
845 HANDLE a[2];
846 a[0] = event1;
847 a[1] = event2;
848 r = WaitForMultipleObjects(2, a, FALSE, INFINITE);
849 if (likely(r >= WAIT_OBJECT_0 + zero) && likely(r < WAIT_OBJECT_0 + 2))
850 return r - WAIT_OBJECT_0;
851 if (unlikely(r == WAIT_FAILED))
852 internal(file_line, "WaitForMultipleObjects failed: %u", GetLastError());
853 internal(file_line, "WaitForMultipleObjects returned: %x", r);
854 return 0;
857 static void unlock_mutex(HANDLE mutex)
859 if (unlikely(!ReleaseMutex(mutex)))
860 internal(file_line, "ReleaseMutex failed: %u", GetLastError());
863 static bool use_terminate_thread(struct win32_io_thread *thr)
865 return unlikely(!fn_CancelIoEx) && !thr->h->is_console && !thr->h->is_overlapped;
868 static void lock_io_thread(struct win32_io_thread *thr)
870 if (unlikely(use_terminate_thread(thr)))
871 wait_for_event(thr->terminate_mutex);
872 address_lock(thr->h, DEPTH_THUNK);
875 static void unlock_io_thread(struct win32_io_thread *thr)
877 address_unlock(thr->h, DEPTH_THUNK);
878 if (unlikely(use_terminate_thread(thr)))
879 unlock_mutex(thr->terminate_mutex);
882 static int read_console_packet(struct win32_io_thread *thr, struct console_read_packet *p)
884 unsigned w;
885 INPUT_RECORD ev;
886 DWORD nr;
887 bool wnt = is_winnt();
888 again:
889 w = wait_for_2_events(thr->terminate_event, thr->h->h);
890 if (unlikely(!w))
891 return 0;
892 if (unlikely(!load_relaxed(&thr->is_packet_console)))
893 return 0;
894 if (wnt) {
895 if (unlikely(!ReadConsoleInputW(thr->h->h, &ev, 1, &nr)))
896 return -1;
897 } else {
898 if (unlikely(!ReadConsoleInputA(thr->h->h, &ev, 1, &nr)))
899 return -1;
901 memset(p, 0, sizeof(struct console_read_packet));
902 if (ev.EventType == KEY_EVENT && ev.Event.KeyEvent.bKeyDown) {
903 /*debug("%x - %x - %x", ev.Event.KeyEvent.uChar.AsciiChar, ev.Event.KeyEvent.wVirtualKeyCode, ev.Event.KeyEvent.dwControlKeyState);*/
904 p->type = 1;
905 p->u.k.vkey = ev.Event.KeyEvent.wVirtualKeyCode;
906 p->u.k.ctrl = ev.Event.KeyEvent.dwControlKeyState;
907 if (wnt) {
908 p->u.k.key = ev.Event.KeyEvent.uChar.UnicodeChar;
909 } else {
910 p->u.k.key = (unsigned char)ev.Event.KeyEvent.uChar.AsciiChar;
911 p->u.k.cp = GetConsoleCP();
913 return 1;
915 if (ev.EventType == MOUSE_EVENT) {
916 CONSOLE_SCREEN_BUFFER_INFO csbi;
917 HANDLE g;
918 unsigned i;
919 for (i = 2; i >= 1; i--) {
920 g = get_std_handle(i);
921 if (likely(GetConsoleScreenBufferInfo(g, &csbi)))
922 goto have_csbi;
924 goto again;
925 have_csbi:
926 p->type = 2;
927 p->u.m.x = ev.Event.MouseEvent.dwMousePosition.X - csbi.srWindow.Left;
928 p->u.m.y = ev.Event.MouseEvent.dwMousePosition.Y - csbi.srWindow.Top;
929 p->u.m.prev_buttons = thr->last_buttons;
930 p->u.m.buttons = thr->last_buttons = ev.Event.MouseEvent.dwButtonState & 0x1f;
931 if (ev.Event.MouseEvent.dwEventFlags & 4)
932 p->u.m.wy = ev.Event.MouseEvent.dwButtonState & 0x80000000U ? 1 : -1;
933 if (ev.Event.MouseEvent.dwEventFlags & 8)
934 p->u.m.wx = ev.Event.MouseEvent.dwButtonState & 0x80000000U ? 1 : -1;
935 return 1;
936 /*debug("%u %u %x %x", ev.Event.MouseEvent.dwMousePosition.X, ev.Event.MouseEvent.dwMousePosition.Y, ev.Event.MouseEvent.dwButtonState, ev.Event.MouseEvent.dwEventFlags);*/
938 goto again;
941 static void echo(struct win32_io_thread *thr, WCHAR ch)
943 DWORD cmode;
944 HANDLE g;
945 unsigned i;
946 if (thr->h->tc_flags & IO_Stty_Flag_Noecho)
947 return;
948 for (i = 2; i >= 1; i--) {
949 g = get_std_handle(i);
950 if (GetConsoleMode(g, &cmode)) {
951 DWORD wr;
952 if (is_winnt()) {
953 WriteConsoleW(g, &ch, 1, &wr, NULL);
954 } else {
955 char cha = ch;
956 WriteConsoleA(g, &cha, 1, &wr, NULL);
958 return;
963 static BOOL read_console(struct win32_io_thread *thr, char *buffer, unsigned len, DWORD *rd)
965 unsigned w;
966 INPUT_RECORD ev;
967 DWORD nr;
968 WCHAR ch;
969 *rd = 0;
970 again:
971 if (thr->line_buffer_pos) {
972 unsigned tx = min(len, thr->line_buffer_pos);
973 unsigned rem = thr->line_buffer_size - thr->line_buffer_pos;
974 memcpy(buffer, thr->line_buffer, tx);
975 memmove(thr->line_buffer, thr->line_buffer + tx, rem);
976 thr->line_buffer_pos -= tx;
977 thr->line_buffer_size -= tx;
978 *rd = tx;
979 return TRUE;
981 if (unlikely(thr->line_buffer_eof) && !thr->line_buffer_size)
982 return TRUE;
983 w = wait_for_2_events(thr->terminate_event, thr->h->h);
984 if (unlikely(!w))
985 return TRUE;
986 if (unlikely(load_relaxed(&thr->is_packet_console)))
987 return TRUE;
988 if (is_winnt()) {
989 if (unlikely(!ReadConsoleInputW(thr->h->h, &ev, 1, &nr)))
990 return FALSE;
991 ch = ev.Event.KeyEvent.uChar.UnicodeChar;
992 } else {
993 if (unlikely(!ReadConsoleInputA(thr->h->h, &ev, 1, &nr)))
994 return FALSE;
995 ch = ev.Event.KeyEvent.uChar.AsciiChar;
997 if (ev.EventType == KEY_EVENT && ev.Event.KeyEvent.bKeyDown && ch && !thr->line_buffer_eof) {
998 if (ch == 26 && !(thr->h->tc_flags & IO_Stty_Flag_Raw)) {
999 thr->line_buffer_eof = true;
1000 thr->line_buffer_pos = thr->line_buffer_size;
1001 } else if (ch == 8 && !(thr->h->tc_flags & IO_Stty_Flag_Raw)) {
1002 if (thr->line_buffer_size > thr->line_buffer_pos) {
1003 echo(thr, 8);
1004 echo(thr, ' ');
1005 echo(thr, 8);
1006 thr->line_buffer_size--;
1008 } else if ((ch == 10 || ch == 13) && !(thr->h->tc_flags & IO_Stty_Flag_Raw)) {
1009 if (thr->line_buffer_size <= WIN32_LINE_BUFFER_SIZE - 2) {
1010 thr->line_buffer[thr->line_buffer_size++] = 13;
1011 thr->line_buffer[thr->line_buffer_size++] = 10;
1012 thr->line_buffer_pos = thr->line_buffer_size;
1014 echo(thr, 13);
1015 echo(thr, 10);
1016 } else {
1017 char ch_buffer[5];
1018 size_t ch_len;
1019 if (is_winnt()) {
1020 WCHAR wchstr[2];
1021 wchstr[0] = ch;
1022 wchstr[1] = 0;
1023 if (unlikely(!wchar_to_utf8(ch_buffer, wchstr, NULL)))
1024 goto skip_invalid_char;
1025 } else {
1026 ch_buffer[0] = ch;
1027 ch_buffer[1] = 0;
1029 ch_len = strlen(ch_buffer);
1030 if (thr->line_buffer_size <= WIN32_LINE_BUFFER_SIZE - 2 - ch_len) {
1031 memcpy(&thr->line_buffer[thr->line_buffer_size], ch_buffer, ch_len);
1032 thr->line_buffer_size += ch_len;
1033 if (thr->h->tc_flags & IO_Stty_Flag_Raw)
1034 thr->line_buffer_pos = thr->line_buffer_size;
1036 skip_invalid_char:
1037 echo(thr, ch);
1040 goto again;
1043 static BOOL read_overlapped(struct win32_io_thread *thr, char *buffer, unsigned len, DWORD *rd)
1045 unsigned w;
1046 OVERLAPPED ovl;
1047 memset(&ovl, 0, sizeof ovl);
1048 if (unlikely(!ReadFile(thr->h->h, buffer, len, rd, &ovl))) {
1049 DWORD gle = GetLastError();
1050 if (gle != ERROR_IO_PENDING)
1051 return FALSE;
1053 w = wait_for_2_events(thr->terminate_event, thr->h->h);
1054 if (unlikely(!w)) {
1055 if (unlikely(!fn_CancelIo(thr->h->h)))
1056 internal(file_line, "CancelIo failed: %u", GetLastError());
1058 return GetOverlappedResult(thr->h->h, &ovl, rd, TRUE);
1061 static DWORD WINAPI win32_read_thread(LPVOID thr_)
1063 struct win32_io_thread *thr = thr_;
1064 if (unlikely(use_terminate_thread(thr)))
1065 win32_set_event(thr->startup_event);
1066 while (1) {
1067 lock_io_thread(thr);
1068 if (unlikely(thr->err) || unlikely(thr->eof) || unlikely(thr->need_terminate)) {
1069 unlock_io_thread(thr);
1070 break;
1071 } else if (load_relaxed(&thr->is_packet_console)) {
1072 int b;
1073 DWORD gle;
1074 if (thr->packet_is_queued)
1075 goto wait_for_space;
1076 unlock_io_thread(thr);
1077 b = read_console_packet(thr, &thr->packet);
1078 gle = GetLastError();
1079 lock_io_thread(thr);
1080 if (unlikely(b < 0)) {
1081 if (unlikely(gle == ERROR_OPERATION_ABORTED)) {
1082 } if (likely(gle == ERROR_BROKEN_PIPE)) {
1083 thr->eof = true;
1084 } else {
1085 thr->err = gle;
1087 } else if (b > 0) {
1088 if (likely(load_relaxed(&thr->is_packet_console)))
1089 thr->packet_is_queued = true;
1091 call(wake_up_wait_list)(&thr->h->wait_list[0], address_get_mutex(thr->h, DEPTH_THUNK), false);
1092 if (unlikely(use_terminate_thread(thr)))
1093 unlock_mutex(thr->terminate_mutex);
1094 } else if (thr->buffer_len < WIN32_BUFFER_SIZE) {
1095 BOOL b;
1096 DWORD gle;
1097 DWORD rd = 0;
1098 size_t ptr = (thr->buffer_pos + thr->buffer_len) % WIN32_BUFFER_SIZE;
1099 size_t len = thr->buffer_pos <= ptr ? WIN32_BUFFER_SIZE - ptr : thr->buffer_pos - ptr;
1100 unlock_io_thread(thr);
1101 if (thr->h->is_console)
1102 b = read_console(thr, thr->buffer + ptr, len, &rd);
1103 else if (thr->h->is_overlapped)
1104 b = read_overlapped(thr, thr->buffer + ptr, len, &rd);
1105 else
1106 b = ReadFile(thr->h->h, thr->buffer + ptr, len, &rd, NULL);
1107 gle = GetLastError();
1108 lock_io_thread(thr);
1109 thr->buffer_len += rd;
1110 if (unlikely(!b)) {
1111 if (unlikely(gle == ERROR_OPERATION_ABORTED)) {
1112 } if (likely(gle == ERROR_BROKEN_PIPE)) {
1113 thr->eof = true;
1114 } else {
1115 thr->err = gle;
1117 } else if (!rd) {
1118 if (!load_relaxed(&thr->is_packet_console))
1119 thr->eof = true;
1121 call(wake_up_wait_list)(&thr->h->wait_list[0], address_get_mutex(thr->h, DEPTH_THUNK), false);
1122 if (unlikely(use_terminate_thread(thr)))
1123 unlock_mutex(thr->terminate_mutex);
1124 } else {
1125 wait_for_space:
1126 if (unlikely(!ResetEvent(thr->data_event)))
1127 internal(file_line, "ResetEvent failed: %u", GetLastError());
1128 unlock_io_thread(thr);
1129 wait_for_event(thr->data_event);
1132 return 0;
1135 static BOOL write_overlapped(struct win32_io_thread *thr, char *buffer, unsigned len, DWORD *wr)
1137 unsigned w;
1138 OVERLAPPED ovl;
1139 memset(&ovl, 0, sizeof ovl);
1140 if (unlikely(!WriteFile(thr->h->h, buffer, len, wr, &ovl))) {
1141 DWORD gle = GetLastError();
1142 if (gle != ERROR_IO_PENDING)
1143 return FALSE;
1145 w = wait_for_2_events(thr->terminate_event, thr->h->h);
1146 if (unlikely(!w)) {
1147 if (unlikely(!fn_CancelIo(thr->h->h)))
1148 internal(file_line, "CancelIo failed: %u", GetLastError());
1150 return GetOverlappedResult(thr->h->h, &ovl, wr, TRUE);
1153 static DWORD WINAPI win32_write_thread(LPVOID thr_)
1155 struct win32_io_thread *thr = thr_;
1156 if (unlikely(use_terminate_thread(thr)))
1157 win32_set_event(thr->startup_event);
1158 while (1) {
1159 lock_io_thread(thr);
1160 if (unlikely(thr->err) || unlikely(thr->need_terminate)) {
1161 if (thr->eof && !thr->need_terminate)
1162 goto eof;
1163 unlock_io_thread(thr);
1164 break;
1165 } else if (thr->buffer_len) {
1166 BOOL b;
1167 DWORD gle;
1168 DWORD wr = 0;
1169 size_t len = minimum(thr->buffer_len, WIN32_BUFFER_SIZE - thr->buffer_pos);
1170 unlock_io_thread(thr);
1171 if (thr->h->is_overlapped)
1172 b = write_overlapped(thr, thr->buffer + thr->buffer_pos, len, &wr);
1173 else
1174 b = WriteFile(thr->h->h, thr->buffer + thr->buffer_pos, len, &wr, NULL);
1175 gle = GetLastError();
1176 lock_io_thread(thr);
1177 thr->buffer_pos = (thr->buffer_pos + wr) % WIN32_BUFFER_SIZE;
1178 thr->buffer_len -= wr;
1179 if (unlikely(!b)) {
1180 if (gle != ERROR_OPERATION_ABORTED)
1181 thr->err = gle;
1183 call(wake_up_wait_list)(&thr->h->wait_list[1], address_get_mutex(thr->h, DEPTH_THUNK), false);
1184 if (unlikely(use_terminate_thread(thr)))
1185 unlock_mutex(thr->terminate_mutex);
1186 } else if (unlikely(thr->eof)) {
1187 eof:
1188 thr->buffer_len = 0;
1189 if (thr->should_close)
1190 win32_close_handle(thr->h->h);
1191 mutex_lock(&deferred_mutex);
1192 list_del(&thr->h->deferred_entry);
1193 list_add(&deferred_closed_list, &thr->h->deferred_entry);
1194 mutex_unlock(&deferred_mutex);
1195 unlock_io_thread(thr);
1196 break;
1197 } else {
1198 if (unlikely(!ResetEvent(thr->data_event)))
1199 internal(file_line, "ResetEvent failed: %u", GetLastError());
1200 unlock_io_thread(thr);
1201 wait_for_event(thr->data_event);
1204 return 0;
1207 static bool win32_create_io_thread(handle_t h, struct win32_io_thread **pthr, LPTHREAD_START_ROUTINE win32_thread_function, ajla_error_t *err)
1209 struct win32_io_thread *thr;
1210 DWORD threadid;
1211 thr = mem_calloc_mayfail(struct win32_io_thread *, sizeof(struct win32_io_thread), err);
1212 if (unlikely(!thr))
1213 goto err;
1214 *pthr = thr;
1216 thr->h = h;
1217 thr->eof = false;
1218 thr->buffer_pos = thr->buffer_len = 0;
1220 thr->buffer = mem_alloc_mayfail(char *, WIN32_BUFFER_SIZE, err);
1221 if (unlikely(!thr->buffer))
1222 goto err0;
1223 thr->data_event = CreateEventA(NULL, TRUE, FALSE, NULL);
1224 if (unlikely(!thr->data_event)) {
1225 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1226 fatal_mayfail(e, err, "can't create event: %s", error_decode(e));
1227 goto err1;
1229 if (unlikely(use_terminate_thread(thr))) {
1230 thr->startup_event = CreateEventA(NULL, TRUE, FALSE, NULL);
1231 if (unlikely(!thr->startup_event)) {
1232 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1233 fatal_mayfail(e, err, "can't create event: %s", error_decode(e));
1234 goto err2;
1236 thr->terminate_mutex = CreateMutexA(NULL, FALSE, NULL);
1237 if (unlikely(!thr->terminate_mutex)) {
1238 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1239 fatal_mayfail(e, err, "can't create mutex: %s", error_decode(e));
1240 goto err3;
1243 if (h->is_console || h->is_overlapped) {
1244 thr->terminate_event = CreateEventA(NULL, TRUE, FALSE, NULL);
1245 if (unlikely(!thr->terminate_event)) {
1246 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1247 fatal_mayfail(e, err, "can't create event: %s", error_decode(e));
1248 goto err4;
1251 if (h->is_console) {
1252 thr->line_buffer = mem_alloc_mayfail(char *, WIN32_LINE_BUFFER_SIZE, err);
1253 if (unlikely(!thr->line_buffer))
1254 goto err5;
1257 thr->thread = CreateThread(NULL, WIN32_IO_THREAD_STACK_SIZE, win32_thread_function, thr, 0, &threadid);
1258 if (unlikely(!thr->thread)) {
1259 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1260 fatal_mayfail(e, err, "can't create thread: %s", error_decode(e));
1261 goto err6;
1264 if (unlikely(use_terminate_thread(thr))) {
1265 wait_for_event(thr->startup_event);
1266 win32_close_handle(thr->startup_event);
1269 return true;
1271 err6:
1272 if (h->is_console)
1273 mem_free(thr->line_buffer);
1274 err5:
1275 if (h->is_console || h->is_overlapped)
1276 win32_close_handle(thr->terminate_event);
1277 err4:
1278 if (unlikely(use_terminate_thread(thr)))
1279 win32_close_handle(thr->terminate_mutex);
1280 err3:
1281 if (unlikely(use_terminate_thread(thr)))
1282 win32_close_handle(thr->startup_event);
1283 err2:
1284 win32_close_handle(thr->data_event);
1285 err1:
1286 mem_free(thr->buffer);
1287 err0:
1288 mem_free(thr);
1289 err:
1290 return false;
1293 static void win32_terminate_io_thread(struct win32_io_thread *thr)
1295 DWORD gle;
1296 if (unlikely(use_terminate_thread(thr))) {
1297 wait_for_event(thr->terminate_mutex);
1298 if (likely(fn_RtlFreeUserThreadStack != NULL)) {
1299 CONTEXT context;
1300 if (unlikely(SuspendThread(thr->thread) == (DWORD)-1)) {
1301 gle = GetLastError();
1302 if (likely(gle == 5))
1303 goto already_terminating;
1304 internal(file_line, "SuspendThread failed: %u", gle);
1306 context.ContextFlags = CONTEXT_CONTROL;
1307 if (unlikely(!GetThreadContext(thr->thread, &context))) {
1308 gle = GetLastError();
1309 if (likely(gle == 5))
1310 goto already_terminating;
1311 internal(file_line, "GetThreadContext failed: %u", gle);
1313 fn_RtlFreeUserThreadStack(GetCurrentProcess(), thr->thread);
1315 already_terminating:
1316 if (unlikely(!TerminateThread(thr->thread, 0))) {
1317 gle = GetLastError();
1318 if (unlikely(gle != 87))
1319 internal(file_line, "TerminateThread failed: %u", gle);
1321 unlock_mutex(thr->terminate_mutex);
1322 wait_for_event(thr->thread);
1323 win32_close_handle(thr->terminate_mutex);
1324 } else if (thr->h->is_console || thr->h->is_overlapped) {
1325 address_lock(thr->h, DEPTH_THUNK);
1326 thr->need_terminate = true;
1327 win32_set_event(thr->data_event);
1328 win32_set_event(thr->terminate_event);
1329 address_unlock(thr->h, DEPTH_THUNK);
1330 wait_for_event(thr->thread);
1331 win32_close_handle(thr->terminate_event);
1332 if (thr->h->is_console)
1333 mem_free(thr->line_buffer);
1334 } else {
1335 /*Sleep(1000);*/
1336 csi_again:
1337 address_lock(thr->h, DEPTH_THUNK);
1338 thr->need_terminate = true;
1339 win32_set_event(thr->data_event);
1340 if (unlikely(!fn_CancelIoEx(thr->h->h, NULL))) {
1341 DWORD gle = GetLastError();
1342 if (unlikely(gle != 6) && unlikely(gle != 1168))
1343 internal(file_line, "CancelIoEx failed: %u", gle);
1345 address_unlock(thr->h, DEPTH_THUNK);
1346 if (unlikely(!wait_for_event_timeout(thr->thread))) {
1347 goto csi_again;
1350 win32_close_handle(thr->thread);
1351 thr->thread = NULL;
1352 win32_close_handle(thr->data_event);
1353 mem_free(thr->buffer);
1354 mem_free(thr);
1357 static bool win32_create_read_thread(handle_t h, ajla_error_t *err)
1359 if (unlikely(!h->rd))
1360 return win32_create_io_thread(h, &h->rd, win32_read_thread, err);
1361 return true;
1364 static bool win32_create_write_thread(handle_t h, ajla_error_t *err)
1366 if (unlikely(!h->wr))
1367 return win32_create_io_thread(h, &h->wr, win32_write_thread, err);
1368 return true;
1371 static void win32_close_read_thread(handle_t h)
1373 struct win32_io_thread *thr;
1374 address_lock(h, DEPTH_THUNK);
1375 if (!h->rd) {
1376 address_unlock(h, DEPTH_THUNK);
1377 return;
1379 thr = h->rd;
1380 h->rd = NULL;
1381 call(wake_up_wait_list)(&h->wait_list[0], address_get_mutex(h, DEPTH_THUNK), true);
1382 win32_terminate_io_thread(thr);
1385 static ssize_t os_read_nonblock(handle_t h, char *buffer, int size, ajla_error_t *err)
1387 ssize_t this_len;
1388 address_lock(h, DEPTH_THUNK);
1389 if (unlikely(!win32_create_read_thread(h, err))) {
1390 address_unlock(h, DEPTH_THUNK);
1391 return OS_RW_ERROR;
1393 if (load_relaxed(&h->rd->is_packet_console)) {
1394 store_relaxed(&h->rd->is_packet_console, false);
1395 h->rd->packet_is_queued = false;
1396 win32_set_event(h->rd->data_event);
1398 if (h->rd->buffer_len) {
1399 bool was_full = h->rd->buffer_len == WIN32_BUFFER_SIZE;
1400 this_len = minimum(h->rd->buffer_len, WIN32_BUFFER_SIZE - h->rd->buffer_pos);
1401 this_len = minimum(this_len, size);
1402 memcpy(buffer, h->rd->buffer + h->rd->buffer_pos, this_len);
1403 h->rd->buffer_pos = (h->rd->buffer_pos + this_len) % WIN32_BUFFER_SIZE;
1404 h->rd->buffer_len -= this_len;
1405 if (was_full) {
1406 win32_set_event(h->rd->data_event);
1408 } else {
1409 if (unlikely(h->rd->err != 0)) {
1410 ajla_error_t e = error_from_win32(EC_SYSCALL, h->rd->err);
1411 fatal_mayfail(e, err, "can't read handle: %s", error_decode(e));
1412 this_len = OS_RW_ERROR;
1413 goto unlock_ret;
1415 if (unlikely(h->rd->eof)) {
1416 this_len = 0;
1417 goto unlock_ret;
1419 this_len = OS_RW_WOULDBLOCK;
1421 unlock_ret:
1422 address_unlock(h, DEPTH_THUNK);
1423 return this_len;
1426 static ssize_t os_write_nonblock(handle_t h, const char *buffer, int size, ajla_error_t *err)
1428 size_t ptr;
1429 ssize_t this_len;
1430 address_lock(h, DEPTH_THUNK);
1431 if (unlikely(!win32_create_write_thread(h, err))) {
1432 address_unlock(h, DEPTH_THUNK);
1433 return OS_RW_ERROR;
1435 if (unlikely(h->wr->err)) {
1436 ajla_error_t e = error_from_win32(EC_SYSCALL, h->wr->err);
1437 fatal_mayfail(e, err, "can't write handle: %s", error_decode(e));
1438 this_len = OS_RW_ERROR;
1439 goto unlock_ret;
1441 if (h->wr->buffer_len < WIN32_BUFFER_SIZE) {
1442 bool was_empty = !h->wr->buffer_len;
1443 ptr = (h->wr->buffer_pos + h->wr->buffer_len) % WIN32_BUFFER_SIZE;
1444 this_len = h->wr->buffer_pos <= ptr ? WIN32_BUFFER_SIZE - ptr : h->wr->buffer_pos - ptr;
1445 this_len = minimum(this_len, size);
1446 memcpy(h->wr->buffer + ptr, buffer, this_len);
1447 h->wr->buffer_len += this_len;
1448 if (was_empty) {
1449 win32_set_event(h->wr->data_event);
1451 } else {
1452 this_len = OS_RW_WOULDBLOCK;
1454 unlock_ret:
1455 address_unlock(h, DEPTH_THUNK);
1456 return this_len;
1459 static bool win32_setfileptr(handle_t h, os_off_t off, DWORD rel, os_off_t *result, ajla_error_t *err)
1461 DWORD gle;
1462 LONG high;
1463 DWORD low_ret;
1464 if (unlikely(h->type != FILE_TYPE_DISK)) {
1465 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to seek on non-disk handle");
1466 return false;
1468 high = off >> 31 >> 1;
1469 SetLastError(0);
1470 low_ret = SetFilePointer(h->h, off, &high, rel);
1471 gle = GetLastError();
1472 if (unlikely(gle != 0)) {
1473 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
1474 fatal_mayfail(e, err, "can't set handle position: %s", error_decode(e));
1475 return false;
1477 if (result) {
1478 *result = low_ret + ((uint64_t)(uint32_t)high << 32);
1480 return true;
1483 static ssize_t os_do_rw(handle_t h, char *buffer, int size, bool wr, os_off_t *off, ajla_error_t *err)
1485 BOOL b;
1486 DWORD result;
1487 bool need_lock = os_threads_initialized && (off || (h->flags & O_APPEND && likely(h->type == FILE_TYPE_DISK)));
1489 if (likely(need_lock))
1490 address_lock(h, DEPTH_THUNK);
1492 if (off) {
1493 if (unlikely(!win32_setfileptr(h, *off, FILE_BEGIN, NULL, err)))
1494 goto unlock_ret_error;
1495 } else if (h->flags & O_APPEND && likely(h->type == FILE_TYPE_DISK)) {
1496 if (unlikely(!win32_setfileptr(h, 0, FILE_END, NULL, err)))
1497 goto unlock_ret_error;
1500 do_io_again:
1501 if (!wr)
1502 b = ReadFile(h->h, buffer, size, &result, NULL);
1503 else
1504 b = WriteFile(h->h, buffer, size, &result, NULL);
1506 if (!b) {
1507 ajla_error_t e;
1508 DWORD gle = GetLastError();
1509 if (gle == ERROR_OPERATION_ABORTED) {
1510 if (!result)
1511 goto do_io_again;
1512 else
1513 goto ok;
1515 e = error_from_win32(EC_SYSCALL, gle);
1517 if (likely(need_lock))
1518 address_unlock(h, DEPTH_THUNK);
1519 fatal_mayfail(e, err, "can't %s handle: %s", !wr ? "read from" : "write to", error_decode(e));
1520 return OS_RW_ERROR;
1524 if (likely(need_lock))
1525 address_unlock(h, DEPTH_THUNK);
1527 return result;
1529 unlock_ret_error:
1530 if (likely(need_lock))
1531 address_unlock(h, DEPTH_THUNK);
1533 return OS_RW_ERROR;
1536 static ssize_t os_write_console(handle_t h, const char *buffer, int size, ajla_error_t *err)
1538 ssize_t ret = size;
1539 WCHAR *r;
1540 size_t l;
1541 if (unlikely(!array_init_mayfail(WCHAR, &r, &l, err)))
1542 return OS_RW_ERROR;
1543 while (size--) {
1544 ajla_error_t sink;
1545 WCHAR *wc;
1546 unsigned char c = *buffer++;
1547 if (c < 0x80) {
1548 h->utf8_buffer_size = 0;
1549 if (unlikely(!array_add_mayfail(WCHAR, &r, &l, c, NULL, err)))
1550 return OS_RW_ERROR;
1551 continue;
1552 } else if (c < 0xc0) {
1553 if (unlikely(!h->utf8_buffer_size) || unlikely((size_t)h->utf8_buffer_size + 1 >= sizeof(h->utf8_buffer)))
1554 continue;
1555 h->utf8_buffer[h->utf8_buffer_size++] = c;
1556 } else if (c < 0xf8) {
1557 h->utf8_buffer[0] = c;
1558 h->utf8_buffer_size = 1;
1559 } else {
1560 h->utf8_buffer_size = 0;
1561 continue;
1563 h->utf8_buffer[h->utf8_buffer_size] = 0;
1564 wc = utf8_to_wchar(h->utf8_buffer, &sink);
1565 if (wc) {
1566 size_t wl;
1567 h->utf8_buffer_size = 0;
1568 for (wl = 0; wc[wl]; wl++) ;
1569 if (unlikely(!array_add_multiple_mayfail(WCHAR, &r, &l, wc, wl, NULL, err))) {
1570 mem_free(wc);
1571 return OS_RW_ERROR;
1573 mem_free(wc);
1574 } else {
1575 if (unlikely(sink.error_class == EC_ASYNC)) {
1576 if (!err)
1577 fatal("can't allocate console buffer: %s", error_decode(sink));
1578 *err = sink;
1579 mem_free(r);
1580 return OS_RW_ERROR;
1584 if (l) {
1585 DWORD written;
1586 BOOL b = WriteConsoleW(h->h, r, l, &written, NULL);
1587 if (unlikely(!b)) {
1588 ajla_error_t e;
1589 DWORD gle = GetLastError();
1590 mem_free(r);
1591 e = error_from_win32(EC_SYSCALL, gle);
1592 fatal_mayfail(e, err, "can't write to console: %s", error_decode(e));
1593 return OS_RW_ERROR;
1596 mem_free(r);
1597 return ret;
1600 static ssize_t os_read_socket(handle_t h, char *buffer, int size, ajla_error_t *err)
1602 int r;
1604 r = recv(h->s, buffer, size, 0);
1605 if (unlikely(r == SOCKET_ERROR)) {
1606 int er = WSAGetLastError();
1607 if (likely(er == WSAEWOULDBLOCK))
1608 return OS_RW_WOULDBLOCK;
1609 fatal_mayfail(error_from_win32_socket(er), err, "error reading socket");
1610 return OS_RW_ERROR;
1612 return r;
1615 static ssize_t os_write_socket(handle_t h, const char *buffer, int size, ajla_error_t *err)
1617 int r;
1619 r = send(h->s, buffer, size, 0);
1620 if (unlikely(r == SOCKET_ERROR)) {
1621 int er = WSAGetLastError();
1622 if (likely(er == WSAEWOULDBLOCK))
1623 return OS_RW_WOULDBLOCK;
1624 fatal_mayfail(error_from_win32_socket(er), err, "error writing socket");
1625 return OS_RW_ERROR;
1627 return r;
1630 ssize_t os_read(handle_t h, char *buffer, int size, ajla_error_t *err)
1632 if (unlikely((h->flags & 3) == O_WRONLY)) {
1633 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to read from write-only handle");
1634 return OS_RW_ERROR;
1637 if (handle_is_socket(h))
1638 return os_read_socket(h, buffer, size, err);
1639 if (h->flags & O_NONBLOCK)
1640 return os_read_nonblock(h, buffer, size, err);
1641 return os_do_rw(h, buffer, size, false, NULL, err);
1644 ssize_t os_write(handle_t h, const char *buffer, int size, ajla_error_t *err)
1646 if (unlikely((h->flags & 3) == O_RDONLY)) {
1647 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to write to read-only handle");
1648 return OS_RW_ERROR;
1651 if (handle_is_socket(h))
1652 return os_write_socket(h, buffer, size, err);
1653 if (h->flags & O_NONBLOCK && !h->is_console)
1654 return os_write_nonblock(h, buffer, size, err);
1655 if (h->is_console && is_winnt())
1656 return os_write_console(h, buffer, size, err);
1657 return os_do_rw(h, cast_ptr(char *, buffer), size, true, NULL, err);
1660 ssize_t os_pread(handle_t h, char *buffer, int size, os_off_t off, ajla_error_t *err)
1662 if (unlikely((h->flags & 3) == O_WRONLY)) {
1663 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to read from write-only handle");
1664 return OS_RW_ERROR;
1667 if (unlikely(handle_is_socket(h))) {
1668 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1669 return OS_RW_ERROR;
1671 return os_do_rw(h, buffer, size, false, &off, err);
1674 ssize_t os_pwrite(handle_t h, const char *buffer, int size, os_off_t off, ajla_error_t *err)
1676 if (unlikely((h->flags & 3) == O_RDONLY)) {
1677 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to write to read-only handle");
1678 return OS_RW_ERROR;
1681 if (unlikely(handle_is_socket(h))) {
1682 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1683 return OS_RW_ERROR;
1685 return os_do_rw(h, cast_ptr(char *, buffer), size, true, &off, err);
1688 bool os_lseek(handle_t h, unsigned mode, os_off_t off, os_off_t *result, ajla_error_t *err)
1690 bool ret;
1691 ULONG rel;
1692 os_off_t len;
1694 if (unlikely(handle_is_socket(h))) {
1695 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "seek operation on socket");
1696 return false;
1699 if (likely(os_threads_initialized))
1700 address_lock(h, DEPTH_THUNK);
1702 switch (mode) {
1703 case 0: rel = FILE_BEGIN; break;
1704 case 1: rel = FILE_CURRENT; break;
1705 case 2: rel = FILE_END; break;
1706 case 3: ret = win32_setfileptr(h, 0, FILE_END, &len, err);
1707 if (unlikely(!ret))
1708 goto ret_ret;
1709 if (unlikely(off > len))
1710 off = len;
1711 *result = off;
1712 ret = true;
1713 goto ret_ret;
1714 case 4: rel = FILE_END; off = 0; break;
1715 default:internal(file_line, "os_lseek: unsupported mode %u", mode);
1716 rel = (ULONG)-1; break;
1719 ret = win32_setfileptr(h, off, rel, result, err);
1721 ret_ret:
1722 if (likely(os_threads_initialized))
1723 address_unlock(h, DEPTH_THUNK);
1725 return ret;
1728 bool os_ftruncate(handle_t h, os_off_t size, ajla_error_t *err)
1730 bool ret;
1731 BOOL b;
1733 if (unlikely(handle_is_socket(h))) {
1734 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "ftruncate operation on socket");
1735 return false;
1738 if (likely(os_threads_initialized))
1739 address_lock(h, DEPTH_THUNK);
1741 ret = win32_setfileptr(h, size, FILE_BEGIN, NULL, err);
1742 if (unlikely(!ret))
1743 goto ret_ret;
1745 b = SetEndOfFile(h->h);
1746 if (unlikely(!b)) {
1747 DWORD gle = GetLastError();
1748 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
1749 fatal_mayfail(e, err, "can't set file size: %s", error_decode(e));
1750 ret = false;
1751 goto ret_ret;
1754 ret = true;
1756 ret_ret:
1757 if (likely(os_threads_initialized))
1758 address_unlock(h, DEPTH_THUNK);
1760 return ret;
1763 bool os_fallocate(handle_t attr_unused h, os_off_t attr_unused position, os_off_t attr_unused size, ajla_error_t attr_unused *err)
1765 return true;
1768 bool os_clone_range(handle_t attr_unused src_h, os_off_t attr_unused src_pos, handle_t attr_unused dst_h, os_off_t attr_unused dst_pos, os_off_t attr_unused len, ajla_error_t *err)
1770 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "clone not supported");
1771 return false;
1774 bool os_fsync(handle_t h, unsigned attr_unused mode, ajla_error_t *err)
1776 BOOL b;
1778 if (unlikely(!handle_is_valid(h)) || unlikely(h->is_console))
1779 return true;
1781 if (unlikely(handle_is_socket(h))) {
1782 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "fsync operation on socket");
1783 return false;
1786 b = FlushFileBuffers(h->h);
1787 if (unlikely(!b)) {
1788 DWORD gle = GetLastError();
1789 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
1790 fatal_mayfail(e, err, "can't flush file: %s", error_decode(e));
1791 return false;
1794 return true;
1798 int os_charset(void)
1800 if (is_winnt())
1801 return 0;
1802 else
1803 return GetACP();
1806 int os_charset_console(void)
1808 if (is_winnt())
1809 return 0;
1810 else
1811 return GetConsoleOutputCP();
1814 ssize_t os_read_console_packet(handle_t h, struct console_read_packet *result, ajla_error_t *err)
1816 ssize_t retval;
1817 DWORD cmode;
1818 address_lock(h, DEPTH_THUNK);
1819 if (unlikely(!h->is_console) || unlikely((h->flags & 3) == O_WRONLY)) {
1820 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to use packet console on non-console");
1821 retval = OS_RW_ERROR;
1822 goto unlock_ret;
1824 if (GetConsoleMode(h->h, &cmode)) {
1825 if (unlikely(!(cmode & ENABLE_MOUSE_INPUT))) {
1826 cmode |= ENABLE_MOUSE_INPUT;
1827 SetConsoleMode(h->h, cmode);
1830 if (unlikely(!win32_create_read_thread(h, err))) {
1831 address_unlock(h, DEPTH_THUNK);
1832 retval = OS_RW_ERROR;
1833 goto unlock_ret;
1835 if (!load_relaxed(&h->rd->is_packet_console)) {
1836 store_relaxed(&h->rd->is_packet_console, true);
1837 h->rd->last_buttons = 0;
1838 h->rd->buffer_len = 0;
1839 win32_set_event(h->rd->data_event);
1841 if (h->rd->packet_is_queued) {
1842 memcpy(result, &h->rd->packet, sizeof(struct console_read_packet));
1843 h->rd->packet_is_queued = false;
1844 win32_set_event(h->rd->data_event);
1845 retval = 1;
1846 } else {
1847 if (unlikely(h->rd->err != 0)) {
1848 ajla_error_t e = error_from_win32(EC_SYSCALL, h->rd->err);
1849 fatal_mayfail(e, err, "can't read console packet: %s", error_decode(e));
1850 retval = OS_RW_ERROR;
1851 } else {
1852 retval = OS_RW_WOULDBLOCK;
1855 unlock_ret:
1856 address_unlock(h, DEPTH_THUNK);
1857 return retval;
1860 static atomic_type unsigned window_offset_x;
1861 static atomic_type unsigned window_offset_y;
1863 bool os_write_console_packet(handle_t h, struct console_write_packet *packet, ajla_error_t *err)
1865 BOOL ret;
1866 unsigned offset_x = load_relaxed(&window_offset_x);
1867 unsigned offset_y = load_relaxed(&window_offset_y);
1868 bool wnt = is_winnt();
1869 if (unlikely(!h->is_console) || unlikely((h->flags & 3) == O_RDONLY)) {
1870 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "attempting to use packet console on non-console");
1871 return false;
1874 if (h->is_console && (h->flags & 3) == O_RDONLY) {
1875 handle_t h1;
1876 h1 = os_get_std_handle(1);
1877 if (h1->is_console) {
1878 h = h1;
1879 goto have_h;
1881 h1 = os_get_std_handle(2);
1882 if (h1->is_console) {
1883 h = h1;
1884 goto have_h;
1888 have_h:
1890 next:
1891 switch (packet->type) {
1892 case 1: {
1893 break;
1895 case 2: {
1896 int i;
1897 CHAR_INFO *chr;
1898 COORD dwBufferSize;
1899 COORD dwBufferCoord;
1900 SMALL_RECT lpWriteRegion;
1902 chr = mem_alloc_array_mayfail(mem_alloc_mayfail, CHAR_INFO *, 0, 0, packet->u.c.n_chars, sizeof(CHAR_INFO), err);
1903 if (unlikely(!chr))
1904 return false;
1905 for (i = 0; i < packet->u.c.n_chars; i++) {
1906 if (wnt)
1907 chr[i].Char.UnicodeChar = packet->u.c.data[i * 2];
1908 else
1909 chr[i].Char.AsciiChar = packet->u.c.data[i * 2];
1910 chr[i].Attributes = packet->u.c.data[i * 2 + 1];
1912 dwBufferSize.X = packet->u.c.n_chars;
1913 dwBufferSize.Y = 1;
1914 dwBufferCoord.X = 0;
1915 dwBufferCoord.Y = 0;
1916 lpWriteRegion.Left = packet->u.c.x + offset_x;
1917 lpWriteRegion.Top = packet->u.c.y + offset_y;
1918 lpWriteRegion.Right = packet->u.c.x + packet->u.c.n_chars - 1 + offset_x;
1919 lpWriteRegion.Bottom = packet->u.c.y + offset_y;
1920 if (wnt)
1921 ret = WriteConsoleOutputW(h->h, chr, dwBufferSize, dwBufferCoord, &lpWriteRegion);
1922 else
1923 ret = WriteConsoleOutputA(h->h, chr, dwBufferSize, dwBufferCoord, &lpWriteRegion);
1924 if (unlikely(!ret)) {
1925 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1926 fatal_mayfail(e, err, "can't write to console buffer");
1927 mem_free(chr);
1928 return false;
1930 mem_free(chr);
1931 packet = cast_ptr(struct console_write_packet *, &packet->u.c.data[packet->u.c.n_chars * 2]);
1932 goto next;
1934 case 3: {
1935 COORD dwCursorPosition;
1936 dwCursorPosition.X = packet->u.p.x + offset_x;
1937 dwCursorPosition.Y = packet->u.p.y + offset_y;
1938 ret = SetConsoleCursorPosition(h->h, dwCursorPosition);
1939 /*if (unlikely(!ret)) {
1940 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1941 fatal_mayfail(e, err, "can't set cursor position");
1942 return false;
1944 packet = cast_ptr(struct console_write_packet *, &packet->u.p.end);
1945 goto next;
1947 case 4: {
1948 CONSOLE_CURSOR_INFO cci;
1949 ret = GetConsoleCursorInfo(h->h, &cci);
1950 if (unlikely(!ret)) {
1951 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1952 fatal_mayfail(e, err, "can't get cursor info");
1953 return false;
1955 cci.bVisible = packet->u.v.v;
1956 ret = SetConsoleCursorInfo(h->h, &cci);
1957 if (unlikely(!ret)) {
1958 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
1959 fatal_mayfail(e, err, "can't set cursor info");
1960 return false;
1962 packet = cast_ptr(struct console_write_packet *, &packet->u.v.end);
1963 goto next;
1965 default: {
1966 internal(file_line, "os_write_console_packet: invalid type %d", (int)packet->type);
1967 break;
1970 return true;
1974 dir_handle_t os_dir_root(ajla_error_t *err)
1976 unsigned bit;
1977 DWORD drv;
1978 char *d = str_dup(" :\\", -1, err);
1979 if (unlikely(!d))
1980 return NULL;
1981 drv = GetLogicalDrives();
1982 if (unlikely(!drv))
1983 drv = 4;
1984 if (drv & ~3U)
1985 drv &= ~3U;
1986 bit = low_bit(drv);
1987 d[0] = 'A' + bit;
1988 return d;
1991 dir_handle_t os_dir_cwd(ajla_error_t *err)
1993 DWORD buf_size, data_size;
1994 char *buf = NULL; /* avoid warning */
1995 WCHAR *wbuf = NULL; /* avoid warning */
1997 if (is_winnt()) {
1998 buf_size = GetCurrentDirectoryW(0, NULL);
1999 } else {
2000 buf_size = GetCurrentDirectoryA(0, NULL);
2002 if (unlikely(!buf_size)) {
2003 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2004 fatal_mayfail(e, err, "can't get current directory");
2005 return dir_none;
2008 again:
2009 if (is_winnt()) {
2010 wbuf = mem_alloc_mayfail(WCHAR *, buf_size * sizeof(WCHAR), err);
2011 if (unlikely(!wbuf))
2012 return dir_none;
2013 data_size = GetCurrentDirectoryW(buf_size, wbuf);
2014 } else {
2015 buf = mem_alloc_mayfail(char *, buf_size, err);
2016 if (unlikely(!buf))
2017 return dir_none;
2018 data_size = GetCurrentDirectoryA(buf_size, buf);
2020 if (unlikely(!data_size)) {
2021 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2022 mem_free(is_winnt() ? cast_ptr(void *, wbuf) : cast_ptr(void *, buf));
2023 fatal_mayfail(e, err, "can't get current directory");
2024 return dir_none;
2026 if (unlikely(data_size > buf_size)) {
2027 mem_free(is_winnt() ? cast_ptr(void *, wbuf) : cast_ptr(void *, buf));
2028 buf_size = data_size;
2029 goto again;
2031 if (is_winnt()) {
2032 buf = wchar_to_utf8(NULL, wbuf, err);
2033 mem_free(wbuf);
2035 return buf;
2038 dir_handle_t os_dir_open(dir_handle_t dir, const char *path, int attr_unused flags, ajla_error_t *err)
2040 char *joined;
2041 DWORD gle;
2042 DWORD attr;
2043 joined = os_join_paths(dir, path, true, err);
2044 if (unlikely(!joined))
2045 return dir_none;
2046 if (is_winnt()) {
2047 WCHAR *w = utf8_to_wchar(joined, err);
2048 if (unlikely(!w)) {
2049 mem_free(joined);
2050 return NULL;
2052 attr = GetFileAttributesW(w);
2053 gle = GetLastError();
2054 mem_free(w);
2055 } else {
2056 attr = GetFileAttributesA(joined);
2057 gle = GetLastError();
2059 if (unlikely(attr == INVALID_FILE_ATTRIBUTES)) {
2060 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
2061 mem_free(joined);
2062 fatal_mayfail(e, err, "can't open directory");
2063 return dir_none;
2065 if (unlikely(!(attr & FILE_ATTRIBUTE_DIRECTORY))) {
2066 ajla_error_t e = error_ajla_system(EC_SYSCALL, SYSTEM_ERROR_ENOTDIR);
2067 mem_free(joined);
2068 fatal_mayfail(e, err, "can't open directory");
2069 return dir_none;
2071 return joined;
2074 void os_dir_close(dir_handle_t h)
2076 mem_free(h);
2079 char *os_dir_path(dir_handle_t h, ajla_error_t *err)
2081 return str_dup(h, -1, err);
2084 static bool add_file_name(char *a, char ***files, size_t *n_files, ajla_error_t *err)
2086 void *err_ptr;
2087 if (unlikely(!strcmp(a, ".")) ||
2088 unlikely(!strcmp(a, ".."))) {
2089 mem_free(a);
2090 return true;
2092 if (unlikely(!array_add_mayfail(char *, files, n_files, a, &err_ptr, err))) {
2093 *files = err_ptr;
2094 mem_free(a);
2095 return false;
2097 return true;
2100 bool os_dir_read(dir_handle_t h, char ***files, size_t *n_files, ajla_error_t *err)
2102 HANDLE hdir;
2103 BOOL b;
2104 DWORD gle;
2105 char *fn;
2106 union {
2107 WIN32_FIND_DATAA find_data_a;
2108 WIN32_FIND_DATAW find_data_w;
2109 } u;
2111 if (unlikely(!array_init_mayfail(char *, files, n_files, err)))
2112 return false;
2114 fn = os_join_paths(h, "*", false, err);
2115 if (unlikely(!fn))
2116 goto ret_false;
2118 if (is_winnt()) {
2119 WCHAR *w = utf8_to_wchar(fn, err);
2120 if (unlikely(!w)) {
2121 mem_free(fn);
2122 goto ret_false;
2124 mem_free(fn);
2125 hdir = FindFirstFileW(w, &u.find_data_w);
2126 gle = GetLastError();
2127 mem_free(w);
2128 } else {
2129 hdir = FindFirstFileA(fn, &u.find_data_a);
2130 gle = GetLastError();
2131 mem_free(fn);
2134 if (unlikely(hdir == INVALID_HANDLE_VALUE)) {
2135 ajla_error_t e;
2136 if (/*likely(gle == ERROR_FILE_NOT_FOUND) ||*/ likely(gle == ERROR_NO_MORE_FILES))
2137 return true;
2138 e = error_from_win32(EC_SYSCALL, gle);
2139 fatal_mayfail(e, err, "can't read directory");
2140 goto ret_false;
2143 loop:
2144 if (is_winnt()) {
2145 char *a = wchar_to_utf8(NULL, u.find_data_w.cFileName, err);
2146 if (unlikely(!a))
2147 goto close_h_ret_false;
2148 if (unlikely(!add_file_name(a, files, n_files, err)))
2149 goto close_h_ret_false;
2150 } else {
2151 char *a = str_dup(u.find_data_a.cFileName, -1, err);
2152 if (unlikely(!a))
2153 goto close_h_ret_false;
2154 if (unlikely(!add_file_name(a, files, n_files, err)))
2155 goto close_h_ret_false;
2158 if (is_winnt())
2159 b = FindNextFileW(hdir, &u.find_data_w);
2160 else
2161 b = FindNextFileA(hdir, &u.find_data_a);
2162 if (unlikely(!b)) {
2163 ajla_error_t e;
2164 DWORD gle = GetLastError();
2165 if (/*likely(gle == ERROR_FILE_NOT_FOUND) ||*/ likely(gle == ERROR_NO_MORE_FILES)) {
2166 if (unlikely(!FindClose(hdir)))
2167 internal(file_line, "FindClose failed: %u", GetLastError());
2168 return true;
2170 e = error_from_win32(EC_SYSCALL, gle);
2171 fatal_mayfail(e, err, "can't read directory");
2172 goto close_h_ret_false;
2174 goto loop;
2177 close_h_ret_false:
2178 if (unlikely(!FindClose(hdir)))
2179 internal(file_line, "FindClose failed: %u", GetLastError());
2180 ret_false:
2181 os_dir_free(*files, *n_files);
2182 return false;
2185 void os_dir_free(char **files, size_t n_files)
2187 size_t i;
2188 for (i = 0; i < n_files; i++)
2189 mem_free(files[i]);
2190 mem_free(files);
2193 unsigned os_dev_t_major(dev_t attr_unused dev)
2195 return 0;
2198 unsigned os_dev_t_minor(dev_t attr_unused dev)
2200 return 0;
2203 ajla_time_t os_time_t_to_ajla_time(os_time_t tick)
2205 return tick / 10 - (int64_t)116444736 * (int64_t)100000000;
2208 static os_time_t ajla_time_to_os_time(ajla_time_t a)
2210 return 10 * a + (int64_t)10 * (int64_t)116444736 * (int64_t)100000000;
2213 static uint64_t get_win32_time(const FILETIME *ft)
2215 /*debug("get_win32_time: %llx", ((uint64_t)ft->dwHighDateTime << 32) | ft->dwLowDateTime);*/
2216 return ((uint64_t)ft->dwHighDateTime << 32) | ft->dwLowDateTime;
2219 static void make_win32_filetime(uint64_t t, FILETIME *ft)
2221 ft->dwLowDateTime = t;
2222 ft->dwHighDateTime = t >> 32;
2225 bool os_fstat(handle_t h, os_stat_t *st, ajla_error_t *err)
2227 BY_HANDLE_FILE_INFORMATION info;
2229 memset(st, 0, sizeof(*st));
2231 switch (h->type) {
2232 case FILE_TYPE_DISK: st->st_mode = S_IFREG; break;
2233 case FILE_TYPE_CHAR: st->st_mode = S_IFCHR; break;
2234 case FILE_TYPE_PIPE: st->st_mode = S_IFIFO; break;
2235 default: st->st_mode = S_IFCHR; break;
2238 if (unlikely(!GetFileInformationByHandle(h->h, &info))) {
2239 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2240 fatal_mayfail(e, err, "GetFileInformationByHandle returned an error: %s", error_decode(e));
2241 return false;
2244 st->st_dev = info.dwVolumeSerialNumber;
2245 st->st_ino = ((uint64_t)info.nFileIndexHigh << 32) | info.nFileIndexLow;
2246 st->st_size = ((uint64_t)info.nFileSizeHigh << 32) | info.nFileSizeLow;
2247 st->st_blocks = round_up(st->st_size, 4096) / 512;
2248 st->st_blksize = 4096;
2249 st->st_nlink = info.nNumberOfLinks;
2250 st->st_ctime = get_win32_time(&info.ftCreationTime);
2251 st->st_atime = get_win32_time(&info.ftLastAccessTime);
2252 st->st_mtime = get_win32_time(&info.ftLastWriteTime);
2254 if (unlikely(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
2255 st->st_mode |= 0444;
2256 else
2257 st->st_mode |= 0666;
2259 return true;
2262 bool os_stat(dir_handle_t dir, const char *path, bool attr_unused lnk, os_stat_t *st, ajla_error_t *err)
2264 HANDLE hdir;
2265 DWORD gle;
2266 union {
2267 WIN32_FIND_DATAA find_data_a;
2268 WIN32_FIND_DATAW find_data_w;
2269 } u;
2271 ajla_error_t sink;
2272 dir_handle_t dh;
2274 memset(st, 0, sizeof(*st));
2276 dh = os_dir_open(dir, path, 0, &sink);
2277 if (dir_handle_is_valid(dh)) {
2278 st->st_mode = S_IFDIR | 0777;
2279 } else {
2280 handle_t fh = os_open(dir, path, O_RDONLY, 0, err);
2281 if (handle_is_valid(fh)) {
2282 bool succ = os_fstat(fh, st, err);
2283 os_close(fh);
2284 return succ;
2286 st->st_mode = S_IFREG | 0666;
2287 dh = os_join_paths(dir, path, false, err);
2288 if (unlikely(!dh))
2289 return false;
2292 if (unlikely(strchr(dh, '*') != NULL) || unlikely(strchr(dh, '?') != NULL)) {
2293 ajla_error_t e = error_ajla_system(EC_SYSCALL, SYSTEM_ERROR_ENOENT);
2294 fatal_mayfail(e, err, "can't open file '%s': %s", dh, error_decode(e));
2295 os_dir_close(dh);
2296 return false;
2299 if (is_winnt()) {
2300 WCHAR *w = utf8_to_wchar(dh, err);
2301 if (unlikely(!w)) {
2302 os_dir_close(dh);
2303 return false;
2305 hdir = FindFirstFileW(w, &u.find_data_w);
2306 gle = GetLastError();
2307 mem_free(w);
2308 } else {
2309 hdir = FindFirstFileA(dh, &u.find_data_a);
2310 gle = GetLastError();
2312 if (unlikely(hdir == INVALID_HANDLE_VALUE)) {
2313 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
2314 fatal_mayfail(e, err, "can't open file '%s': %s", dh, error_decode(e));
2315 os_dir_close(dh);
2316 return false;
2319 st->st_nlink = 1;
2321 if (is_winnt()) {
2322 st->st_size = ((uint64_t)u.find_data_w.nFileSizeHigh << 32) | u.find_data_a.nFileSizeLow;
2323 st->st_ctime = get_win32_time(&u.find_data_w.ftCreationTime);
2324 st->st_atime = get_win32_time(&u.find_data_w.ftLastAccessTime);
2325 st->st_mtime = get_win32_time(&u.find_data_w.ftLastWriteTime);
2326 if (unlikely(u.find_data_w.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
2327 st->st_mode &= ~0222;
2328 } else {
2329 st->st_size = ((uint64_t)u.find_data_a.nFileSizeHigh << 32) | u.find_data_a.nFileSizeLow;
2330 st->st_ctime = get_win32_time(&u.find_data_a.ftCreationTime);
2331 st->st_atime = get_win32_time(&u.find_data_a.ftLastAccessTime);
2332 st->st_mtime = get_win32_time(&u.find_data_a.ftLastWriteTime);
2333 if (unlikely(u.find_data_a.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
2334 st->st_mode &= ~0222;
2336 st->st_blocks = round_up(st->st_size, 4096) / 512;
2337 st->st_blksize = 4096;
2339 if (unlikely(!FindClose(hdir)))
2340 internal(file_line, "FindClose failed: %u", GetLastError());
2342 os_dir_close(dh);
2343 return true;
2346 bool os_fstatvfs(handle_t h, os_statvfs_t *st, ajla_error_t *err)
2348 if (unlikely(!h->file_name[0])) {
2349 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "statvfs not supported");
2350 return false;
2352 return os_dstatvfs(h->file_name, st, err);
2355 bool os_dstatvfs(dir_handle_t dir, os_statvfs_t *st, ajla_error_t *err)
2357 size_t len;
2358 char *disk;
2359 BOOL b;
2360 BOOL b2;
2361 DWORD spc, bps, freecl, totalcl;
2362 DWORD gle;
2363 ULARGE_INTEGER availb, totalb, freeb;
2364 availb.QuadPart = 0; /* avoid warning */
2365 totalb.QuadPart = 0; /* avoid warning */
2366 freeb.QuadPart = 0; /* avoid warning */
2367 if ((dir[0] == '/' || dir[0] == '\\') && (dir[1] == '/' || dir[1] == '\\')) {
2368 len = 2;
2369 len += strcspn(dir + 2, "/\\");
2370 if (dir[len]) {
2371 len++;
2372 len += strcspn(dir + len, "/\\");
2373 if (dir[len])
2374 len++;
2376 } else if ((dir[0] & 0xDF) >= 'A' && (dir[0] & 0xDF) <= 'Z' && dir[1] == ':' && (dir[2] == '/' || dir[2] == '\\')) {
2377 len = 3;
2378 } else {
2379 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "statvfs not on this directory");
2380 return false;
2382 disk = str_dup(dir, len, err);
2383 if (unlikely(!disk))
2384 return false;
2385 if (is_winnt()) {
2386 WCHAR *w = utf8_to_wchar(disk, err);
2387 if (unlikely(!w)) {
2388 mem_free(disk);
2389 return false;
2391 b2 = FALSE;
2392 if (fn_GetDiskFreeSpaceExW)
2393 b2 = fn_GetDiskFreeSpaceExW(w, &availb, &totalb, &freeb);
2394 b = GetDiskFreeSpaceW(w, &spc, &bps, &freecl, &totalcl);
2395 gle = GetLastError();
2396 mem_free(w);
2397 } else {
2398 b2 = FALSE;
2399 if (fn_GetDiskFreeSpaceExA)
2400 b2 = fn_GetDiskFreeSpaceExA(disk, &availb, &totalb, &freeb);
2401 b = GetDiskFreeSpaceA(disk, &spc, &bps, &freecl, &totalcl);
2402 gle = GetLastError();
2404 if (unlikely(!b)) {
2405 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
2406 fatal_mayfail(e, err, "can't get disk '%s' free space: %s", disk, error_decode(e));
2407 mem_free(disk);
2408 return false;
2410 memset(st, 0, sizeof(os_statvfs_t));
2411 st->f_bsize = bps * spc;
2412 st->f_frsize = bps;
2413 st->f_blocks = (uint64_t)totalcl * spc;
2414 st->f_bfree = st->f_bavail = (uint64_t)freecl * spc;
2415 st->f_fsid = disk[0];
2416 st->f_namemax = 255;
2417 if (b2) {
2418 st->f_blocks = totalb.QuadPart / st->f_frsize;
2419 st->f_bfree = freeb.QuadPart / st->f_frsize;
2420 st->f_bavail = availb.QuadPart / st->f_frsize;
2422 mem_free(disk);
2423 return true;
2426 char *os_readlink(dir_handle_t attr_unused dir, const char attr_unused *path, ajla_error_t *err)
2428 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "readlink not supported");
2429 return NULL;
2432 bool os_dir_action(dir_handle_t dir, const char *path, int action, int attr_unused mode, ajla_time_t dev_major, ajla_time_t dev_minor, const char attr_unused *syml, ajla_error_t *err)
2434 bool ret = false;
2435 BOOL b;
2436 DWORD gle;
2437 char *joined;
2438 WCHAR *joined_w = NULL;
2439 bool allow_trailing_slash = action == IO_Action_Rm_Dir || action == IO_Action_Mk_Dir;
2441 joined = os_join_paths(dir, path, allow_trailing_slash, err);
2442 if (unlikely(!joined))
2443 return false;
2445 if (is_winnt()) {
2446 joined_w = utf8_to_wchar(joined, err);
2447 if (unlikely(!joined_w)) {
2448 mem_free(joined);
2449 goto free_ret;
2453 switch (action) {
2454 case IO_Action_Rm:
2455 if (joined_w)
2456 b = DeleteFileW(joined_w);
2457 else
2458 b = DeleteFileA(joined);
2459 gle = GetLastError();
2460 break;
2461 case IO_Action_Rm_Dir:
2462 if (joined_w)
2463 b = RemoveDirectoryW(joined_w);
2464 else
2465 b = RemoveDirectoryA(joined);
2466 gle = GetLastError();
2467 break;
2468 case IO_Action_Mk_Dir:
2469 if (joined_w)
2470 b = CreateDirectoryW(joined_w, NULL);
2471 else
2472 b = CreateDirectoryA(joined, NULL);
2473 gle = GetLastError();
2474 if (gle == ERROR_ACCESS_DENIED) {
2475 if ((joined[0] & 0xdf) >= 'A' && (joined[0] & 0xdf) <= 'Z' && joined[1] == ':' && joined[2] && !joined[2 + strspn(joined + 2, "\\/")])
2476 gle = ERROR_ALREADY_EXISTS;
2478 break;
2479 case IO_Action_Mk_Pipe:
2480 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkpipe not supported");
2481 goto free_ret;
2482 case IO_Action_Mk_Socket:
2483 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksocket not supported");
2484 goto free_ret;
2485 case IO_Action_Mk_CharDev:
2486 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
2487 goto free_ret;
2488 case IO_Action_Mk_BlockDev:
2489 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
2490 goto free_ret;
2491 case IO_Action_Mk_SymLink:
2492 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksymlink not supported");
2493 goto free_ret;
2494 case IO_Action_ChMod:
2495 case IO_Action_ChOwn:
2496 case IO_Action_LChOwn:
2497 if (joined_w)
2498 b = GetFileAttributesW(joined_w) != INVALID_FILE_ATTRIBUTES;
2499 else
2500 b = GetFileAttributesA(joined) != INVALID_FILE_ATTRIBUTES;
2501 gle = GetLastError();
2502 break;
2503 case IO_Action_UTime:
2504 case IO_Action_LUTime: {
2505 HANDLE hfile;
2506 FILETIME modft, accft;
2507 make_win32_filetime(ajla_time_to_os_time(dev_major), &modft);
2508 make_win32_filetime(ajla_time_to_os_time(dev_minor), &accft);
2509 if (joined_w)
2510 hfile = CreateFileW(joined_w, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2511 else
2512 hfile = CreateFileA(joined, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2513 if (unlikely(hfile == INVALID_HANDLE_VALUE)) {
2514 gle = GetLastError();
2515 goto return_error;
2517 b = SetFileTime(hfile, NULL, &accft, &modft);
2518 gle = GetLastError();
2519 win32_close_handle(hfile);
2520 break;
2522 default:
2523 internal(file_line, "os_dir_action: invalid action %d", action);
2526 if (unlikely(!b)) {
2527 ajla_error_t e;
2528 return_error:
2529 e = error_from_win32(EC_SYSCALL, gle);
2530 fatal_mayfail(e, err, "can't perform action %d on '%s': %s", action, joined, error_decode(e));
2531 goto free_ret;
2534 ret = true;
2536 free_ret:
2537 if (joined_w)
2538 mem_free(joined_w);
2539 mem_free(joined);
2540 return ret;
2543 bool os_dir2_action(dir_handle_t dest_dir, const char *dest_path, int action, dir_handle_t src_dir, const char *src_path, ajla_error_t *err)
2545 bool ret = false;
2546 BOOL b;
2547 DWORD gle;
2548 char *dest_joined = NULL, *src_joined = NULL;
2549 WCHAR *dest_joined_w = NULL, *src_joined_w = NULL;
2551 dest_joined = os_join_paths(dest_dir, dest_path, false, err);
2552 if (unlikely(!dest_joined))
2553 goto free_ret;
2555 src_joined = os_join_paths(src_dir, src_path, false, err);
2556 if (unlikely(!src_joined))
2557 goto free_ret;
2558 if (is_winnt()) {
2559 dest_joined_w = utf8_to_wchar(dest_joined, err);
2560 if (unlikely(!dest_joined_w)) {
2561 mem_free(dest_joined);
2562 goto free_ret;
2564 src_joined_w = utf8_to_wchar(src_joined, err);
2565 if (unlikely(!src_joined_w)) {
2566 mem_free(src_joined);
2567 goto free_ret;
2571 switch (action) {
2572 case IO_Action_Mk_Link:
2573 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mklink not supported");
2574 goto free_ret;
2575 case IO_Action_Rename:
2576 if (dest_joined_w) {
2577 if (unlikely(!fn_MoveFileExW)) {
2578 move_file_w:
2579 if (unlikely(GetFileAttributesW(src_joined_w) == INVALID_FILE_ATTRIBUTES)) {
2580 gle = GetLastError();
2581 b = FALSE;
2582 goto return_error;
2584 DeleteFileW(dest_joined_w);
2585 b = MoveFileW(src_joined_w, dest_joined_w);
2586 gle = GetLastError();
2587 } else {
2588 b = fn_MoveFileExW(src_joined_w, dest_joined_w, MOVEFILE_REPLACE_EXISTING);
2589 gle = GetLastError();
2590 if (unlikely(!b) && gle == ERROR_CALL_NOT_IMPLEMENTED)
2591 goto move_file_w;
2593 } else {
2594 if (unlikely(!fn_MoveFileExA)) {
2595 move_file:
2596 if (unlikely(GetFileAttributesA(src_joined) == INVALID_FILE_ATTRIBUTES)) {
2597 gle = GetLastError();
2598 b = FALSE;
2599 goto return_error;
2601 DeleteFileA(dest_joined);
2602 b = MoveFileA(src_joined, dest_joined);
2603 gle = GetLastError();
2604 } else {
2605 b = fn_MoveFileExA(src_joined, dest_joined, MOVEFILE_REPLACE_EXISTING);
2606 gle = GetLastError();
2607 if (unlikely(!b) && gle == ERROR_CALL_NOT_IMPLEMENTED)
2608 goto move_file;
2611 break;
2612 default:
2613 internal(file_line, "os_dir2_action: invalid action %d", action);
2615 if (unlikely(!b)) {
2616 ajla_error_t e;
2617 return_error:
2618 e = error_from_win32(EC_SYSCALL, gle);
2619 fatal_mayfail(e, err, "can't perform action %d on '%s' and '%s': %s", action, src_joined, dest_joined, error_decode(e));
2620 goto free_ret;
2623 ret = true;
2625 free_ret:
2626 if (dest_joined)
2627 mem_free(dest_joined);
2628 if (dest_joined_w)
2629 mem_free(dest_joined_w);
2630 if (src_joined)
2631 mem_free(src_joined);
2632 if (src_joined_w)
2633 mem_free(src_joined_w);
2634 return ret;
2637 bool os_drives(char **drives, size_t *drives_l, ajla_error_t *err)
2639 uint32_t mask = GetLogicalDrives();
2640 return os_drives_bitmap(mask, drives, drives_l, err);
2644 bool os_tcgetattr(handle_t h, os_termios_t *t, ajla_error_t attr_unused *err)
2646 t->tc_flags = h->tc_flags;
2647 return true;
2650 bool os_tcsetattr(handle_t h, const os_termios_t *t, ajla_error_t attr_unused *err)
2652 h->tc_flags = t->tc_flags;
2653 if (h->is_console) {
2654 if (t->tc_flags & IO_Stty_Flag_Nosignal)
2655 SetConsoleCtrlHandler(NULL, TRUE);
2656 else
2657 SetConsoleCtrlHandler(NULL, FALSE);
2659 return true;
2662 void os_tcflags(os_termios_t *t, int flags)
2664 t->tc_flags = flags;
2667 bool os_tty_size(handle_t h, int *nx, int *ny, int *ox, int *oy, ajla_error_t *err)
2669 CONSOLE_SCREEN_BUFFER_INFO csbi;
2671 if (h->is_console && (h->flags & 3) == O_RDONLY) {
2672 handle_t h1;
2673 h1 = os_get_std_handle(1);
2674 if (h1->is_console) {
2675 h = h1;
2676 goto have_h;
2678 h1 = os_get_std_handle(2);
2679 if (h1->is_console) {
2680 h = h1;
2681 goto have_h;
2685 have_h:
2686 if (!GetConsoleScreenBufferInfo(h->h, &csbi)) {
2687 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2688 fatal_mayfail(e, err, "GetConsoleScreenBufefrInfo failed: %s", error_decode(e));
2689 return false;
2692 *nx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
2693 *ny = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
2694 *ox = csbi.srWindow.Left;
2695 *oy = csbi.srWindow.Top;
2697 store_relaxed(&window_offset_x, csbi.srWindow.Left);
2698 store_relaxed(&window_offset_y, csbi.srWindow.Top);
2700 return true;
2704 const char *os_get_flavor(void)
2706 return "Windows";
2709 void os_get_uname(os_utsname_t *un)
2711 OSVERSIONINFOA osva;
2712 OSVERSIONINFOW osvw;
2713 unsigned platform, major, minor, build;
2715 memset(un, 0, sizeof(os_utsname_t));
2716 strcpy(un->sysname, "Windows");
2718 if (fn_RtlGetVersion) {
2719 memset(&osvw, 0, sizeof osvw);
2720 osvw.dwOSVersionInfoSize = sizeof osvw;
2721 if (unlikely(fn_RtlGetVersion(&osvw)))
2722 fatal("RtlGetVersion failed");
2724 platform = osvw.dwPlatformId;
2725 major = osvw.dwMajorVersion;
2726 minor = osvw.dwMinorVersion;
2727 build = osvw.dwBuildNumber;
2728 } else {
2729 memset(&osva, 0, sizeof osva);
2730 osva.dwOSVersionInfoSize = sizeof osva;
2731 if (unlikely(!GetVersionExA(&osva)))
2732 fatal("GetVersionExA failed: %u", GetLastError());
2734 platform = osva.dwPlatformId;
2735 major = osva.dwMajorVersion;
2736 minor = osva.dwMinorVersion;
2737 build = osva.dwBuildNumber;
2740 switch (platform) {
2741 case VER_PLATFORM_WIN32s:
2742 sprintf(un->release, "%u.%u", major, minor);
2743 break;
2744 case VER_PLATFORM_WIN32_WINDOWS:
2745 if (minor < 10) {
2746 if (build < 1111)
2747 strcpy(un->release, "95");
2748 else
2749 strcpy(un->release, "95 OSR2");
2750 } else if (minor == 10) {
2751 if (build < 2222)
2752 strcpy(un->release, "98");
2753 else
2754 strcpy(un->release, "98 SE");
2755 } else if (minor == 90) {
2756 strcpy(un->release, "ME");
2757 } else {
2758 sprintf(un->release, "%u.%u", major, minor);
2760 break;
2761 case VER_PLATFORM_WIN32_NT:
2762 if (major == 5 && minor == 0) {
2763 strcpy(un->release, "2000");
2764 } else if (major == 5 && minor == 1) {
2765 strcpy(un->release, "XP");
2766 } else if (major == 5 && minor == 2) {
2767 strcpy(un->release, "Server 2003");
2768 } else if (major == 6 && minor == 0) {
2769 strcpy(un->release, "Vista");
2770 } else if (major == 6 && minor == 1) {
2771 strcpy(un->release, "7");
2772 } else if (major == 6 && minor == 2) {
2773 strcpy(un->release, "8");
2774 } else if (major == 6 && minor == 3) {
2775 strcpy(un->release, "8.1");
2776 } else if (major == 10 && minor == 0) {
2777 if (build < 22000)
2778 strcpy(un->release, "10");
2779 else
2780 strcpy(un->release, "11");
2781 } else {
2782 sprintf(un->release, "NT %u.%u", major, minor);
2784 break;
2787 if (fn_RtlGetVersion) {
2788 ajla_error_t sink;
2789 char *u = wchar_to_utf8(NULL, osvw.szCSDVersion, &sink);
2790 if (u) {
2791 strncpy(un->version, u, sizeof un->version - 1);
2792 mem_free(u);
2794 } else {
2795 strncpy(un->version, osva.szCSDVersion, sizeof un->version - 1);
2798 #ifdef ARCH_NAME
2799 strcpy(un->machine, ARCH_NAME);
2800 #endif
2803 char *os_get_host_name(ajla_error_t *err)
2805 if (fn_GetNetworkParams) {
2806 ULONG buf_len;
2807 DWORD dw;
2808 dw = fn_GetNetworkParams(NULL, &buf_len);
2809 if (dw == ERROR_BUFFER_OVERFLOW) {
2810 char *buffer;
2811 retry:
2812 buffer = mem_alloc_mayfail(char *, buf_len, err);
2813 if (unlikely(!buffer))
2814 return NULL;
2815 dw = fn_GetNetworkParams(buffer, &buf_len);
2816 if (likely(dw == ERROR_SUCCESS))
2817 return buffer;
2818 mem_free(buffer);
2819 if (dw == ERROR_BUFFER_OVERFLOW)
2820 goto retry;
2823 return str_dup("", -1, err);
2827 static char *os_path_to_exe;
2829 static void os_init_path_to_exe(void)
2831 DWORD r;
2832 DWORD s = 16;
2833 size_t i, j;
2834 again:
2835 if (is_winnt()) {
2836 WCHAR *w = mem_alloc(WCHAR *, s * sizeof(WCHAR));
2837 r = GetModuleFileNameW(NULL, w, s);
2838 if (unlikely(!r)) {
2839 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2840 fatal("GetModuleFileNameW returned an error: %s", error_decode(e));
2842 if (r >= s - 1) {
2843 mem_free(w);
2844 s *= 2;
2845 if (unlikely(!s))
2846 fatal("GetModuleFileNameW overflow");
2847 goto again;
2849 os_path_to_exe = wchar_to_utf8(NULL, w, NULL);
2850 mem_free(w);
2851 } else {
2852 os_path_to_exe = mem_alloc(char *, s);
2853 r = GetModuleFileNameA(NULL, os_path_to_exe, s);
2854 if (unlikely(!r)) {
2855 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
2856 fatal("GetModuleFileNameA returned an error: %s", error_decode(e));
2858 if (r >= s - 1) {
2859 mem_free(os_path_to_exe);
2860 s *= 2;
2861 if (unlikely(!s))
2862 fatal("GetModuleFileNameA overflow");
2863 goto again;
2867 j = 0;
2868 for (i = 0; os_path_to_exe[i]; i++)
2869 if (os_is_path_separator(os_path_to_exe[i]))
2870 j = i + 1;
2871 os_path_to_exe[j] = 0;
2874 const char *os_get_path_to_exe(void)
2876 return os_path_to_exe;
2880 ajla_time_t os_time_real(void)
2882 FILETIME ft;
2883 GetSystemTimeAsFileTime(&ft);
2884 return os_time_t_to_ajla_time(get_win32_time(&ft));
2887 static mutex_t tick_mutex;
2888 static DWORD tick_last;
2889 static DWORD tick_high;
2891 ajla_time_t os_time_monotonic(void)
2893 DWORD t;
2894 int64_t ret;
2896 #ifdef USER_QPC
2897 if (likely(fn_QueryPerformanceCounter != NULL)) {
2898 LARGE_INTEGER li;
2899 if (likely(fn_QueryPerformanceCounter(&li))) {
2900 int64_t c = li.LowPart + ((uint64_t)li.HighPart << 32);
2901 c = (long double)c * perf_multiplier;
2902 return c;
2905 #endif
2907 if (likely(fn_GetTickCount64 != NULL)) {
2908 ret = fn_GetTickCount64();
2909 } else {
2910 if (likely(os_threads_initialized))
2911 mutex_lock(&tick_mutex);
2912 t = GetTickCount();
2913 if (unlikely(t < tick_last))
2914 tick_high++;
2915 tick_last = t;
2916 ret = ((uint64_t)tick_high << 32) | t;
2917 if (likely(os_threads_initialized))
2918 mutex_unlock(&tick_mutex);
2920 return ret * 1000;
2924 static int get_socket_error(handle_t h)
2926 int r;
2927 int e;
2928 socklen_t e_l = sizeof(int);
2930 r = getsockopt(h->s, SOL_SOCKET, SO_ERROR, cast_ptr(char *, &e), &e_l);
2931 if (unlikely(r == SOCKET_ERROR)) {
2932 int er = WSAGetLastError();
2933 internal(file_line, "getsockopt returned an error: %d", er);
2936 return e;
2939 void iomux_never(mutex_t **mutex_to_lock, struct list *list_entry)
2941 *mutex_to_lock = address_get_mutex(NULL, DEPTH_THUNK);
2942 list_init(list_entry);
2945 static void win32_notify(void);
2947 void iomux_register_wait(handle_t h, bool wr, mutex_t **mutex_to_lock, struct list *list_entry)
2949 struct win32_io_thread *thr;
2950 address_lock(h, DEPTH_THUNK);
2951 *mutex_to_lock = address_get_mutex(h, DEPTH_THUNK);
2952 list_add(&h->wait_list[wr], list_entry);
2953 if (handle_is_socket(h)) {
2954 struct list *socket_entry = &h->socket_entry[wr];
2955 address_unlock(h, DEPTH_THUNK);
2956 mutex_lock(&socket_list_mutex);
2957 if (socket_entry->next == NULL)
2958 list_add(&socket_list[(int)wr], socket_entry);
2959 mutex_unlock(&socket_list_mutex);
2960 win32_notify();
2961 return;
2963 thr = !wr ? h->rd : h->wr;
2964 if (!wr) {
2965 if (unlikely(thr->buffer_len != 0) || unlikely(thr->packet_is_queued) || unlikely(thr->err != 0) || unlikely(thr->eof))
2966 goto wake_up;
2967 } else {
2968 if (unlikely(thr->buffer_len != WIN32_BUFFER_SIZE) || unlikely(thr->err != 0)) {
2969 goto wake_up;
2972 address_unlock(h, DEPTH_THUNK);
2973 return;
2975 wake_up:
2976 call(wake_up_wait_list)(&h->wait_list[wr], address_get_mutex(h, DEPTH_THUNK), true);
2979 bool iomux_test_handle(handle_t h, bool wr)
2981 if (handle_is_socket(h)) {
2982 int r;
2983 struct fdx_set fd;
2984 struct timeval tv;
2985 if (h->connect_in_progress && likely(wr)) {
2986 int e = get_socket_error(h);
2987 if (e)
2988 return true;
2990 fd.fd_count = 1;
2991 fd.fd_array[0] = h->s;
2992 tv.tv_sec = 0;
2993 tv.tv_usec = 0;
2994 if (!wr)
2995 r = select(FD_SETSIZE, cast_ptr(fd_set *, &fd), NULL, NULL, &tv);
2996 else
2997 r = select(FD_SETSIZE, NULL, cast_ptr(fd_set *, &fd), NULL, &tv);
2998 if (unlikely(r == SOCKET_ERROR))
2999 internal(file_line, "select returned an error: %d", WSAGetLastError());
3000 return !!r;
3003 * os_read/os_write is non-blocking even for standard handles,
3004 * so we don't need this function
3006 return true;
3010 #define HANDLES_PER_THREAD (MAXIMUM_WAIT_OBJECTS - 1)
3012 struct monitor_handle {
3013 HANDLE h;
3014 void (*wake_up)(void *cookie);
3015 void (*cls)(HANDLE h);
3016 void *cookie;
3017 bool needs_close;
3020 struct monitor_thread {
3021 struct list entry;
3022 thread_t thread;
3023 HANDLE wake_up;
3024 bool terminate;
3025 int n_handles;
3026 struct monitor_handle handles[HANDLES_PER_THREAD];
3029 static mutex_t monitor_mutex;
3030 static struct list monotor_threads;
3032 static inline void monitor_lock(void)
3034 mutex_lock(&monitor_mutex);
3037 static inline void monitor_unlock(void)
3039 mutex_unlock(&monitor_mutex);
3042 thread_function_decl(monitor_thread,
3043 struct monitor_thread *mt = arg;
3044 HANDLE handles[HANDLES_PER_THREAD + 1];
3046 monitor_lock();
3047 while (1) {
3048 DWORD r;
3049 int i;
3050 int j;
3051 int n_handles = mt->n_handles;
3052 for (i = j = 0; i < n_handles; i++, j++) {
3053 if (mt->handles[i].needs_close) {
3054 mt->handles[i].cls(mt->handles[i].h);
3055 j--;
3056 mt->n_handles--;
3057 continue;
3059 mt->handles[j] = mt->handles[i];
3060 handles[j] = mt->handles[j].h;
3062 handles[j] = mt->wake_up;
3063 if (mt->terminate)
3064 break;
3065 monitor_unlock();
3066 r = WaitForMultipleObjects(j + 1, handles, FALSE, INFINITE);
3067 if (unlikely(r == WAIT_FAILED))
3068 internal(file_line, "WaitForMultipleObjects failed: %u", GetLastError());
3069 monitor_lock();
3070 if (r >= WAIT_OBJECT_0 + zero && likely(r < WAIT_OBJECT_0 + j)) {
3071 i = r - WAIT_OBJECT_0;
3072 if (i < mt->n_handles && !mt->handles[i].needs_close) {
3073 void (*wake_up)(void *cookie) = mt->handles[i].wake_up;
3074 void *cookie = mt->handles[i].cookie;
3075 memmove(&mt->handles[i], &mt->handles[i + 1], (mt->n_handles - (i + 1)) * sizeof(struct monitor_handle));
3076 mt->n_handles--;
3077 wake_up(cookie);
3078 monitor_lock();
3082 monitor_unlock();
3085 static bool monitor_handle(HANDLE h, void (*wake_up)(void *cookie), void (*cls)(HANDLE h), void *cookie, ajla_error_t *err)
3087 struct list *l;
3088 struct monitor_thread *mt;
3089 int n;
3091 monitor_lock();
3093 list_for_each(l, &monotor_threads) {
3094 mt = get_struct(l, struct monitor_thread, entry);
3095 n = mt->n_handles;
3096 if (likely(n < HANDLES_PER_THREAD)) {
3097 if (unlikely(l != monotor_threads.next)) {
3098 list_del(&mt->entry);
3099 list_add(&monotor_threads, &mt->entry);
3101 goto setup_handle;
3105 mt = mem_calloc_mayfail(struct monitor_thread *, sizeof(struct monitor_thread), err);
3106 if (unlikely(!mt))
3107 goto fail;
3108 mt->wake_up = CreateEventA(NULL, FALSE, FALSE, NULL);
3109 if (!mt->wake_up) {
3110 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
3111 fatal_mayfail(e, err, "can't create event: %s", error_decode(e));
3112 goto fail;
3114 if (unlikely(!thread_spawn(&mt->thread, monitor_thread, mt, PRIORITY_IO, err))) {
3115 goto fail;
3117 n = 0;
3118 list_add(&monotor_threads, &mt->entry);
3120 setup_handle:
3121 mt->handles[n].h = h;
3122 mt->handles[n].wake_up = wake_up;
3123 mt->handles[n].cls = cls;
3124 mt->handles[n].cookie = cookie;
3125 mt->handles[n].needs_close = false;
3126 mt->n_handles = n + 1;
3127 win32_set_event(mt->wake_up);
3129 monitor_unlock();
3131 return true;
3133 fail:
3134 if (mt) {
3135 if (mt->wake_up)
3136 win32_close_handle(mt->wake_up);
3137 mem_free(mt);
3139 return false;
3142 static void monitor_close(HANDLE h)
3144 struct list *l;
3145 struct monitor_thread *mt;
3146 int n;
3148 list_for_each(l, &monotor_threads) {
3149 mt = get_struct(l, struct monitor_thread, entry);
3150 for (n = 0; n < mt->n_handles; n++) {
3151 if (h == mt->handles[n].h)
3152 goto found;
3155 return;
3157 found:
3158 mt->handles[n].needs_close = true;
3160 win32_set_event(mt->wake_up);
3163 static void monitor_init(void)
3165 list_init(&monotor_threads);
3166 mutex_init(&monitor_mutex);
3169 static void monitor_done(void)
3171 monitor_lock();
3172 while (!list_is_empty(&monotor_threads)) {
3173 struct monitor_thread *mt = get_struct(monotor_threads.next, struct monitor_thread, entry);
3174 mt->terminate = true;
3175 win32_set_event(mt->wake_up);
3176 monitor_unlock();
3177 thread_join(&mt->thread);
3178 monitor_lock();
3179 if (unlikely(mt->n_handles))
3180 internal(file_line, "monitor_done: %d handles were leaked", mt->n_handles);
3181 list_del(&mt->entry);
3182 win32_close_handle(mt->wake_up);
3183 mem_free(mt);
3185 monitor_unlock();
3186 mutex_done(&monitor_mutex);
3190 struct proc_handle {
3191 HANDLE process_handle;
3192 DWORD exit_code;
3193 bool fired;
3194 struct list wait_list;
3197 static void proc_wait_end(void *ph_)
3199 struct proc_handle *ph = ph_;
3201 ph->fired = true;
3202 if (unlikely(!GetExitCodeProcess(ph->process_handle, &ph->exit_code)))
3203 internal(file_line, "GetExitCodeProcess failed: %u", GetLastError());
3204 win32_close_handle(ph->process_handle);
3205 call(wake_up_wait_list)(&ph->wait_list, &monitor_mutex, true);
3208 static bool proc_addstr(char **ptr, size_t *len, const char *str, bool cvt_slashes, ajla_error_t *err)
3210 size_t i, j, bs;
3211 bool quote;
3212 if (*len)
3213 if (unlikely(!array_add_mayfail(char, ptr, len, ' ', NULL, err)))
3214 return false;
3215 quote = !str[0] || str[strcspn(str, " \t")];
3216 if (unlikely(quote)) {
3217 if (unlikely(!array_add_mayfail(char, ptr, len, '"', NULL, err)))
3218 return false;
3220 bs = 0;
3221 for (i = 0; str[i]; i++) {
3222 char c = str[i];
3223 if (cvt_slashes && c == '/')
3224 c = '\\';
3225 if (c == '\\') {
3226 bs++;
3227 } else if (c == '"') {
3228 for (j = 0; j <= bs; j++)
3229 if (unlikely(!array_add_mayfail(char, ptr, len, '\\', NULL, err)))
3230 return false;
3231 bs = 0;
3232 } else {
3233 bs = 0;
3235 if (unlikely(!array_add_mayfail(char, ptr, len, c, NULL, err)))
3236 return false;
3238 if (unlikely(quote)) {
3239 for (j = 0; j < bs; j++)
3240 if (unlikely(!array_add_mayfail(char, ptr, len, '\\', NULL, err)))
3241 return false;
3242 if (unlikely(!array_add_mayfail(char, ptr, len, '"', NULL, err)))
3243 return false;
3245 return true;
3248 struct proc_handle *os_proc_spawn(dir_handle_t wd, const char *path, size_t n_handles, handle_t *source, int *target, char * const args[], char *envc, ajla_error_t *err)
3250 char *path_cpy;
3251 struct proc_handle *ph;
3252 union {
3253 STARTUPINFOA sti_a;
3254 STARTUPINFOW sti_w;
3255 } u;
3256 DWORD dwCreationFlags = 0;
3257 PROCESS_INFORMATION pi;
3258 BOOL b;
3259 DWORD gle;
3260 char * const *a;
3261 char *ptr;
3262 size_t len;
3263 size_t i;
3265 path_cpy = str_dup(path, -1, err);
3266 if (unlikely(!path_cpy))
3267 return NULL;
3268 for (i = 0; path_cpy[i]; i++)
3269 if (path_cpy[i] == '/')
3270 path_cpy[i] = '\\';
3272 ph = mem_calloc_mayfail(struct proc_handle *, sizeof(struct proc_handle), err);
3273 if (unlikely(!ph))
3274 goto err0;
3276 list_init(&ph->wait_list);
3278 if (!*args) {
3279 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "empty arguments in spawn");
3280 goto err3;
3282 if (unlikely(!array_init_mayfail(char, &ptr, &len, err)))
3283 goto err3;
3284 for (a = args; *a; a++) {
3285 if (!proc_addstr(&ptr, &len, *a, a == args, err))
3286 goto err3;
3288 if (unlikely(!array_add_mayfail(char, &ptr, &len, 0, NULL, err)))
3289 goto err3;
3291 if (is_winnt()) {
3292 memset(&u.sti_w, 0, sizeof u.sti_w);
3293 u.sti_w.cb = sizeof u.sti_w;
3294 u.sti_w.dwFlags = STARTF_USESTDHANDLES;
3295 if (fn_GetEnvironmentStringsW && fn_FreeEnvironmentStringsW) {
3296 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
3298 } else {
3299 memset(&u.sti_a, 0, sizeof u.sti_a);
3300 u.sti_a.cb = sizeof u.sti_a;
3301 u.sti_a.dwFlags = STARTF_USESTDHANDLES;
3303 for (i = 0; i < n_handles; i++) {
3304 HANDLE *t;
3305 if (target[i] == 0) {
3306 t = is_winnt() ? &u.sti_w.hStdInput : &u.sti_a.hStdInput;
3307 } else if (target[i] == 1) {
3308 t = is_winnt() ? &u.sti_w.hStdOutput : &u.sti_a.hStdOutput;
3309 } else if (likely(target[i] == 2)) {
3310 t = is_winnt() ? &u.sti_w.hStdError : &u.sti_a.hStdError;
3311 } else {
3312 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "only the first three handles can be redirected");
3313 goto err4;
3315 if (unlikely(handle_is_socket(source[i]))) {
3316 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "socket can't be inherited by subprocesses on Windows");
3317 goto err4;
3319 if (unlikely(*t != NULL)) {
3320 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "redirecting a handle multiple times");
3321 goto err4;
3323 win32_close_read_thread(source[i]);
3324 if (unlikely(!DuplicateHandle(GetCurrentProcess(), source[i]->h, GetCurrentProcess(), t, 0, TRUE, DUPLICATE_SAME_ACCESS))) {
3325 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
3326 fatal_mayfail(e, err, "can't duplicate handle: %s", error_decode(e));
3327 goto err4;
3330 if (is_winnt()) {
3331 WCHAR *path_cpy_w, *ptr_w, *wd_w;
3332 path_cpy_w = utf8_to_wchar(path_cpy, err);
3333 if (unlikely(!path_cpy_w)) {
3334 goto err4;
3336 ptr_w = utf8_to_wchar(ptr, err);
3337 if (unlikely(!ptr_w)) {
3338 mem_free(path_cpy_w);
3339 goto err4;
3341 wd_w = utf8_to_wchar(wd, err);
3342 if (unlikely(!wd_w)) {
3343 mem_free(path_cpy_w);
3344 mem_free(ptr_w);
3345 goto err4;
3347 if (dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) {
3348 char *e;
3349 WCHAR *uni_env;
3350 size_t uni_env_l;
3351 if (unlikely(!array_init_mayfail(WCHAR, &uni_env, &uni_env_l, err))) {
3352 mem_free(path_cpy_w);
3353 mem_free(ptr_w);
3354 mem_free(wd_w);
3355 goto err4;
3357 e = envc;
3358 while (*e) {
3359 ajla_error_t sink;
3360 size_t i;
3361 WCHAR *wc = utf8_to_wchar(e, &sink);
3362 e += strlen(e) + 1;
3363 if (unlikely(!wc)) {
3364 if (sink.error_class == EC_ASYNC) {
3365 if (!err)
3366 fatal("can't allocate environment memory: %s", error_decode(sink));
3367 *err = sink;
3368 mem_free(uni_env);
3369 mem_free(path_cpy_w);
3370 mem_free(ptr_w);
3371 mem_free(wd_w);
3372 goto err4;
3374 continue;
3376 for (i = 0; wc[i]; i++) ;
3377 if (unlikely(!array_add_multiple_mayfail(WCHAR, &uni_env, &uni_env_l, wc, i + 1, NULL, err))) {
3378 mem_free(wc);
3379 mem_free(path_cpy_w);
3380 mem_free(ptr_w);
3381 mem_free(wd_w);
3382 goto err4;
3384 mem_free(wc);
3386 if (unlikely(!array_add_mayfail(WCHAR, &uni_env, &uni_env_l, 0, NULL, err))) {
3387 mem_free(path_cpy_w);
3388 mem_free(ptr_w);
3389 mem_free(wd_w);
3390 goto err4;
3392 b = CreateProcessW(path_cpy_w, ptr_w, NULL, NULL, TRUE, dwCreationFlags, uni_env, wd_w, &u.sti_w, &pi);
3393 gle = GetLastError();
3394 mem_free(uni_env);
3395 } else {
3396 b = CreateProcessW(path_cpy_w, ptr_w, NULL, NULL, TRUE, dwCreationFlags, envc, wd_w, &u.sti_w, &pi);
3397 gle = GetLastError();
3399 mem_free(path_cpy_w);
3400 mem_free(ptr_w);
3401 mem_free(wd_w);
3402 } else {
3403 b = CreateProcessA(path_cpy, ptr, NULL, NULL, TRUE, dwCreationFlags, envc, wd, &u.sti_a, &pi);
3404 gle = GetLastError();
3406 if (unlikely(!b)) {
3407 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
3408 fatal_mayfail(e, err, "can't create process(%s): %s", path, error_decode(e));
3409 goto err4;
3411 ph->process_handle = pi.hProcess;
3412 win32_close_handle(pi.hThread);
3414 if (unlikely(!monitor_handle(ph->process_handle, proc_wait_end, win32_close_handle, ph, err))) {
3415 goto err4;
3418 for (i = 0; i < 3; i++) {
3419 HANDLE *t;
3420 if (!i)
3421 t = is_winnt() ? &u.sti_w.hStdInput : &u.sti_a.hStdInput;
3422 else if (i == 1)
3423 t = is_winnt() ? &u.sti_w.hStdOutput : &u.sti_a.hStdOutput;
3424 else
3425 t = is_winnt() ? &u.sti_w.hStdError : &u.sti_a.hStdError;
3426 if (*t)
3427 win32_close_handle(*t);
3429 mem_free(ptr);
3430 mem_free(path_cpy);
3431 return ph;
3433 err4:
3434 for (i = 0; i < 3; i++) {
3435 HANDLE *t;
3436 if (!i)
3437 t = is_winnt() ? &u.sti_w.hStdInput : &u.sti_a.hStdInput;
3438 else if (i == 1)
3439 t = is_winnt() ? &u.sti_w.hStdOutput : &u.sti_a.hStdOutput;
3440 else
3441 t = is_winnt() ? &u.sti_w.hStdError : &u.sti_a.hStdError;
3442 if (*t)
3443 win32_close_handle(*t);
3445 mem_free(ptr);
3446 err3:
3447 mem_free(ph);
3448 err0:
3449 mem_free(path_cpy);
3450 return NULL;
3453 void os_proc_free_handle(struct proc_handle *ph)
3455 ajla_assert_lo(list_is_empty(&ph->wait_list), (file_line, "os_proc_free_handle: freeing handle when there are processes waiting for it"));
3456 monitor_lock();
3457 if (!ph->fired)
3458 monitor_close(ph->process_handle);
3459 monitor_unlock();
3460 mem_free(ph);
3463 bool os_proc_register_wait(struct proc_handle *ph, mutex_t **mutex_to_lock, struct list *list_entry, int *status)
3465 monitor_lock();
3466 if (ph->fired) {
3467 *status = ph->exit_code;
3468 monitor_unlock();
3469 return true;
3470 } else {
3471 *mutex_to_lock = &monitor_mutex;
3472 list_add(&ph->wait_list, list_entry);
3473 monitor_unlock();
3474 return false;
3479 static mutex_t signal_mutex;
3481 #define N_SIG 2
3483 static struct {
3484 signal_seq_t seq;
3485 uintptr_t refcount;
3486 struct list wait_list;
3487 } signals[N_SIG];
3489 static BOOL WINAPI signal_handler(DWORD sig)
3491 if (sig >= N_SIG)
3492 return FALSE;
3493 mutex_lock(&signal_mutex);
3494 if (!signals[sig].refcount) {
3495 mutex_unlock(&signal_mutex);
3496 return FALSE;
3498 signals[sig].seq++;
3499 call(wake_up_wait_list)(&signals[sig].wait_list, &signal_mutex, false);
3500 return TRUE;
3503 int os_signal_handle(const char *str, signal_seq_t *seq, ajla_error_t attr_unused *err)
3505 int sig;
3506 if (!strcmp(str, "SIGINT")) {
3507 sig = CTRL_C_EVENT;
3508 } else if (!strcmp(str, "SIGBREAK")) {
3509 sig = CTRL_BREAK_EVENT;
3510 } else {
3511 *seq = 0;
3512 return N_SIG;
3514 mutex_lock(&signal_mutex);
3515 *seq = signals[sig].seq;
3516 signals[sig].refcount++;
3517 mutex_unlock(&signal_mutex);
3518 return sig;
3521 void os_signal_unhandle(int sig)
3523 ajla_assert_lo(sig >= 0 && sig <= N_SIG, (file_line, "os_signal_unhandle: invalid signal %d", sig));
3524 if (sig == N_SIG)
3525 return;
3526 mutex_lock(&signal_mutex);
3527 ajla_assert_lo(signals[sig].refcount > 0, (file_line, "os_signal_unhandle: refcount underflow"));
3528 signals[sig].refcount--;
3529 mutex_unlock(&signal_mutex);
3532 signal_seq_t os_signal_seq(int sig)
3534 signal_seq_t seq;
3535 ajla_assert_lo(sig >= 0 && sig <= N_SIG, (file_line, "os_signal_seq: invalid signal %d", sig));
3536 if (sig == N_SIG)
3537 return 0;
3538 mutex_lock(&signal_mutex);
3539 seq = signals[sig].seq;
3540 mutex_unlock(&signal_mutex);
3541 return seq;
3544 bool os_signal_wait(int sig, signal_seq_t seq, mutex_t **mutex_to_lock, struct list *list_entry)
3546 ajla_assert_lo(sig >= 0 && sig <= N_SIG, (file_line, "os_signal_wait: invalid signal %d", sig));
3547 if (sig == N_SIG) {
3548 iomux_never(mutex_to_lock, list_entry);
3549 return true;
3551 mutex_lock(&signal_mutex);
3552 if (seq != signals[sig].seq) {
3553 mutex_unlock(&signal_mutex);
3554 return false;
3556 *mutex_to_lock = &signal_mutex;
3557 list_add(&signals[sig].wait_list, list_entry);
3558 mutex_unlock(&signal_mutex);
3559 return true;
3563 struct win32_notify_handle {
3564 HANDLE h;
3565 struct list wait_list;
3566 bool fired;
3569 static void iomux_directory_handle_wake_up(void *nh_)
3571 struct win32_notify_handle *nh = nh_;
3572 nh->fired = true;
3573 win32_close_change_notification_handle(nh->h);
3574 call(wake_up_wait_list)(&nh->wait_list, &monitor_mutex, true);
3577 bool iomux_directory_handle_alloc(dir_handle_t handle, notify_handle_t *h, uint64_t attr_unused *seq, ajla_error_t *err)
3579 struct win32_notify_handle *nh;
3580 DWORD gle;
3581 nh = mem_alloc_mayfail(struct win32_notify_handle *, sizeof(struct win32_notify_handle), err);
3582 if (unlikely(!nh))
3583 return false;
3584 list_init(&nh->wait_list);
3585 nh->fired = false;
3586 if (is_winnt()) {
3587 WCHAR *w = utf8_to_wchar(handle, err);
3588 if (unlikely(!w)) {
3589 mem_free(nh);
3590 return false;
3592 nh->h = FindFirstChangeNotificationW(w, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY);
3593 gle = GetLastError();
3594 mem_free(w);
3595 } else {
3596 nh->h = FindFirstChangeNotificationA(handle, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY);
3597 gle = GetLastError();
3599 if (unlikely(nh->h == INVALID_HANDLE_VALUE)) {
3600 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
3601 fatal_mayfail(e, err, "can't wait on change of '%s': %s", handle, error_decode(e));
3602 mem_free(nh);
3603 return false;
3605 if (unlikely(!monitor_handle(nh->h, iomux_directory_handle_wake_up, win32_close_change_notification_handle, nh, err))) {
3606 win32_close_change_notification_handle(nh->h);
3607 mem_free(nh);
3608 return false;
3610 *h = nh;
3611 return true;
3614 bool iomux_directory_handle_wait(notify_handle_t h, uint64_t attr_unused seq, mutex_t **mutex_to_lock, struct list *list_entry)
3616 struct win32_notify_handle *nh = h;
3617 monitor_lock();
3618 if (nh->fired) {
3619 monitor_unlock();
3620 return true;
3621 } else {
3622 *mutex_to_lock = &monitor_mutex;
3623 list_add(&nh->wait_list, list_entry);
3624 monitor_unlock();
3625 return false;
3629 void iomux_directory_handle_free(notify_handle_t h)
3631 struct win32_notify_handle *nh = h;
3632 ajla_assert_lo(list_is_empty(&nh->wait_list), (file_line, "iomux_directory_handle_free: freeing handle when there are processes waiting for it"));
3633 monitor_lock();
3634 if (!nh->fired)
3635 monitor_close(nh->h);
3636 monitor_unlock();
3637 mem_free(nh);
3641 static SOCKET win32_notify_socket[2];
3643 static bool win32_socketpair(SOCKET result[2])
3645 SOCKET lst;
3646 struct sockaddr_in sin;
3647 socklen_t len;
3648 int r;
3649 u_long one;
3651 lst = INVALID_SOCKET;
3652 result[0] = result[1] = INVALID_SOCKET;
3654 lst = socket(PF_INET, SOCK_STREAM, 0);
3655 if (unlikely(lst == INVALID_SOCKET))
3656 goto fail;
3658 memset(&sin, 0, sizeof sin);
3659 sin.sin_family = AF_INET;
3660 sin.sin_addr.s_addr = htonl(0x7f000001);
3661 r = bind(lst, (struct sockaddr *)&sin, sizeof sin);
3662 if (unlikely(r == SOCKET_ERROR))
3663 goto fail;
3665 len = sizeof sin;
3666 r = getsockname(lst, (struct sockaddr *)&sin, &len);
3667 if (unlikely(r == SOCKET_ERROR))
3668 goto fail;
3670 r = listen(lst, 1);
3671 if (unlikely(r == SOCKET_ERROR))
3672 goto fail;
3674 result[0] = socket(PF_INET, SOCK_STREAM, 0);
3675 if (unlikely(result[0] == INVALID_SOCKET))
3676 goto fail;
3678 r = connect(result[0], (struct sockaddr *)&sin, sizeof sin);
3679 if (unlikely(r == SOCKET_ERROR))
3680 goto fail;
3682 len = sizeof sin;
3683 result[1] = accept(lst, (struct sockaddr *)&sin, &len);
3684 if (unlikely(result[1] == INVALID_SOCKET))
3685 goto fail;
3687 one = 1;
3688 r = ioctlsocket(result[0], FIONBIO, &one);
3689 if (unlikely(r == SOCKET_ERROR))
3690 goto fail;
3691 r = ioctlsocket(result[1], FIONBIO, &one);
3692 if (unlikely(r == SOCKET_ERROR))
3693 goto fail;
3695 win32_close_socket(lst);
3697 return true;
3699 fail:
3700 if (lst != INVALID_SOCKET)
3701 win32_close_socket(lst);
3702 if (result[0] != INVALID_SOCKET)
3703 win32_close_socket(result[0]);
3704 if (result[1] != INVALID_SOCKET)
3705 win32_close_socket(result[1]);
3706 return false;
3709 static void win32_notify(void)
3711 int r;
3712 char c = 0;
3713 r = send(win32_notify_socket[1], &c, 1, 0);
3714 if (unlikely(r == SOCKET_ERROR)) {
3715 int er = WSAGetLastError();
3716 if (unlikely(er != WSAEWOULDBLOCK))
3717 fatal("error writing to the notify socket: %d", er);
3721 static void win32_shutdown_notify_pipe(void)
3723 int r;
3724 char c = 1;
3725 retry:
3726 r = send(win32_notify_socket[1], &c, 1, 0);
3727 if (unlikely(r == SOCKET_ERROR)) {
3728 int er = WSAGetLastError();
3729 if (er == WSAEWOULDBLOCK) {
3730 Sleep(1);
3731 goto retry;
3733 fatal("error writing to the notify socket: %d", er);
3737 static bool win32_drain_notify_pipe(void)
3739 char buffer[1024];
3740 int r;
3741 r = recv(win32_notify_socket[0], buffer, sizeof buffer, 0);
3742 if (unlikely(r == SOCKET_ERROR)) {
3743 int er = WSAGetLastError();
3744 if (likely(er == WSAEWOULDBLOCK))
3745 return false;
3746 fatal("error reading the notify socket: %d", er);
3748 return !!memchr(buffer, 1, r);
3752 handle_t os_socket(int domain, int type, int protocol, ajla_error_t *err)
3754 SOCKET sock;
3755 if (unlikely(!winsock_supported)) {
3756 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not configured");
3757 return NULL;
3759 domain = os_socket_pf(domain, err);
3760 if (unlikely(domain == -1))
3761 return NULL;
3762 type = os_socket_type(type, err);
3763 if (unlikely(type == -1))
3764 return NULL;
3765 sock = socket(domain, type, protocol);
3766 if (unlikely(sock == INVALID_SOCKET)) {
3767 fatal_mayfail(error_from_win32_socket(WSAGetLastError()), err, "socket failed");
3768 return NULL;
3770 return win32_socket_to_handle(sock, err);
3773 bool os_bind_connect(bool bnd, handle_t h, unsigned char *addr, size_t addr_len, ajla_error_t *err)
3775 int r;
3776 int er;
3777 struct sockaddr *sa;
3779 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3780 if (unlikely(!handle_is_socket(h))) {
3781 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3782 return false;
3784 sa = os_get_sock_addr(addr, &addr_len, err);
3785 if (unlikely(!sa))
3786 return false;
3787 r = (likely(!bnd) ? connect : bind)(h->s, sa, addr_len);
3788 er = WSAGetLastError();
3789 mem_free_aligned(sa);
3790 if (unlikely(r != SOCKET_ERROR))
3791 return true;
3792 if (likely(!bnd) && (likely(er == WSAEWOULDBLOCK) || likely(!er))) {
3793 /*debug("connect would block");
3794 while (1) {
3795 int t1 = iomux_test_handle(h, false);
3796 int t2 = iomux_test_handle(h, true);
3797 Sleep(1000);
3798 debug("test handle: %d", t1, t2);
3799 if (t2)
3800 break;
3802 int er;
3803 socklen_t er_l = sizeof(er);
3804 r = getsockopt(h->s, SOL_SOCKET, SO_ERROR, cast_ptr(char *, &er), &er_l);
3805 if (unlikely(r == SOCKET_ERROR)) {
3806 er = WSAGetLastError();
3807 fatal_mayfail(error_from_win32_socket(er), err, "getsockopt returned an error: %d", er);
3808 return false;
3810 debug("socket status: %d", er);
3813 h->connect_in_progress = true;
3814 return true;
3816 fatal_mayfail(error_from_win32_socket(er), err, "can't %s socket: %d", !bnd ? "connect" : "bind", er);
3817 return false;
3820 bool os_connect_completed(handle_t h, ajla_error_t *err)
3822 int e;
3824 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3825 if (unlikely(!handle_is_socket(h))) {
3826 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3827 return false;
3830 e = get_socket_error(h);
3831 if (unlikely(e)) {
3832 fatal_mayfail(error_from_win32_socket(e), err, "can't connect socket: %d", e);
3833 return false;
3835 h->connect_in_progress = false;
3836 return true;
3839 bool os_listen(handle_t h, ajla_error_t *err)
3841 int r;
3842 int er;
3844 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3845 if (unlikely(!handle_is_socket(h))) {
3846 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3847 return false;
3850 r = listen(h->s, signed_maximum(int));
3851 if (unlikely(r == SOCKET_ERROR)) {
3852 er = WSAGetLastError();
3853 fatal_mayfail(error_from_win32_socket(er), err, "listen returned an error: %d", er);
3854 return false;
3856 return true;
3859 int os_accept(handle_t h, handle_t *result, ajla_error_t *err)
3861 SOCKET sock;
3863 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3864 if (unlikely(!handle_is_socket(h))) {
3865 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3866 return OS_RW_ERROR;
3869 sock = accept(h->s, NULL, 0);
3870 if (unlikely(sock == INVALID_SOCKET)) {
3871 int er = WSAGetLastError();
3872 if (likely(er == WSAEWOULDBLOCK))
3873 return OS_RW_WOULDBLOCK;
3874 fatal_mayfail(error_from_win32_socket(er), err, "accept returned an error: %d", er);
3875 return OS_RW_ERROR;
3878 *result = win32_socket_to_handle(sock, err);
3880 return unlikely(*result == NULL) ? OS_RW_ERROR : 0;
3883 bool os_getsockpeername(bool peer, handle_t h, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
3885 int r;
3886 struct sockaddr *sa;
3887 socklen_t addrlen;
3889 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3890 if (unlikely(!handle_is_socket(h))) {
3891 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3892 return OS_RW_ERROR;
3895 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
3896 if (unlikely(!sa))
3897 return false;
3898 addrlen = SOCKADDR_MAX_LEN;
3899 r = (!peer ? getsockname : getpeername)(h->s, sa, &addrlen);
3900 if (r == SOCKET_ERROR) {
3901 int er = WSAGetLastError();
3902 fatal_mayfail(error_from_win32_socket(er), err, "%s returned an error: %d", !peer ? "getsockname" : "getpeername", er);
3903 goto free_ret_false;
3906 *addr = os_get_ajla_addr(sa, &addrlen, err);
3907 if (unlikely(!*addr))
3908 goto free_ret_false;
3910 *addr_len = addrlen;
3912 mem_free_aligned(sa);
3913 return true;
3915 free_ret_false:
3916 mem_free_aligned(sa);
3917 return false;
3920 ssize_t os_recvfrom(handle_t h, char *buffer, size_t len, int flags, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
3922 int r, f;
3923 struct sockaddr *sa;
3924 socklen_t addrlen;
3926 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3927 if (unlikely(!handle_is_socket(h))) {
3928 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3929 return OS_RW_ERROR;
3932 f = translate_flags(os_socket_msg, flags, err);
3933 if (unlikely(f < 0))
3934 return OS_RW_ERROR;
3936 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
3937 if (unlikely(!sa))
3938 return OS_RW_ERROR;
3939 addrlen = SOCKADDR_MAX_LEN;
3940 r = recvfrom(h->s, buffer, len, f, sa, &addrlen);
3941 if (unlikely(r == SOCKET_ERROR)) {
3942 int er = WSAGetLastError();
3943 if (likely(er == WSAEWOULDBLOCK))
3944 return OS_RW_WOULDBLOCK;
3945 fatal_mayfail(error_from_win32_socket(er), err, "recvfrom returned an error: %d", er);
3946 goto free_ret_error;
3948 if (unlikely(addrlen > SOCKADDR_MAX_LEN)) {
3949 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "the system returned too long address");
3950 goto free_ret_error;
3953 if (!addrlen) {
3954 if (unlikely(!array_init_mayfail(unsigned char, addr, addr_len, err))) {
3955 goto free_ret_error;
3957 } else {
3958 *addr = os_get_ajla_addr(sa, &addrlen, err);
3959 if (unlikely(!*addr))
3960 goto free_ret_error;
3961 *addr_len = addrlen;
3964 mem_free_aligned(sa);
3965 return r;
3967 free_ret_error:
3968 mem_free_aligned(sa);
3969 return OS_RW_ERROR;
3972 ssize_t os_sendto(handle_t h, const char *buffer, size_t len, int flags, unsigned char *addr, size_t addr_len, ajla_error_t *err)
3974 int r, f;
3975 struct sockaddr *sa;
3976 int er;
3978 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
3979 if (unlikely(!handle_is_socket(h))) {
3980 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
3981 return OS_RW_ERROR;
3984 f = translate_flags(os_socket_msg, flags, err);
3985 if (unlikely(f < 0))
3986 return OS_RW_ERROR;
3988 if (addr_len != 0) {
3989 size_t al = addr_len;
3990 sa = os_get_sock_addr(addr, &al, err);
3991 if (unlikely(!sa))
3992 return OS_RW_ERROR;
3993 r = sendto(h->s, buffer, len, f, sa, al);
3994 er = WSAGetLastError();
3995 mem_free_aligned(sa);
3996 } else {
3997 r = send(h->s, buffer, len, f);
3998 er = WSAGetLastError();
4001 if (unlikely(r == SOCKET_ERROR)) {
4002 if (likely(er == WSAEWOULDBLOCK))
4003 return OS_RW_WOULDBLOCK;
4004 fatal_mayfail(error_from_win32_socket(er), err, "send%s returned an error: %d", addr_len ? "to" : "", er);
4005 return OS_RW_ERROR;
4008 return r;
4011 bool os_getsockopt(handle_t h, int level, int option, char **buffer, size_t *buffer_len, ajla_error_t *err)
4013 int r;
4014 socklen_t opt_len;
4016 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
4017 if (unlikely(!handle_is_socket(h))) {
4018 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
4019 return false;
4022 level = os_socket_level(level, err);
4023 if (unlikely(level < 0))
4024 return false;
4026 option = os_socket_option(option, err);
4027 if (unlikely(level < 0))
4028 return false;
4030 opt_len = 4096;
4032 *buffer = mem_alloc_mayfail(char *, opt_len, err);
4033 if (unlikely(!*buffer))
4034 return false;
4036 r = getsockopt(h->s, level, option, *buffer, &opt_len);
4038 if (unlikely(r == SOCKET_ERROR)) {
4039 int er = WSAGetLastError();
4040 fatal_mayfail(error_from_win32_socket(er), err, "getsockopt returned an error: %d", er);
4041 mem_free(*buffer);
4042 return false;
4045 *buffer_len = opt_len;
4046 return true;
4049 bool os_setsockopt(handle_t h, int level, int option, const char *buffer, size_t buffer_len, ajla_error_t *err)
4051 int r;
4053 obj_registry_verify(OBJ_TYPE_HANDLE, ptr_to_num(h), file_line);
4054 if (unlikely(!handle_is_socket(h))) {
4055 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "socket operation on non-socket");
4056 return false;
4059 level = os_socket_level(level, err);
4060 if (unlikely(level < 0))
4061 return false;
4063 option = os_socket_option(option, err);
4064 if (unlikely(level < 0))
4065 return false;
4067 r = setsockopt(h->s, level, option, buffer, buffer_len);
4069 if (unlikely(r == SOCKET_ERROR)) {
4070 int er = WSAGetLastError();
4071 fatal_mayfail(error_from_win32_socket(er), err, "setsockopt returned an error: %d", er);
4072 return false;
4075 return true;
4078 static bool os_use_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
4080 char port_str[6];
4081 ssize_t r;
4082 size_t i;
4083 struct addrinfo *res = NULL, *rs;
4085 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
4086 return false;
4088 sprintf(port_str, "%d", port);
4089 r = fn_getaddrinfo(host, port_str, NULL, &res);
4090 if (unlikely(r)) {
4091 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
4092 goto fail;
4095 for (rs = res; rs; rs = rs->ai_next) {
4096 void *xresult;
4097 struct address addr;
4098 ajla_error_t e;
4099 socklen_t addrlen = rs->ai_addrlen;
4101 addr.address = os_get_ajla_addr(rs->ai_addr, &addrlen, &e);
4102 if (unlikely(!addr.address))
4103 continue;
4104 addr.address_length = addrlen;
4106 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
4107 *result = xresult;
4108 goto fail;
4112 if (unlikely(!*result_l)) {
4113 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs(EAI_NONAME)), err, "host not found");
4114 goto fail;
4117 fn_freeaddrinfo(res);
4118 return true;
4120 fail:
4121 if (res)
4122 fn_freeaddrinfo(res);
4123 for (i = 0; i < *result_l; i++)
4124 mem_free((*result)[i].address);
4125 mem_free(*result);
4126 return false;
4129 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
4131 struct hostent *he;
4132 size_t i;
4133 void *xresult;
4134 char *a;
4136 if (unlikely(!winsock_supported)) {
4137 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not configured");
4138 return false;
4141 if (likely(fn_getaddrinfo != NULL)) {
4142 return os_use_getaddrinfo(host, port, result, result_l, err);
4145 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
4146 return false;
4148 he = gethostbyname(host);
4150 if (unlikely(!he)) {
4151 int er = WSAGetLastError();
4152 fatal_mayfail(error_from_win32_socket(er), err, "host not found");
4153 goto fail;
4156 if (he->h_addrtype != AF_INET || he->h_length != 4 || !he->h_addr) {
4157 int er = WSANO_DATA;
4158 fatal_mayfail(error_from_win32_socket(er), err, "host not found");
4159 goto fail;
4162 for (i = 0; (a = he->h_addr_list[i]); i++) {
4163 struct sockaddr_in sin;
4164 struct sockaddr sa;
4165 struct address addr;
4166 ajla_error_t e;
4167 socklen_t addrlen = sizeof sin;
4169 sin.sin_family = AF_INET;
4170 sin.sin_port = (port << 8) | (port >> 8);
4171 memcpy(&sin.sin_addr, a, 4);
4173 memcpy(&sa, &sin, sizeof sin);
4175 addr.address = os_get_ajla_addr(&sa, &addrlen, &e);
4176 if (unlikely(!addr.address))
4177 continue;
4178 addr.address_length = addrlen;
4180 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
4181 *result = xresult;
4182 goto fail;
4186 if (unlikely(!*result_l)) {
4187 int er = WSANO_DATA;
4188 fatal_mayfail(error_from_win32_socket(er), err, "host not found");
4189 goto fail;
4192 return true;
4194 fail:
4195 for (i = 0; i < *result_l; i++)
4196 mem_free((*result)[i].address);
4197 mem_free(*result);
4198 return false;
4201 static char *os_use_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
4203 struct sockaddr *sa;
4204 int r;
4205 char *h;
4206 size_t h_len;
4207 sa = os_get_sock_addr(addr, &addr_len, err);
4208 if (unlikely(!sa))
4209 return NULL;
4210 h_len = 64;
4211 alloc_buffer_again:
4212 h = mem_alloc_mayfail(char *, h_len, err);
4213 if (unlikely(!h)) {
4214 mem_free_aligned(sa);
4215 return NULL;
4217 r = fn_getnameinfo(sa, addr_len, h, h_len, NULL, 0, 0);
4218 if (unlikely(r)) {
4219 if (unlikely(r == 122)) {
4220 mem_free(h);
4221 h_len *= 2;
4222 if (unlikely(!h_len)) {
4223 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "name buffer overflow");
4224 mem_free_aligned(sa);
4225 return NULL;
4227 goto alloc_buffer_again;
4229 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
4230 mem_free(h);
4231 mem_free_aligned(sa);
4232 return NULL;
4234 mem_free_aligned(sa);
4235 return h;
4238 char *os_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
4240 struct sockaddr *sa;
4241 struct hostent *he;
4242 char *name;
4243 size_t le;
4245 if (unlikely(!winsock_supported)) {
4246 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "TCP/IP is not configured");
4247 return false;
4250 if (likely(fn_getnameinfo != NULL)) {
4251 return os_use_getnameinfo(addr, addr_len, err);
4254 sa = os_get_sock_addr(addr, &addr_len, err);
4255 if (unlikely(!sa))
4256 return NULL;
4257 switch (sa->sa_family) {
4258 case AF_INET: {
4259 struct sockaddr_in *sin;
4260 if (unlikely(addr_len < offsetof(struct sockaddr_in, sin_addr) + 4)) {
4261 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
4262 mem_free_aligned(sa);
4263 return NULL;
4265 sin = cast_ptr(struct sockaddr_in *, sa);
4266 he = gethostbyaddr(cast_ptr(char *, &sin->sin_addr.s_addr), 4, sa->sa_family);
4267 break;
4269 #ifdef AF_INET6
4270 case AF_INET6: {
4271 struct sockaddr_in6 *sin6;
4272 if (unlikely(addr_len < offsetof(struct sockaddr_in6, sin6_addr) + 16)) {
4273 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
4274 mem_free_aligned(sa);
4275 return NULL;
4277 sin6 = cast_ptr(struct sockaddr_in6 *, sa);
4278 he = gethostbyaddr(&sin6->sin6_addr.s6_addr, 16, sa->sa_family);
4279 break;
4281 #endif
4282 default: {
4283 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "address family %d not supported", sa->sa_family);
4284 mem_free_aligned(sa);
4285 return NULL;
4288 mem_free_aligned(sa);
4289 if (unlikely(!he) || !he->h_name) {
4290 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, h_errno), err, "host not found");
4291 return NULL;
4293 le = strlen(he->h_name);
4294 name = mem_alloc_mayfail(char *, le + 1, err);
4295 if (unlikely(!name))
4296 return NULL;
4297 return memcpy(name, he->h_name, le + 1);
4300 bool os_getaddrinfo_is_thread_safe(void)
4302 return winsock_supported && fn_getaddrinfo != NULL && fn_getnameinfo != NULL;
4305 const char *os_decode_error(ajla_error_t attr_unused error, char attr_unused *(*tls_buffer)(void))
4307 return NULL;
4311 struct dl_handle_t *os_dlopen(const char *filename, ajla_error_t *err, char **err_msg)
4313 size_t i;
4314 DWORD gle;
4315 HMODULE h;
4316 *err_msg = NULL;
4317 if (is_winnt()) {
4318 WCHAR *w = utf8_to_wchar(filename, err);
4319 if (unlikely(!w))
4320 return NULL;
4321 for (i = 0; w[i]; i++)
4322 if (w[i] == '/')
4323 w[i] = '\\';
4324 h = LoadLibraryW(w);
4325 gle = GetLastError();
4326 mem_free(w);
4327 } else {
4328 char *a = str_dup(filename, -1, err);
4329 if (unlikely(!a))
4330 return NULL;
4331 for (i = 0; a[i]; i++)
4332 if (a[i] == '/')
4333 a[i] = '\\';
4334 h = LoadLibraryA(a);
4335 gle = GetLastError();
4336 mem_free(a);
4338 if (unlikely(!h)) {
4339 ajla_error_t e = error_from_win32(EC_SYSCALL, gle);
4340 fatal_mayfail(e, err, "can't load library %s: %s", filename, error_decode(e));
4341 return NULL;
4343 return cast_ptr(struct dl_handle_t *, h);
4346 void os_dlclose(struct dl_handle_t *dlh)
4348 if (unlikely(!FreeLibrary(cast_ptr(HMODULE, dlh))))
4349 internal(file_line, "FreeLibrary failed: %u", GetLastError());
4352 bool os_dlsym(struct dl_handle_t *dlh, const char *symbol, void **result)
4354 void *r = GetProcAddress(cast_ptr(HMODULE, dlh), symbol);
4355 if (unlikely(!r))
4356 return false;
4357 *result = r;
4358 return true;
4362 static thread_t iomux_thread;
4364 static int compare_socket(const void *x1, const void *x2)
4366 SOCKET s1 = *cast_ptr(const SOCKET *, x1);
4367 SOCKET s2 = *cast_ptr(const SOCKET *, x2);
4368 if (s1 < s2)
4369 return -1;
4370 if (s1 > s2)
4371 return 1;
4372 return 0;
4375 thread_function_decl(iomux_poll_thread,
4376 u_int fdx_size;
4377 struct fdx_set *fdx[2];
4379 thread_set_id(-1);
4381 fdx_size = 1;
4382 fdx[0] = mem_alloc(struct fdx_set *, sizeof(struct fdx_set));
4383 fdx[1] = mem_alloc(struct fdx_set *, sizeof(struct fdx_set));
4385 while (likely(!win32_drain_notify_pipe())) {
4386 int wr;
4387 int r;
4388 bool need_timeout = false;
4389 struct timeval timeout;
4390 timeout.tv_sec = CONNECT_TIMEOUT / 1000000;
4391 timeout.tv_usec = CONNECT_TIMEOUT % 1000000;
4393 fdx[0]->fd_count = 1;
4394 fdx[0]->fd_array[0] = win32_notify_socket[0];
4395 fdx[1]->fd_count = 0;
4397 mutex_lock(&socket_list_mutex);
4398 for (wr = 0; wr < 2; wr++) {
4399 struct list *l;
4400 list_for_each(l, &socket_list[wr]) {
4401 handle_t h = get_struct(l, struct win32_handle, socket_entry[wr]);
4402 address_lock(h, DEPTH_THUNK);
4403 if (!list_is_empty(&h->wait_list[wr])) {
4404 if (unlikely(fdx[wr]->fd_count == fdx_size)) {
4405 fdx_size *= 2;
4406 if (unlikely(!fdx_size))
4407 fatal("too many sockets");
4408 mem_check_overflow(offsetof(struct fdx_set, fd_array), fdx_size, sizeof(SOCKET), NULL);
4409 fdx[0] = mem_realloc(struct fdx_set *, fdx[0], offsetof(struct fdx_set, fd_array[fdx_size]));
4410 fdx[1] = mem_realloc(struct fdx_set *, fdx[1], offsetof(struct fdx_set, fd_array[fdx_size]));
4412 fdx[wr]->fd_array[fdx[wr]->fd_count++] = h->s;
4413 if (unlikely(h->connect_in_progress))
4414 need_timeout = true;
4415 } else {
4416 l = l->prev;
4417 list_del(&h->socket_entry[wr]);
4418 h->socket_entry[wr].next = NULL;
4420 address_unlock(h, DEPTH_THUNK);
4423 mutex_unlock(&socket_list_mutex);
4425 r = select(FD_SETSIZE, cast_ptr(fd_set *, fdx[0]), cast_ptr(fd_set *, fdx[1]), NULL, need_timeout ? &timeout : NULL);
4426 if (unlikely(r == SOCKET_ERROR)) {
4427 int er = WSAGetLastError();
4428 internal(file_line, "select returned an error: %d", er);
4431 qsort(fdx[0]->fd_array, fdx[0]->fd_count, sizeof(SOCKET), compare_socket);
4432 qsort(fdx[1]->fd_array, fdx[1]->fd_count, sizeof(SOCKET), compare_socket);
4434 mutex_lock(&socket_list_mutex);
4435 for (wr = 0; wr < 2; wr++) {
4436 struct list *l;
4437 list_for_each(l, &socket_list[wr]) {
4438 handle_t h = get_struct(l, struct win32_handle, socket_entry[wr]);
4439 SOCKET hndl = h->s;
4440 void *p;
4441 p = bsearch(&hndl, fdx[wr]->fd_array, fdx[wr]->fd_count, sizeof(SOCKET), compare_socket);
4442 if (p) {
4443 wake_up:
4444 address_lock(h, DEPTH_THUNK);
4445 call(wake_up_wait_list)(&h->wait_list[wr], address_get_mutex(h, DEPTH_THUNK), true);
4446 } else {
4447 if (unlikely(h->connect_in_progress)) {
4448 int e = get_socket_error(h);
4449 if (e)
4450 goto wake_up;
4455 mutex_unlock(&socket_list_mutex);
4458 mem_free(fdx[0]);
4459 mem_free(fdx[1]);
4463 int os_getpagesize(void)
4465 SYSTEM_INFO si;
4466 static int ps = 0;
4467 if (likely(ps))
4468 return ps;
4469 GetSystemInfo(&si);
4470 ps = si.dwAllocationGranularity;
4471 return ps;
4474 static DWORD prot_to_protect(int prot)
4476 if (prot == PROT_NONE)
4477 return PAGE_NOACCESS;
4478 if (prot == PROT_READ)
4479 return PAGE_READONLY;
4480 if (prot == (PROT_READ | PROT_WRITE))
4481 return PAGE_READWRITE;
4482 return PAGE_EXECUTE_READWRITE;
4485 void *os_mmap(void *ptr, size_t size, int prot, int flags, handle_t h, os_off_t off, ajla_error_t *err)
4487 size_t align;
4488 DWORD allocation_type, protect;
4489 void *res;
4490 ajla_error_t e;
4492 align = (size_t)1 << MAP_ALIGNED_BITS(flags);
4493 allocation_type = MEM_RESERVE | (prot == PROT_NONE ? 0 : MEM_COMMIT);
4494 protect = prot_to_protect(prot);
4496 if (unlikely((flags & (MAP_EXCL | MAP_FIXED)) == MAP_FIXED)) {
4497 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "fixed mappings not supported");
4498 return MAP_FAILED;
4500 if (unlikely(handle_is_valid(h))) {
4501 HANDLE hmap;
4502 DWORD map_access = !prot || prot == PROT_READ ? FILE_MAP_READ : FILE_MAP_WRITE;
4503 if (unlikely(handle_is_socket(h))) {
4504 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mmap of socket not supported");
4505 return MAP_FAILED;
4507 hmap = CreateFileMappingA(h->h, NULL, protect, 0, 0, NULL);
4508 if (unlikely(!hmap)) {
4509 e = error_from_win32(EC_SYSCALL, GetLastError());
4510 fatal_mayfail(e, err, "can't create mapping object: %s", error_decode(e));
4511 return MAP_FAILED;
4513 repeat_filemap:
4514 res = MapViewOfFileEx(hmap, map_access, off >> 31 >> 1, off, size, ptr);
4515 if (unlikely(!res)) {
4516 if (ptr && !(flags & MAP_FIXED)) {
4517 ptr = NULL;
4518 goto repeat_filemap;
4520 e = error_from_win32(EC_SYSCALL, GetLastError());
4521 win32_close_handle(hmap);
4522 fatal_mayfail(e, err, "can't map file: %s", error_decode(e));
4523 return MAP_FAILED;
4525 if (ptr && unlikely(res != ptr))
4526 internal(file_line, "MapViewOfFileEx allocated different memory: %p != %p", res, ptr);
4527 win32_close_handle(hmap);
4528 return res;
4530 repeat:
4531 res = VirtualAlloc(ptr, size + align - 1, allocation_type, protect);
4532 if (unlikely(!res)) {
4533 if (ptr && !(flags & MAP_FIXED)) {
4534 ptr = NULL;
4535 goto repeat;
4537 e = error_from_win32(EC_SYSCALL, GetLastError());
4538 fatal_mayfail(e, err, "can't allocate memory: %s", error_decode(e));
4539 return MAP_FAILED;
4541 if (ptr && unlikely(res != ptr))
4542 internal(file_line, "VirtualAlloc allocated different memory: %p != %p", res, ptr);
4543 if (align != 1) {
4544 void *aptr = num_to_ptr(round_up(ptr_to_num(res), align));
4545 if (unlikely(!VirtualFree(res, 0, MEM_RELEASE)))
4546 internal(file_line, "VirtualFree failed: %u", GetLastError());
4547 res = VirtualAlloc(aptr, size, allocation_type, protect);
4548 if (unlikely(!res))
4549 goto repeat;
4550 if (unlikely(res != aptr)) {
4551 internal(file_line, "VirtualAlloc allocated different memory: %p != %p", res, aptr);
4553 return aptr;
4555 return res;
4558 void os_munmap(void *ptr, size_t attr_unused size, bool file)
4560 if (!file) {
4561 if (unlikely(!VirtualFree(ptr, 0, MEM_RELEASE)))
4562 internal(file_line, "VirtualFree failed: %u", GetLastError());
4563 } else {
4564 if (unlikely(!UnmapViewOfFile(ptr)))
4565 internal(file_line, "UnmapViewOfFile failed: %u", GetLastError());
4569 bool os_mprotect(void *ptr, size_t size, int prot, ajla_error_t *err)
4571 if (prot == PROT_NONE) {
4572 if (unlikely(!VirtualFree(ptr, size, MEM_DECOMMIT)))
4573 internal(file_line, "VirtualFree failed: %u", GetLastError());
4574 } else {
4575 DWORD allocation_type, protect;
4576 allocation_type = MEM_COMMIT;
4577 protect = prot_to_protect(prot);
4578 if (unlikely(!VirtualAlloc(ptr, size, allocation_type, protect))) {
4579 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4580 fatal_mayfail(e, err, "can't commit memory: %s", error_decode(e));
4581 return false;
4584 return true;
4587 void os_code_invalidate_cache(uint8_t *code, size_t code_size, bool set_exec)
4589 DWORD old;
4590 if (fn_FlushInstructionCache) {
4591 if (unlikely(!fn_FlushInstructionCache(GetCurrentProcess(), cast_ptr(void *, code), code_size))) {
4592 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4593 fatal("failed to flush instruction cache: FlushInstructionCache(%p, %"PRIxMAX") returned error: %s", code, (uintmax_t)code_size, error_decode(e));
4596 if (set_exec) {
4597 if (unlikely(!VirtualProtect(code, code_size, PAGE_EXECUTE_READWRITE, &old))) {
4598 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4599 fatal("failed to set memory range read+write+exec: VirtualProtect(%p, %"PRIxMAX") returned error: %s", code, (uintmax_t)code_size, error_decode(e));
4604 void *os_code_map(uint8_t *code, size_t attr_unused code_size, ajla_error_t attr_unused *err)
4606 /*debug("making executable: %p, %lx", code, code_size);*/
4607 os_code_invalidate_cache(code, code_size, true);
4608 return code;
4611 void os_code_unmap(void *mapped_code, size_t attr_unused code_size)
4613 mem_free(mapped_code);
4617 void os_get_environment(char **str, size_t *l)
4619 array_init(char, str, l);
4620 if (!is_winnt() || !fn_GetEnvironmentStringsW || !fn_FreeEnvironmentStringsW) {
4621 const char *e, *a;
4622 if (fn_GetEnvironmentStringsA)
4623 e = fn_GetEnvironmentStringsA();
4624 else
4625 e = fn_GetEnvironmentStrings();
4626 a = e;
4627 if (unlikely(!e)) {
4628 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4629 fatal("GetEnvironmentStringsA failed: %s", error_decode(e));
4631 while (*a) {
4632 bool upcase = true;
4633 if (*a == '=') {
4634 a += strlen(a) + 1;
4635 continue;
4637 while (*a) {
4638 char c = *a++;
4639 if (upcase) {
4640 if (c >= 'a' && c <= 'z')
4641 c -= 0x20;
4642 if (c == '=')
4643 upcase = false;
4645 array_add(char, str, l, c);
4647 array_add(char, str, l, 0);
4648 a++;
4650 if (fn_GetEnvironmentStringsA && fn_FreeEnvironmentStringsA) {
4651 if (unlikely(!fn_FreeEnvironmentStringsA(e))) {
4652 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4653 fatal("FreeEnvironmentStringsA failed: %s", error_decode(e));
4656 } else {
4657 const WCHAR *e, *a;
4658 e = fn_GetEnvironmentStringsW();
4659 a = e;
4660 if (unlikely(!e)) {
4661 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4662 fatal("GetEnvironmentStringsW failed: %s", error_decode(e));
4664 while (*a) {
4665 ajla_error_t err;
4666 size_t utf8_len, i;
4667 char *utf8_str;
4668 if (*a == '=')
4669 goto skip;
4670 utf8_str = wchar_to_utf8(NULL, a, &err);
4671 if (unlikely(!utf8_str)) {
4672 if (err.error_class == EC_ASYNC) {
4673 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4674 fatal("can't allocate environment memory: %s", error_decode(e));
4676 goto skip;
4678 utf8_len = strlen(utf8_str);
4679 for (i = 0; i < utf8_len; i++) {
4680 if (utf8_str[i] == '=')
4681 break;
4682 if (utf8_str[i] >= 'a' && utf8_str[i] <= 'z')
4683 utf8_str[i] -= 0x20;
4685 array_add_multiple(char, str, l, utf8_str, utf8_len + 1);
4686 mem_free(utf8_str);
4687 skip:
4688 while (*a)
4689 a++;
4690 a++;
4692 if (unlikely(!fn_FreeEnvironmentStringsW(e))) {
4693 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4694 fatal("FreeEnvironmentStringsW failed: %s", error_decode(e));
4700 void iomux_init(void)
4704 void iomux_done(void)
4708 void os_init(void)
4710 #ifdef DEBUG
4711 unsigned i;
4712 for (i = 0; i < n_array_elements(win32_error_to_system_error) - 1; i++)
4713 if (unlikely(win32_error_to_system_error[i].errn >= win32_error_to_system_error[i + 1].errn))
4714 internal(file_line, "os_init: win32_error_to_system_error is not monotonic at %u", i);
4715 #endif
4716 os_threads_initialized = false;
4718 tick_high = 0;
4719 tick_last = 0;
4720 store_relaxed(&window_offset_x, 0);
4721 store_relaxed(&window_offset_y, 0);
4723 os_cwd = os_dir_cwd(NULL);
4724 os_init_path_to_exe();
4726 fn_GetEnvironmentStrings = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStrings");
4727 fn_GetEnvironmentStringsA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStringsA");
4728 fn_GetEnvironmentStringsW = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetEnvironmentStringsW");
4729 fn_FreeEnvironmentStringsA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeEnvironmentStringsA");
4730 fn_FreeEnvironmentStringsW = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeEnvironmentStringsW");
4731 if (!fn_GetEnvironmentStrings)
4732 fatal("Could not get GetEnvironmentStrings");
4733 fn_RtlGetVersion = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
4734 fn_RtlFreeUserThreadStack = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlFreeUserThreadStack");
4735 fn_GetDiskFreeSpaceExA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
4736 fn_GetDiskFreeSpaceExW = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExW");
4737 fn_CancelIo = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIo");
4738 fn_CancelIoEx = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIoEx");
4739 fn_MoveFileExA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "MoveFileExA");
4740 fn_MoveFileExW = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "MoveFileExW");
4741 fn_FlushInstructionCache = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FlushInstructionCache");
4742 handle_iphlpa = LoadLibraryA("iphlpapi.dll");
4743 if (handle_iphlpa)
4744 fn_GetNetworkParams = (void *)GetProcAddress(handle_iphlpa, "GetNetworkParams");
4746 fn_GetTickCount64 = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetTickCount64");
4748 #ifdef USER_QPC
4749 fn_QueryPerformanceFrequency = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "QueryPerformanceFrequency");
4750 if (likely(fn_QueryPerformanceFrequency != NULL)) {
4751 LARGE_INTEGER li;
4752 int64_t perf_frequency;
4753 if (likely(fn_QueryPerformanceFrequency(&li))) {
4754 perf_frequency = li.LowPart + ((uint64_t)li.HighPart << 32);
4755 perf_multiplier = (long double)1000000 / (long double)perf_frequency;
4756 fn_QueryPerformanceCounter = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "QueryPerformanceCounter");
4759 #endif
4761 fn_getaddrinfo = (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "getaddrinfo");
4762 fn_freeaddrinfo = (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "freeaddrinfo");
4763 if (unlikely(!fn_getaddrinfo) || unlikely(!fn_freeaddrinfo)) {
4764 fn_getaddrinfo = NULL;
4765 fn_freeaddrinfo = NULL;
4767 fn_getnameinfo = (void *)GetProcAddress(GetModuleHandleA("ws2_32.dll"), "getnameinfo");
4770 void os_init_multithreaded(void)
4772 unsigned u;
4773 WSADATA wsadata;
4774 int wsaret;
4776 os_init_calendar_lock();
4778 monitor_init();
4780 if (!fn_CancelIoEx) {
4781 pipe_count = 0;
4782 mutex_init(&pipe_count_mutex);
4784 mutex_init(&tick_mutex);
4785 list_init(&deferred_write_list);
4786 list_init(&deferred_closed_list);
4787 mutex_init(&deferred_mutex);
4788 os_threads_initialized = true;
4789 for (u = 0; u < 3; u++)
4790 win32_std_handles[u] = win32_hfile_to_handle(get_std_handle(u), (!u ? O_RDONLY : O_WRONLY) | O_NONBLOCK, false, "", NULL);
4792 #if 0
4794 int i = 0;
4795 while (1) {
4796 debug("X1: %d", i++);
4797 win32_create_read_thread(win32_std_handles[0], NULL);
4798 /*win32_terminate_io_thread(&win32_std_handles[0]->rd);*/
4799 os_free_handle(win32_std_handles[0], false);
4800 win32_std_handles[0] = win32_hfile_to_handle(get_std_handle(0), O_RDONLY | O_NONBLOCK, false, "", NULL);
4803 #endif
4804 #if 0
4806 int i = 0;
4807 while (1) {
4808 ssize_t r;
4809 char c;
4810 handle_t p[2];
4811 debug("X2: %d", i++);
4812 os_pipe(p, 3, NULL);
4813 r = os_read(p[0], &c, 1, NULL);
4814 r = r;
4815 os_close(p[0]);
4816 os_close(p[1]);
4819 #endif
4821 wsaret = WSAStartup(MAKEWORD(1, 1), &wsadata);
4822 winsock_supported = !wsaret;
4823 if (winsock_supported) {
4824 if (unlikely(!win32_socketpair(win32_notify_socket))) {
4825 if (WSACleanup())
4826 warning("WSACleanup returned an error: %u", WSAGetLastError());
4827 winsock_supported = false;
4830 if (winsock_supported) {
4831 mutex_init(&socket_list_mutex);
4832 list_init(&socket_list[0]);
4833 list_init(&socket_list[1]);
4834 thread_spawn(&iomux_thread, iomux_poll_thread, NULL, PRIORITY_IO, NULL);
4837 mutex_init(&signal_mutex);
4838 memset(signals, 0, sizeof signals);
4839 for (u = 0; u < N_SIG; u++)
4840 list_init(&signals[u].wait_list);
4842 if (unlikely(!SetConsoleCtrlHandler(signal_handler, TRUE))) {
4843 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4844 fatal("SetConsoleCtrlHandler set failed: %s", error_decode(e));
4848 void os_done_multithreaded(void)
4850 unsigned u;
4852 if (unlikely(!SetConsoleCtrlHandler(signal_handler, FALSE))) {
4853 ajla_error_t e = error_from_win32(EC_SYSCALL, GetLastError());
4854 fatal("SetConsoleCtrlHandler unset failed: %s", error_decode(e));
4857 mutex_done(&signal_mutex);
4859 if (winsock_supported) {
4860 win32_shutdown_notify_pipe();
4861 thread_join(&iomux_thread);
4862 win32_close_socket(win32_notify_socket[0]);
4863 win32_close_socket(win32_notify_socket[1]);
4864 ajla_assert_lo(list_is_empty(&socket_list[0]), (file_line, "os_done_multithreaded: read socket list is not empty"));
4865 ajla_assert_lo(list_is_empty(&socket_list[1]), (file_line, "os_done_multithreaded: write socket list is not empty"));
4866 mutex_done(&socket_list_mutex);
4867 if (WSACleanup())
4868 warning("WSACleanup returned an error: %u", WSAGetLastError());
4869 winsock_supported = false;
4872 for (u = 0; u < 3; u++)
4873 os_free_handle(win32_std_handles[u], false);
4875 mutex_lock(&deferred_mutex);
4876 while (!list_is_empty(&deferred_write_list)) {
4877 mutex_unlock(&deferred_mutex);
4878 Sleep(1);
4879 mutex_lock(&deferred_mutex);
4881 mutex_unlock(&deferred_mutex);
4882 win32_clean_up_handles();
4884 mutex_done(&tick_mutex);
4885 if (!fn_CancelIoEx)
4886 mutex_done(&pipe_count_mutex);
4888 monitor_done();
4890 os_done_calendar_lock();
4892 os_threads_initialized = false;
4895 void os_done(void)
4897 mem_free(os_path_to_exe);
4898 os_dir_close(os_cwd);
4901 #endif