summaryrefslogtreecommitdiffstats
path: root/src/lwt/lwt_unix_stubs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lwt/lwt_unix_stubs.c')
-rw-r--r--src/lwt/lwt_unix_stubs.c1374
1 files changed, 687 insertions, 687 deletions
diff --git a/src/lwt/lwt_unix_stubs.c b/src/lwt/lwt_unix_stubs.c
index aa85e5b..a32d5f2 100644
--- a/src/lwt/lwt_unix_stubs.c
+++ b/src/lwt/lwt_unix_stubs.c
@@ -1,687 +1,687 @@
-#include <winsock2.h>
-#include <windows.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <caml/mlvalues.h>
-#include <caml/alloc.h>
-#include <caml/memory.h>
-#include <caml/fail.h>
-#include <caml/bigarray.h>
-#include <caml/callback.h>
-
-//#define D(x) x
-#define D(x) while(0){}
-
-#define UNIX_BUFFER_SIZE 16384
-#define Nothing ((value) 0)
-
-typedef struct
-{
- OVERLAPPED overlapped;
- long id;
- long action;
-} completionData;
-
-struct filedescr {
- union {
- HANDLE handle;
- SOCKET socket;
- } fd;
- enum { KIND_HANDLE, KIND_SOCKET } kind;
- int crt_fd;
-};
-#define Handle_val(v) (((struct filedescr *) Data_custom_val(v))->fd.handle)
-#define Socket_val(v) (((struct filedescr *) Data_custom_val(v))->fd.socket)
-
-extern void win32_maperr (DWORD errcode);
-extern void uerror (char * cmdname, value arg);
-extern value unix_error_of_code (int errcode);
-extern value win_alloc_handle (HANDLE h);
-extern value win_alloc_socket(SOCKET);
-extern void get_sockaddr (value mladdr,
- struct sockaddr * addr /*out*/,
- int * addr_len /*out*/);
-
-#define Array_data(a, i) (((char *) a->data) + Long_val(i))
-
-CAMLprim value ml_blit_string_to_buffer
-(value s, value i, value a, value j, value l)
-{
- char *src = String_val(s) + Int_val(i);
- char *dest = Array_data(Bigarray_val(a), j);
- memcpy(dest, src, Long_val(l));
- return Val_unit;
-}
-
-CAMLprim value ml_blit_buffer_to_string
-(value a, value i, value s, value j, value l)
-{
- char *src = Array_data(Bigarray_val(a), i);
- char *dest = String_val(s) + Long_val(j);
- memcpy(dest, src, Long_val(l));
- return Val_unit;
-}
-
-/****/
-
-#define READ 0
-#define WRITE 1
-#define READ_OVERLAPPED 2
-#define WRITE_OVERLAPPED 3
-#define READDIRECTORYCHANGES 4
-static char * action_name[5] = {
- "read", "write", "read(overlapped)", "write(overlapped)",
- "ReadDirectoryChangesW"
-};
-
-static value completionCallback;
-
-static void invoke_completion_callback
-(long id, long len, long errCode, long action) {
- CAMLlocal2 (err, name);
- value args[4];
- err = Val_long(0);
- if (errCode != NO_ERROR) {
- len = -1;
- win32_maperr (errCode);
- err = unix_error_of_code(errno);
- }
- name = copy_string (action_name[action]);
- D(printf("Action %s completed: id %ld -> len %ld / err %d (errCode %ld)\n",
- action_name[action], id, len, errno, errCode));
- args[0] = Val_long(id);
- args[1] = Val_long(len);
- args[2] = err;
- args[3] = name;
- caml_callbackN(completionCallback, 4, args);
- D(printf("Callback performed\n"));
-}
-
-typedef struct {
- long id;
- long len;
- long errCode;
- long action; } completionInfo;
-
-int compN = 0;
-int complQueueSize = 0;
-completionInfo * complQueue = NULL;
-
-static void completion (long id, long len, long errCode, long action) {
- D(printf("Queueing action %s: id %ld -> len %ld / err %d (errCode %ld)\n",
- action_name[action], id, len, errno, errCode));
- if (compN + 1 > complQueueSize) {
- completionInfo * queue;
- int n = complQueueSize * 2 + 1;
- D(printf("Resizing queue to %d\n", n));
- queue = (completionInfo *) GlobalAlloc(GPTR, n * sizeof(completionInfo));
- if (complQueue != NULL)
- CopyMemory (queue, complQueue, complQueueSize * sizeof(completionInfo));
- complQueue = queue;
- complQueueSize = n;
- }
- complQueue[compN].id = id;
- complQueue[compN].len = len;
- complQueue[compN].errCode = errCode;
- complQueue[compN].action = action;
- compN++;
-}
-
-CAMLprim value get_queue (value unit) {
- CAMLparam1 (unit);
- int i;
- for (i = 0; i < compN; i++)
- invoke_completion_callback
- (complQueue[i].id, complQueue[i].len,
- complQueue[i].errCode, complQueue[i].action);
- compN = 0;
- CAMLreturn (Val_unit);
-}
-
-/****/
-
-static HANDLE main_thread;
-
-static DWORD CALLBACK helper_thread (void * param) {
- D(printf("Helper thread created\n"));
- while (1) SleepEx(INFINITE, TRUE);
- return 0;
-}
-
-static VOID CALLBACK exit_thread(ULONG_PTR param) {
- D(printf("Helper thread exiting\n"));
- ExitThread(0);
-}
-
-static HANDLE get_helper_thread (value threads, int kind) {
- HANDLE h = (HANDLE) Field(threads, kind);
-
- if (h != INVALID_HANDLE_VALUE) return h;
-
- h = CreateThread (NULL, 0, helper_thread, NULL, 0, NULL);
- if (h == NULL) {
- win32_maperr (GetLastError ());
- uerror("createHelperThread", Nothing);
- }
- Field(threads, kind) = (value) h;
- return h;
-}
-
-static void kill_thread (HANDLE *h) {
- D(printf("Killing thread\n"));
- QueueUserAPC(exit_thread, *h, 0);
- CloseHandle(*h);
- *h = INVALID_HANDLE_VALUE;
-}
-
-CAMLprim value win_kill_threads (value fd) {
- CAMLparam1(fd);
- if (Field(fd, 1) != Val_long(0)) {
- kill_thread((HANDLE *) &Field(Field(fd, 1), READ));
- kill_thread((HANDLE *) &Field(Field(fd, 1), WRITE));
- }
- CAMLreturn(Val_unit);
-}
-
-CAMLprim value win_wrap_fd (value fd) {
- CAMLparam1(fd);
- CAMLlocal2(th, res);
- D(printf("Wrapping file descriptor (sync)\n"));
- res = caml_alloc_tuple(2);
- Store_field(res, 0, fd);
- th = caml_alloc(2, Abstract_tag);
- Field(th, READ) = (value) INVALID_HANDLE_VALUE;
- Field(th, WRITE) = (value) INVALID_HANDLE_VALUE;
- Store_field(res, 1, th);
- CAMLreturn(res);
-}
-
-/****/
-
-typedef struct {
- long action;
- long id;
- HANDLE fd;
- char * buffer;
- long len;
- long error;
-} ioInfo;
-
-
-static VOID CALLBACK thread_completion(ULONG_PTR param) {
- ioInfo * info = (ioInfo *) param;
- completion (info->id, info->len, info->error, info->action);
- GlobalFree (info);
-}
-
-static VOID CALLBACK perform_io_on_thread(ULONG_PTR param) {
- ioInfo * info = (ioInfo *) param;
- DWORD l;
- BOOL res;
-
- D(printf("Starting %s: id %ld, len %ld\n",
- action_name[info->action], info->id, info->len));
-
- res =
- (info->action == READ)?
- ReadFile(info->fd, info->buffer,info->len, &l, NULL):
- WriteFile(info->fd, info->buffer,info->len, &l, NULL);
- if (!res) {
- info->len = -1;
- info->error = GetLastError ();
- } else {
- info->len = l;
- info->error = NO_ERROR;
- }
- D(printf("Action %s done: id %ld -> len %ld / err %d (errCode %ld)\n",
- action_name[info->action],
- info->id, info->len, errno, info->error));
- QueueUserAPC(thread_completion, main_thread, param);
-}
-
-static void thread_io
-(long action, long id, value threads, HANDLE h, char * buf, long len) {
- struct caml_bigarray *buf_arr = Bigarray_val(buf);
- ioInfo * info = GlobalAlloc(GPTR, sizeof(ioInfo));
- if (info == NULL) {
- errno = ENOMEM;
- uerror(action_name[action], Nothing);
- }
-
- info->action = action;
- info->id = id;
- info->fd = h;
- info->buffer = buf;
- info->len = len;
-
- h = get_helper_thread(threads, action);
- QueueUserAPC(perform_io_on_thread, h, (ULONG_PTR) info);
-}
-
-/****/
-
-static void CALLBACK overlapped_completion
-(DWORD errCode, DWORD len, LPOVERLAPPED overlapped) {
- completionData * d = (completionData * )overlapped;
- completion (d->id, len, errCode, d->action);
- GlobalFree (d);
-}
-
-static void overlapped_action(long action, long id,
- HANDLE fd, char *buf, long len) {
- BOOL res;
- long err;
- completionData * d = GlobalAlloc(GPTR, sizeof(completionData));
- if (d == NULL) {
- errno = ENOMEM;
- uerror(action_name[action], Nothing);
- }
- d->id = id;
- d->action = action;
-
- D(printf("Starting %s: id %ld, len %ld\n", action_name[action], id, len));
- res =
- (action == READ_OVERLAPPED)?
- ReadFileEx(fd, buf, len, &(d->overlapped), overlapped_completion):
- WriteFileEx(fd, buf, len, &(d->overlapped), overlapped_completion);
-
- if (!res) {
- err = GetLastError ();
- if (err != ERROR_IO_PENDING) {
- win32_maperr (err);
- D(printf("Action %s failed: id %ld -> err %d (errCode %ld)\n",
- action_name[action], id, errno, err));
- uerror("ReadFileEx", Nothing);
- }
- }
-}
-
-CAMLprim value win_wrap_overlapped (value fd) {
- CAMLparam1(fd);
- CAMLlocal1(res);
- D(printf("Wrapping file descriptor (async)\n"));
- res = caml_alloc_tuple(2);
- Store_field(res, 0, fd);
- Store_field(res, 1, Val_long(0));
- CAMLreturn(res);
-}
-
-/****/
-
-#define Handle(fd) Handle_val(Field(fd, 0))
-
-CAMLprim value win_read
-(value fd, value buf, value ofs, value len, value id) {
- CAMLparam4(fd, buf, ofs, len);
- struct caml_bigarray *buf_arr = Bigarray_val(buf);
-
- if (Field(fd, 1) == Val_long(0))
- overlapped_action (READ_OVERLAPPED, Long_val(id), Handle(fd),
- Array_data (buf_arr, ofs), Long_val(len));
- else
- thread_io (READ, Long_val(id), Field(fd, 1), Handle(fd),
- Array_data (buf_arr, ofs), Long_val(len));
- CAMLreturn (Val_unit);
-}
-
-CAMLprim value win_write
-(value fd, value buf, value ofs, value len, value id) {
- CAMLparam4(fd, buf, ofs, len);
- struct caml_bigarray *buf_arr = Bigarray_val(buf);
-
- if (Field(fd, 1) == Val_long(0))
- overlapped_action (WRITE_OVERLAPPED, Long_val(id), Handle(fd),
- Array_data (buf_arr, ofs), Long_val(len));
- else
- thread_io (WRITE, Long_val(id), Field(fd, 1), Handle(fd),
- Array_data (buf_arr, ofs), Long_val(len));
- CAMLreturn (Val_unit);
-}
-
-/*
-#ifndef SO_UPDATE_CONNECT_CONTEXT
-#define SO_UPDATE_CONNECT_CONTEXT 0x7010
-#endif
-
-static void after_connect (SOCKET s) {
- if (!setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0)) {
- win32_maperr (GetLastError ());
- uerror("after_connect", Nothing);
- }
-}
-*/
-
-static HANDLE events[MAXIMUM_WAIT_OBJECTS];
-//static OVERLAPPED oData[MAXIMUM_WAIT_OBJECTS];
-
-CAMLprim value win_register_wait (value socket, value kind, value idx) {
- CAMLparam3(socket, kind, idx);
- long i = Long_val(idx);
- long mask;
-
- D(printf("Register: i %ld, kind %ld\n", Long_val(i), Long_val(kind)));
- events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
- mask = (Long_val(kind) == 0) ? FD_CONNECT : FD_ACCEPT;
- if (WSAEventSelect(Socket_val(socket), events[i], mask) == SOCKET_ERROR) {
- win32_maperr(WSAGetLastError ());
- uerror("WSAEventSelect", Nothing);
- }
-
- CAMLreturn (Val_unit);
-}
-
-CAMLprim value win_check_connection (value socket, value kind, value idx) {
- CAMLparam3 (socket, kind, idx);
- WSANETWORKEVENTS evs;
- int res, err, i = Long_val(idx);
-
- D(printf("Check connection... %d\n", i));
- if (WSAEnumNetworkEvents(Socket_val(socket), NULL, &evs)) {
- win32_maperr(WSAGetLastError ());
- uerror("WSAEnumNetworkEvents", Nothing);
- }
- if (WSAEventSelect(Socket_val(socket), NULL, 0) == SOCKET_ERROR) {
- win32_maperr(WSAGetLastError ());
- uerror("WSAEventSelect", Nothing);
- }
- if (!CloseHandle(events[i])) {
- win32_maperr(GetLastError ());
- uerror("CloseHandle", Nothing);
- }
- err =
- evs.iErrorCode[(Long_val(kind) == 0) ? FD_CONNECT_BIT : FD_ACCEPT_BIT];
- D(printf("Check connection: %ld, err %d\n", evs.lNetworkEvents, err));
- if (err != 0) {
- win32_maperr(err);
- uerror("check_connection", Nothing);
- }
- CAMLreturn (Val_unit);
-}
-
-static HANDLE dummyEvent;
-
-CAMLprim value init_lwt (value callback) {
- CAMLparam1 (callback);
- // GUID GuidConnectEx = WSAID_CONNECTEX;
- // SOCKET s;
- // DWORD l;
- int i;
-
- D(printf("Init...\n"));
- register_global_root (&completionCallback);
- completionCallback = callback;
-
- dummyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Dummy event
-
- DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
- GetCurrentProcess (), &main_thread,
- 0, FALSE, DUPLICATE_SAME_ACCESS);
-
- /*
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s == INVALID_SOCKET) return Val_unit;
- WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
- &GuidConnectEx, sizeof(GuidConnectEx),
- &ConnectEx, sizeof(ConnectExPtr),
- &l, NULL, NULL);
- closesocket(s);
- */
-
- D(printf("Init done\n"));
- CAMLreturn (Val_long (MAXIMUM_WAIT_OBJECTS));
-}
-
-CAMLprim value win_wait (value timeout, value event_count) {
- CAMLparam2(timeout, event_count);
- DWORD t, t2;
- DWORD res;
- long ret, n = Long_val(event_count);
- t = Long_val(timeout);
- if (t < 0) t = INFINITE;
- t2 = (compN > 0) ? 0 : t;
- D(printf("Waiting: %ld events, timeout %ldms -> %ldms\n", n, t, t2));
- res =
- (n > 0) ?
- WaitForMultipleObjectsEx(n, events, FALSE, t, TRUE) :
- WaitForMultipleObjectsEx(1, &dummyEvent, FALSE, t, TRUE);
- D(printf("Done waiting\n"));
- if ((t != t2) && (res == WAIT_TIMEOUT)) res = WAIT_IO_COMPLETION;
- switch (res) {
- case WAIT_TIMEOUT:
- D(printf("Timeout\n"));
- ret = -1;
- break;
- case WAIT_IO_COMPLETION:
- D(printf("I/O completion\n"));
- ret = -2;
- break;
- case WAIT_FAILED:
- D(printf("Wait failed\n"));
- ret = 0;
- win32_maperr (GetLastError ());
- uerror("WaitForMultipleObjectsEx", Nothing);
- break;
- default:
- ret = res;
- D(printf("Event: %ld\n", res));
- break;
- }
- get_queue (Val_unit);
- CAMLreturn (Val_long(ret));
-}
-
-static long pipeSerial;
-
-value win_pipe(long readMode, long writeMode) {
- CAMLparam0();
- SECURITY_ATTRIBUTES attr;
- HANDLE readh, writeh;
- CHAR name[MAX_PATH];
- CAMLlocal3(readfd, writefd, res);
-
- attr.nLength = sizeof(attr);
- attr.lpSecurityDescriptor = NULL;
- attr.bInheritHandle = TRUE;
-
- sprintf(name, "\\\\.\\Pipe\\UnisonAnonPipe.%08lx.%08lx",
- GetCurrentProcessId(), pipeSerial++);
-
- readh =
- CreateNamedPipeA
- (name, PIPE_ACCESS_INBOUND | readMode, PIPE_TYPE_BYTE | PIPE_WAIT,
- 1, UNIX_BUFFER_SIZE, UNIX_BUFFER_SIZE, 0, &attr);
-
- if (readh == INVALID_HANDLE_VALUE) {
- win32_maperr(GetLastError());
- uerror("CreateNamedPipe", Nothing);
- return FALSE;
- }
-
- writeh =
- CreateFileA
- (name, GENERIC_WRITE, 0, &attr, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | writeMode, NULL);
-
- if (writeh == INVALID_HANDLE_VALUE) {
- win32_maperr(GetLastError());
- CloseHandle(readh);
- uerror("CreateFile", Nothing);
- return FALSE;
- }
-
- readfd = win_alloc_handle(readh);
- writefd = win_alloc_handle(writeh);
- res = alloc_small(2, 0);
- Store_field(res, 0, readfd);
- Store_field(res, 1, writefd);
- CAMLreturn (res);
-}
-
-CAMLprim value win_pipe_in (value unit) {
- CAMLparam0();
- CAMLreturn (win_pipe (FILE_FLAG_OVERLAPPED, 0));
-}
-
-CAMLprim value win_pipe_out (value unit) {
- CAMLparam0();
- CAMLreturn (win_pipe (0, FILE_FLAG_OVERLAPPED));
-}
-
-static int socket_domain_table[] = {
- PF_UNIX, PF_INET
-};
-
-static int socket_type_table[] = {
- SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET
-};
-
-CAMLprim value win_socket (value domain, value type, value proto) {
- CAMLparam3(domain, type, proto);
- SOCKET s;
-
- s = WSASocket(socket_domain_table[Int_val(domain)],
- socket_type_table[Int_val(type)],
- Int_val(proto),
- NULL, 0, WSA_FLAG_OVERLAPPED);
- D(printf("Created socket %lx\n", (long)s));
- if (s == INVALID_SOCKET) {
- win32_maperr(WSAGetLastError ());
- uerror("WSASocket", Nothing);
- }
- CAMLreturn(win_alloc_socket(s));
-}
-
-/*
-#ifndef WSAID_CONNECTEX
-#define WSAID_CONNECTEX \
- {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
-#endif
-
-typedef BOOL (WINAPI *ConnectExPtr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED);
-
-static ConnectExPtr ConnectEx = NULL;
-
-CAMLprim value win_connect (value socket, value address, value id) {
- CAMLparam3(socket, address, id);
- SOCKET s = Socket_val (socket);
- struct sockaddr addr;
- int addr_len;
- DWORD err;
- int i;
-
- if (ConnectEx == NULL) {
- errno = ENOSYS;
- uerror("ConnectEx", Nothing);
- }
- if (eventCount == MAXIMUM_WAIT_OBJECTS) {
- errno = EAGAIN;
- uerror("ConnectEx", Nothing);
- }
- i = free_list[eventCount];
- eventCount++;
-
- ZeroMemory(&(oData[i]), sizeof(OVERLAPPED));
- oData[i].hEvent = events[i];
- ids[i] = Long_val(id);
- sockets[i] = s;
-
- get_sockaddr(address, &addr, &addr_len);
- if (!ConnectEx(s, &addr, addr_len, NULL, 0, 0, &(oData[i]))) {
- err = WSAGetLastError ();
- if (err != ERROR_IO_PENDING) {
- win32_maperr(err);
- uerror("ConnectEx", Nothing);
- }
- } else
- after_connect(s);
- CAMLreturn (Val_unit);
-}
-*/
-
-static int notify_filter_flags[8] = {
- 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_LAST_ACCESS,
- FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_SECURITY
-};
-
-CAMLprim value win_readdirtorychanges
-(value fd_val, value buf_val, value recursive, value flags, value id_val) {
- CAMLparam5(fd_val, buf_val, recursive, flags, id_val);
- struct caml_bigarray *buf_arr = Bigarray_val(buf_val);
- long id = Long_val(id_val);
- HANDLE fd = Handle_val(fd_val);
- char * buf = Array_data (buf_arr, 0);
- long len = buf_arr->dim[0];
- long action = READDIRECTORYCHANGES;
- BOOL res;
- long err;
- int notify_filter = convert_flag_list(flags, notify_filter_flags);
- completionData * d = GlobalAlloc(GPTR, sizeof(completionData));
- if (d == NULL) {
- errno = ENOMEM;
- uerror(action_name[action], Nothing);
- }
- d->id = id;
- d->action = action;
-
- D(printf("Starting %s: id %ld, len %ld\n", action_name[action], id, len));
-
- res = ReadDirectoryChangesW (fd, buf, len, Bool_val(recursive),
- notify_filter, NULL, &(d->overlapped),
- overlapped_completion);
-
- if (!res) {
- err = GetLastError ();
- if (err != ERROR_IO_PENDING) {
- win32_maperr (err);
- D(printf("Action %s failed: id %ld -> err %d (errCode %ld)\n",
- action_name[action], id, errno, err));
- uerror("ReadDirectoryChangesW", Nothing);
- }
- }
- CAMLreturn (Val_unit);
-}
-
-CAMLprim value win_parse_directory_changes (value buf_val) {
- CAMLparam1(buf_val);
- CAMLlocal4(lst, tmp, elt, filename);
- struct caml_bigarray *buf_arr = Bigarray_val(buf_val);
- char * pos = Array_data (buf_arr, 0);
- FILE_NOTIFY_INFORMATION * entry;
-
- lst = Val_long(0);
- while (1) {
- entry = (FILE_NOTIFY_INFORMATION *)pos;
- elt = caml_alloc_tuple(2);
- filename = caml_alloc_string(entry->FileNameLength);
- memmove(String_val(filename), entry->FileName, entry->FileNameLength);
- Store_field (elt, 0, filename);
- Store_field (elt, 1, Val_long(entry->Action - 1));
- tmp = caml_alloc_tuple(2);
- Store_field (tmp, 0, elt);
- Store_field (tmp, 1, lst);
- lst = tmp;
- if (entry->NextEntryOffset == 0) break;
- pos += entry->NextEntryOffset;
- }
- CAMLreturn(lst);
-}
-
-CAMLprim value win_open_directory (value path, value wpath) {
- CAMLparam2 (path, wpath);
- HANDLE h;
- h = CreateFileW((LPCWSTR) String_val(wpath),
- FILE_LIST_DIRECTORY,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
- NULL);
- if (h == INVALID_HANDLE_VALUE) {
- win32_maperr (GetLastError ());
- uerror("open", path);
- }
- CAMLreturn(win_alloc_handle(h));
-}
+#include <winsock2.h>
+#include <windows.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#include <caml/fail.h>
+#include <caml/bigarray.h>
+#include <caml/callback.h>
+
+//#define D(x) x
+#define D(x) while(0){}
+
+#define UNIX_BUFFER_SIZE 16384
+#define Nothing ((value) 0)
+
+typedef struct
+{
+ OVERLAPPED overlapped;
+ long id;
+ long action;
+} completionData;
+
+struct filedescr {
+ union {
+ HANDLE handle;
+ SOCKET socket;
+ } fd;
+ enum { KIND_HANDLE, KIND_SOCKET } kind;
+ int crt_fd;
+};
+#define Handle_val(v) (((struct filedescr *) Data_custom_val(v))->fd.handle)
+#define Socket_val(v) (((struct filedescr *) Data_custom_val(v))->fd.socket)
+
+extern void win32_maperr (DWORD errcode);
+extern void uerror (char * cmdname, value arg);
+extern value unix_error_of_code (int errcode);
+extern value win_alloc_handle (HANDLE h);
+extern value win_alloc_socket(SOCKET);
+extern void get_sockaddr (value mladdr,
+ struct sockaddr * addr /*out*/,
+ int * addr_len /*out*/);
+
+#define Array_data(a, i) (((char *) a->data) + Long_val(i))
+
+CAMLprim value ml_blit_string_to_buffer
+(value s, value i, value a, value j, value l)
+{
+ char *src = String_val(s) + Int_val(i);
+ char *dest = Array_data(Bigarray_val(a), j);
+ memcpy(dest, src, Long_val(l));
+ return Val_unit;
+}
+
+CAMLprim value ml_blit_buffer_to_string
+(value a, value i, value s, value j, value l)
+{
+ char *src = Array_data(Bigarray_val(a), i);
+ char *dest = String_val(s) + Long_val(j);
+ memcpy(dest, src, Long_val(l));
+ return Val_unit;
+}
+
+/****/
+
+#define READ 0
+#define WRITE 1
+#define READ_OVERLAPPED 2
+#define WRITE_OVERLAPPED 3
+#define READDIRECTORYCHANGES 4
+static char * action_name[5] = {
+ "read", "write", "read(overlapped)", "write(overlapped)",
+ "ReadDirectoryChangesW"
+};
+
+static value completionCallback;
+
+static void invoke_completion_callback
+(long id, long len, long errCode, long action) {
+ CAMLlocal2 (err, name);
+ value args[4];
+ err = Val_long(0);
+ if (errCode != NO_ERROR) {
+ len = -1;
+ win32_maperr (errCode);
+ err = unix_error_of_code(errno);
+ }
+ name = copy_string (action_name[action]);
+ D(printf("Action %s completed: id %ld -> len %ld / err %d (errCode %ld)\n",
+ action_name[action], id, len, errno, errCode));
+ args[0] = Val_long(id);
+ args[1] = Val_long(len);
+ args[2] = err;
+ args[3] = name;
+ caml_callbackN(completionCallback, 4, args);
+ D(printf("Callback performed\n"));
+}
+
+typedef struct {
+ long id;
+ long len;
+ long errCode;
+ long action; } completionInfo;
+
+int compN = 0;
+int complQueueSize = 0;
+completionInfo * complQueue = NULL;
+
+static void completion (long id, long len, long errCode, long action) {
+ D(printf("Queueing action %s: id %ld -> len %ld / err %d (errCode %ld)\n",
+ action_name[action], id, len, errno, errCode));
+ if (compN + 1 > complQueueSize) {
+ completionInfo * queue;
+ int n = complQueueSize * 2 + 1;
+ D(printf("Resizing queue to %d\n", n));
+ queue = (completionInfo *) GlobalAlloc(GPTR, n * sizeof(completionInfo));
+ if (complQueue != NULL)
+ CopyMemory (queue, complQueue, complQueueSize * sizeof(completionInfo));
+ complQueue = queue;
+ complQueueSize = n;
+ }
+ complQueue[compN].id = id;
+ complQueue[compN].len = len;
+ complQueue[compN].errCode = errCode;
+ complQueue[compN].action = action;
+ compN++;
+}
+
+CAMLprim value get_queue (value unit) {
+ CAMLparam1 (unit);
+ int i;
+ for (i = 0; i < compN; i++)
+ invoke_completion_callback
+ (complQueue[i].id, complQueue[i].len,
+ complQueue[i].errCode, complQueue[i].action);
+ compN = 0;
+ CAMLreturn (Val_unit);
+}
+
+/****/
+
+static HANDLE main_thread;
+
+static DWORD CALLBACK helper_thread (void * param) {
+ D(printf("Helper thread created\n"));
+ while (1) SleepEx(INFINITE, TRUE);
+ return 0;
+}
+
+static VOID CALLBACK exit_thread(ULONG_PTR param) {
+ D(printf("Helper thread exiting\n"));
+ ExitThread(0);
+}
+
+static HANDLE get_helper_thread (value threads, int kind) {
+ HANDLE h = (HANDLE) Field(threads, kind);
+
+ if (h != INVALID_HANDLE_VALUE) return h;
+
+ h = CreateThread (NULL, 0, helper_thread, NULL, 0, NULL);
+ if (h == NULL) {
+ win32_maperr (GetLastError ());
+ uerror("createHelperThread", Nothing);
+ }
+ Field(threads, kind) = (value) h;
+ return h;
+}
+
+static void kill_thread (HANDLE *h) {
+ D(printf("Killing thread\n"));
+ QueueUserAPC(exit_thread, *h, 0);
+ CloseHandle(*h);
+ *h = INVALID_HANDLE_VALUE;
+}
+
+CAMLprim value win_kill_threads (value fd) {
+ CAMLparam1(fd);
+ if (Field(fd, 1) != Val_long(0)) {
+ kill_thread((HANDLE *) &Field(Field(fd, 1), READ));
+ kill_thread((HANDLE *) &Field(Field(fd, 1), WRITE));
+ }
+ CAMLreturn(Val_unit);
+}
+
+CAMLprim value win_wrap_fd (value fd) {
+ CAMLparam1(fd);
+ CAMLlocal2(th, res);
+ D(printf("Wrapping file descriptor (sync)\n"));
+ res = caml_alloc_tuple(2);
+ Store_field(res, 0, fd);
+ th = caml_alloc(2, Abstract_tag);
+ Field(th, READ) = (value) INVALID_HANDLE_VALUE;
+ Field(th, WRITE) = (value) INVALID_HANDLE_VALUE;
+ Store_field(res, 1, th);
+ CAMLreturn(res);
+}
+
+/****/
+
+typedef struct {
+ long action;
+ long id;
+ HANDLE fd;
+ char * buffer;
+ long len;
+ long error;
+} ioInfo;
+
+
+static VOID CALLBACK thread_completion(ULONG_PTR param) {
+ ioInfo * info = (ioInfo *) param;
+ completion (info->id, info->len, info->error, info->action);
+ GlobalFree (info);
+}
+
+static VOID CALLBACK perform_io_on_thread(ULONG_PTR param) {
+ ioInfo * info = (ioInfo *) param;
+ DWORD l;
+ BOOL res;
+
+ D(printf("Starting %s: id %ld, len %ld\n",
+ action_name[info->action], info->id, info->len));
+
+ res =
+ (info->action == READ)?
+ ReadFile(info->fd, info->buffer,info->len, &l, NULL):
+ WriteFile(info->fd, info->buffer,info->len, &l, NULL);
+ if (!res) {
+ info->len = -1;
+ info->error = GetLastError ();
+ } else {
+ info->len = l;
+ info->error = NO_ERROR;
+ }
+ D(printf("Action %s done: id %ld -> len %ld / err %d (errCode %ld)\n",
+ action_name[info->action],
+ info->id, info->len, errno, info->error));
+ QueueUserAPC(thread_completion, main_thread, param);
+}
+
+static void thread_io
+(long action, long id, value threads, HANDLE h, char * buf, long len) {
+ struct caml_bigarray *buf_arr = Bigarray_val(buf);
+ ioInfo * info = GlobalAlloc(GPTR, sizeof(ioInfo));
+ if (info == NULL) {
+ errno = ENOMEM;
+ uerror(action_name[action], Nothing);
+ }
+
+ info->action = action;
+ info->id = id;
+ info->fd = h;
+ info->buffer = buf;
+ info->len = len;
+
+ h = get_helper_thread(threads, action);
+ QueueUserAPC(perform_io_on_thread, h, (ULONG_PTR) info);
+}
+
+/****/
+
+static void CALLBACK overlapped_completion
+(DWORD errCode, DWORD len, LPOVERLAPPED overlapped) {
+ completionData * d = (completionData * )overlapped;
+ completion (d->id, len, errCode, d->action);
+ GlobalFree (d);
+}
+
+static void overlapped_action(long action, long id,
+ HANDLE fd, char *buf, long len) {
+ BOOL res;
+ long err;
+ completionData * d = GlobalAlloc(GPTR, sizeof(completionData));
+ if (d == NULL) {
+ errno = ENOMEM;
+ uerror(action_name[action], Nothing);
+ }
+ d->id = id;
+ d->action = action;
+
+ D(printf("Starting %s: id %ld, len %ld\n", action_name[action], id, len));
+ res =
+ (action == READ_OVERLAPPED)?
+ ReadFileEx(fd, buf, len, &(d->overlapped), overlapped_completion):
+ WriteFileEx(fd, buf, len, &(d->overlapped), overlapped_completion);
+
+ if (!res) {
+ err = GetLastError ();
+ if (err != ERROR_IO_PENDING) {
+ win32_maperr (err);
+ D(printf("Action %s failed: id %ld -> err %d (errCode %ld)\n",
+ action_name[action], id, errno, err));
+ uerror("ReadFileEx", Nothing);
+ }
+ }
+}
+
+CAMLprim value win_wrap_overlapped (value fd) {
+ CAMLparam1(fd);
+ CAMLlocal1(res);
+ D(printf("Wrapping file descriptor (async)\n"));
+ res = caml_alloc_tuple(2);
+ Store_field(res, 0, fd);
+ Store_field(res, 1, Val_long(0));
+ CAMLreturn(res);
+}
+
+/****/
+
+#define Handle(fd) Handle_val(Field(fd, 0))
+
+CAMLprim value win_read
+(value fd, value buf, value ofs, value len, value id) {
+ CAMLparam4(fd, buf, ofs, len);
+ struct caml_bigarray *buf_arr = Bigarray_val(buf);
+
+ if (Field(fd, 1) == Val_long(0))
+ overlapped_action (READ_OVERLAPPED, Long_val(id), Handle(fd),
+ Array_data (buf_arr, ofs), Long_val(len));
+ else
+ thread_io (READ, Long_val(id), Field(fd, 1), Handle(fd),
+ Array_data (buf_arr, ofs), Long_val(len));
+ CAMLreturn (Val_unit);
+}
+
+CAMLprim value win_write
+(value fd, value buf, value ofs, value len, value id) {
+ CAMLparam4(fd, buf, ofs, len);
+ struct caml_bigarray *buf_arr = Bigarray_val(buf);
+
+ if (Field(fd, 1) == Val_long(0))
+ overlapped_action (WRITE_OVERLAPPED, Long_val(id), Handle(fd),
+ Array_data (buf_arr, ofs), Long_val(len));
+ else
+ thread_io (WRITE, Long_val(id), Field(fd, 1), Handle(fd),
+ Array_data (buf_arr, ofs), Long_val(len));
+ CAMLreturn (Val_unit);
+}
+
+/*
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+static void after_connect (SOCKET s) {
+ if (!setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0)) {
+ win32_maperr (GetLastError ());
+ uerror("after_connect", Nothing);
+ }
+}
+*/
+
+static HANDLE events[MAXIMUM_WAIT_OBJECTS];
+//static OVERLAPPED oData[MAXIMUM_WAIT_OBJECTS];
+
+CAMLprim value win_register_wait (value socket, value kind, value idx) {
+ CAMLparam3(socket, kind, idx);
+ long i = Long_val(idx);
+ long mask;
+
+ D(printf("Register: i %ld, kind %ld\n", Long_val(i), Long_val(kind)));
+ events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ mask = (Long_val(kind) == 0) ? FD_CONNECT : FD_ACCEPT;
+ if (WSAEventSelect(Socket_val(socket), events[i], mask) == SOCKET_ERROR) {
+ win32_maperr(WSAGetLastError ());
+ uerror("WSAEventSelect", Nothing);
+ }
+
+ CAMLreturn (Val_unit);
+}
+
+CAMLprim value win_check_connection (value socket, value kind, value idx) {
+ CAMLparam3 (socket, kind, idx);
+ WSANETWORKEVENTS evs;
+ int res, err, i = Long_val(idx);
+
+ D(printf("Check connection... %d\n", i));
+ if (WSAEnumNetworkEvents(Socket_val(socket), NULL, &evs)) {
+ win32_maperr(WSAGetLastError ());
+ uerror("WSAEnumNetworkEvents", Nothing);
+ }
+ if (WSAEventSelect(Socket_val(socket), NULL, 0) == SOCKET_ERROR) {
+ win32_maperr(WSAGetLastError ());
+ uerror("WSAEventSelect", Nothing);
+ }
+ if (!CloseHandle(events[i])) {
+ win32_maperr(GetLastError ());
+ uerror("CloseHandle", Nothing);
+ }
+ err =
+ evs.iErrorCode[(Long_val(kind) == 0) ? FD_CONNECT_BIT : FD_ACCEPT_BIT];
+ D(printf("Check connection: %ld, err %d\n", evs.lNetworkEvents, err));
+ if (err != 0) {
+ win32_maperr(err);
+ uerror("check_connection", Nothing);
+ }
+ CAMLreturn (Val_unit);
+}
+
+static HANDLE dummyEvent;
+
+CAMLprim value init_lwt (value callback) {
+ CAMLparam1 (callback);
+ // GUID GuidConnectEx = WSAID_CONNECTEX;
+ // SOCKET s;
+ // DWORD l;
+ int i;
+
+ D(printf("Init...\n"));
+ register_global_root (&completionCallback);
+ completionCallback = callback;
+
+ dummyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Dummy event
+
+ DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+ GetCurrentProcess (), &main_thread,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+
+ /*
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s == INVALID_SOCKET) return Val_unit;
+ WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &GuidConnectEx, sizeof(GuidConnectEx),
+ &ConnectEx, sizeof(ConnectExPtr),
+ &l, NULL, NULL);
+ closesocket(s);
+ */
+
+ D(printf("Init done\n"));
+ CAMLreturn (Val_long (MAXIMUM_WAIT_OBJECTS));
+}
+
+CAMLprim value win_wait (value timeout, value event_count) {
+ CAMLparam2(timeout, event_count);
+ DWORD t, t2;
+ DWORD res;
+ long ret, n = Long_val(event_count);
+ t = Long_val(timeout);
+ if (t < 0) t = INFINITE;
+ t2 = (compN > 0) ? 0 : t;
+ D(printf("Waiting: %ld events, timeout %ldms -> %ldms\n", n, t, t2));
+ res =
+ (n > 0) ?
+ WaitForMultipleObjectsEx(n, events, FALSE, t, TRUE) :
+ WaitForMultipleObjectsEx(1, &dummyEvent, FALSE, t, TRUE);
+ D(printf("Done waiting\n"));
+ if ((t != t2) && (res == WAIT_TIMEOUT)) res = WAIT_IO_COMPLETION;
+ switch (res) {
+ case WAIT_TIMEOUT:
+ D(printf("Timeout\n"));
+ ret = -1;
+ break;
+ case WAIT_IO_COMPLETION:
+ D(printf("I/O completion\n"));
+ ret = -2;
+ break;
+ case WAIT_FAILED:
+ D(printf("Wait failed\n"));
+ ret = 0;
+ win32_maperr (GetLastError ());
+ uerror("WaitForMultipleObjectsEx", Nothing);
+ break;
+ default:
+ ret = res;
+ D(printf("Event: %ld\n", res));
+ break;
+ }
+ get_queue (Val_unit);
+ CAMLreturn (Val_long(ret));
+}
+
+static long pipeSerial;
+
+value win_pipe(long readMode, long writeMode) {
+ CAMLparam0();
+ SECURITY_ATTRIBUTES attr;
+ HANDLE readh, writeh;
+ CHAR name[MAX_PATH];
+ CAMLlocal3(readfd, writefd, res);
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+
+ sprintf(name, "\\\\.\\Pipe\\UnisonAnonPipe.%08lx.%08lx",
+ GetCurrentProcessId(), pipeSerial++);
+
+ readh =
+ CreateNamedPipeA
+ (name, PIPE_ACCESS_INBOUND | readMode, PIPE_TYPE_BYTE | PIPE_WAIT,
+ 1, UNIX_BUFFER_SIZE, UNIX_BUFFER_SIZE, 0, &attr);
+
+ if (readh == INVALID_HANDLE_VALUE) {
+ win32_maperr(GetLastError());
+ uerror("CreateNamedPipe", Nothing);
+ return FALSE;
+ }
+
+ writeh =
+ CreateFileA
+ (name, GENERIC_WRITE, 0, &attr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | writeMode, NULL);
+
+ if (writeh == INVALID_HANDLE_VALUE) {
+ win32_maperr(GetLastError());
+ CloseHandle(readh);
+ uerror("CreateFile", Nothing);
+ return FALSE;
+ }
+
+ readfd = win_alloc_handle(readh);
+ writefd = win_alloc_handle(writeh);
+ res = alloc_small(2, 0);
+ Store_field(res, 0, readfd);
+ Store_field(res, 1, writefd);
+ CAMLreturn (res);
+}
+
+CAMLprim value win_pipe_in (value unit) {
+ CAMLparam0();
+ CAMLreturn (win_pipe (FILE_FLAG_OVERLAPPED, 0));
+}
+
+CAMLprim value win_pipe_out (value unit) {
+ CAMLparam0();
+ CAMLreturn (win_pipe (0, FILE_FLAG_OVERLAPPED));
+}
+
+static int socket_domain_table[] = {
+ PF_UNIX, PF_INET
+};
+
+static int socket_type_table[] = {
+ SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET
+};
+
+CAMLprim value win_socket (value domain, value type, value proto) {
+ CAMLparam3(domain, type, proto);
+ SOCKET s;
+
+ s = WSASocket(socket_domain_table[Int_val(domain)],
+ socket_type_table[Int_val(type)],
+ Int_val(proto),
+ NULL, 0, WSA_FLAG_OVERLAPPED);
+ D(printf("Created socket %lx\n", (long)s));
+ if (s == INVALID_SOCKET) {
+ win32_maperr(WSAGetLastError ());
+ uerror("WSASocket", Nothing);
+ }
+ CAMLreturn(win_alloc_socket(s));
+}
+
+/*
+#ifndef WSAID_CONNECTEX
+#define WSAID_CONNECTEX \
+ {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
+#endif
+
+typedef BOOL (WINAPI *ConnectExPtr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED);
+
+static ConnectExPtr ConnectEx = NULL;
+
+CAMLprim value win_connect (value socket, value address, value id) {
+ CAMLparam3(socket, address, id);
+ SOCKET s = Socket_val (socket);
+ struct sockaddr addr;
+ int addr_len;
+ DWORD err;
+ int i;
+
+ if (ConnectEx == NULL) {
+ errno = ENOSYS;
+ uerror("ConnectEx", Nothing);
+ }
+ if (eventCount == MAXIMUM_WAIT_OBJECTS) {
+ errno = EAGAIN;
+ uerror("ConnectEx", Nothing);
+ }
+ i = free_list[eventCount];
+ eventCount++;
+
+ ZeroMemory(&(oData[i]), sizeof(OVERLAPPED));
+ oData[i].hEvent = events[i];
+ ids[i] = Long_val(id);
+ sockets[i] = s;
+
+ get_sockaddr(address, &addr, &addr_len);
+ if (!ConnectEx(s, &addr, addr_len, NULL, 0, 0, &(oData[i]))) {
+ err = WSAGetLastError ();
+ if (err != ERROR_IO_PENDING) {
+ win32_maperr(err);
+ uerror("ConnectEx", Nothing);
+ }
+ } else
+ after_connect(s);
+ CAMLreturn (Val_unit);
+}
+*/
+
+static int notify_filter_flags[8] = {
+ 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_LAST_ACCESS,
+ FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_SECURITY
+};
+
+CAMLprim value win_readdirtorychanges
+(value fd_val, value buf_val, value recursive, value flags, value id_val) {
+ CAMLparam5(fd_val, buf_val, recursive, flags, id_val);
+ struct caml_bigarray *buf_arr = Bigarray_val(buf_val);
+ long id = Long_val(id_val);
+ HANDLE fd = Handle_val(fd_val);
+ char * buf = Array_data (buf_arr, 0);
+ long len = buf_arr->dim[0];
+ long action = READDIRECTORYCHANGES;
+ BOOL res;
+ long err;
+ int notify_filter = convert_flag_list(flags, notify_filter_flags);
+ completionData * d = GlobalAlloc(GPTR, sizeof(completionData));
+ if (d == NULL) {
+ errno = ENOMEM;
+ uerror(action_name[action], Nothing);
+ }
+ d->id = id;
+ d->action = action;
+
+ D(printf("Starting %s: id %ld, len %ld\n", action_name[action], id, len));
+
+ res = ReadDirectoryChangesW (fd, buf, len, Bool_val(recursive),
+ notify_filter, NULL, &(d->overlapped),
+ overlapped_completion);
+
+ if (!res) {
+ err = GetLastError ();
+ if (err != ERROR_IO_PENDING) {
+ win32_maperr (err);
+ D(printf("Action %s failed: id %ld -> err %d (errCode %ld)\n",
+ action_name[action], id, errno, err));
+ uerror("ReadDirectoryChangesW", Nothing);
+ }
+ }
+ CAMLreturn (Val_unit);
+}
+
+CAMLprim value win_parse_directory_changes (value buf_val) {
+ CAMLparam1(buf_val);
+ CAMLlocal4(lst, tmp, elt, filename);
+ struct caml_bigarray *buf_arr = Bigarray_val(buf_val);
+ char * pos = Array_data (buf_arr, 0);
+ FILE_NOTIFY_INFORMATION * entry;
+
+ lst = Val_long(0);
+ while (1) {
+ entry = (FILE_NOTIFY_INFORMATION *)pos;
+ elt = caml_alloc_tuple(2);
+ filename = caml_alloc_string(entry->FileNameLength);
+ memmove(String_val(filename), entry->FileName, entry->FileNameLength);
+ Store_field (elt, 0, filename);
+ Store_field (elt, 1, Val_long(entry->Action - 1));
+ tmp = caml_alloc_tuple(2);
+ Store_field (tmp, 0, elt);
+ Store_field (tmp, 1, lst);
+ lst = tmp;
+ if (entry->NextEntryOffset == 0) break;
+ pos += entry->NextEntryOffset;
+ }
+ CAMLreturn(lst);
+}
+
+CAMLprim value win_open_directory (value path, value wpath) {
+ CAMLparam2 (path, wpath);
+ HANDLE h;
+ h = CreateFileW((LPCWSTR) String_val(wpath),
+ FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+ NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ win32_maperr (GetLastError ());
+ uerror("open", path);
+ }
+ CAMLreturn(win_alloc_handle(h));
+}