Skip to content

Commit 73ce323

Browse files
authored
Merge pull request #509 from patricoferris/fs-windows
Fs implementation for windows
2 parents bd77643 + 9cf5f35 commit 73ce323

File tree

9 files changed

+767
-54
lines changed

9 files changed

+767
-54
lines changed

lib_eio_windows/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(library
22
(name eio_windows)
33
(public_name eio_windows)
4-
(library_flags :standard -ccopt -lbcrypt)
4+
(library_flags :standard -ccopt -lbcrypt -ccopt -lntdll)
55
(enabled_if (= %{os_type} "Win32"))
66
(foreign_stubs
77
(language c)

lib_eio_windows/eio_windows.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ let run main =
3131
method mono_clock = Time.mono_clock
3232
method net = Net.v
3333
method domain_mgr = Domain_mgr.v
34-
method cwd = failwith "file-system operations not supported on Windows yet"
35-
method fs = failwith "file-system operations not supported on Windows yet"
34+
method cwd = ((Fs.cwd, "") :> Eio.Fs.dir Eio.Path.t)
35+
method fs = ((Fs.fs, "") :> Eio.Fs.dir Eio.Path.t)
3636
method process_mgr = failwith "process operations not supported on Windows yet"
3737
method secure_random = Flow.secure_random
3838
end

lib_eio_windows/eio_windows_stubs.c

Lines changed: 175 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,29 @@
1111
#include <assert.h>
1212
#include <ntstatus.h>
1313
#include <bcrypt.h>
14+
#include <winternl.h>
15+
#include <ntdef.h>
16+
17+
typedef ULONG (__stdcall *pNtCreateFile)(
18+
PHANDLE FileHandle,
19+
ULONG DesiredAccess,
20+
PVOID ObjectAttributes,
21+
PVOID IoStatusBlock,
22+
PLARGE_INTEGER AllocationSize,
23+
ULONG FileAttributes,
24+
ULONG ShareAccess,
25+
ULONG CreateDisposition,
26+
ULONG CreateOptions,
27+
PVOID EaBuffer,
28+
ULONG EaLength
29+
);
1430

1531
#include <caml/mlvalues.h>
1632
#include <caml/memory.h>
1733
#include <caml/alloc.h>
1834
#include <caml/unixsupport.h>
1935
#include <caml/bigarray.h>
36+
#include <caml/osdeps.h>
2037

2138
#ifdef ARCH_SIXTYFOUR
2239
#define Int63_val(v) Long_val(v)
@@ -64,19 +81,170 @@ CAMLprim value caml_eio_windows_pwritev(value v_fd, value v_bufs, value v_offset
6481
uerror("pwritev is not supported on windows yet", Nothing);
6582
}
6683

67-
CAMLprim value caml_eio_windows_openat(value v_dirfd, value v_pathname, value v_flags, value v_mode)
68-
{
69-
uerror("openat is not supported on windows yet", Nothing);
84+
// File-system operations
85+
86+
// No follow
87+
void no_follow(HANDLE h) {
88+
BY_HANDLE_FILE_INFORMATION b;
89+
90+
if (!GetFileInformationByHandle(h, &b)) {
91+
caml_win32_maperr(GetLastError());
92+
uerror("nofollow", Nothing);
93+
}
94+
95+
if (b.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
96+
CloseHandle(h);
97+
caml_unix_error(ELOOP, "nofollow", Nothing);
98+
}
7099
}
71100

72-
CAMLprim value caml_eio_windows_mkdirat(value v_fd, value v_path, value v_perm)
101+
// We recreate an openat like function using NtCreateFile
102+
CAMLprim value caml_eio_windows_openat(value v_dirfd, value v_nofollow, value v_pathname, value v_desired_access, value v_create_disposition, value v_create_options)
73103
{
74-
uerror("mkdirat is not supported on windows yet", Nothing);
104+
CAMLparam2(v_dirfd, v_pathname);
105+
HANDLE h, dir;
106+
OBJECT_ATTRIBUTES obj_attr;
107+
IO_STATUS_BLOCK io_status;
108+
wchar_t *pathname;
109+
UNICODE_STRING relative;
110+
NTSTATUS r;
111+
112+
// Not sure what the overhead of this is, but it allows us to have low-level control
113+
// over file creation. In particular, we can specify the HANDLE to the parent directory
114+
// of a relative path a la openat.
115+
pNtCreateFile NtCreatefile = (pNtCreateFile)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateFile");
116+
caml_unix_check_path(v_pathname, "openat");
117+
pathname = caml_stat_strdup_to_utf16(String_val(v_pathname));
118+
RtlInitUnicodeString(&relative, pathname);
119+
120+
// If NULL the filepath has to be absolute
121+
if (Is_some(v_dirfd)) {
122+
dir = Handle_val(Field(v_dirfd, 0));
123+
} else {
124+
dir = NULL;
125+
}
126+
127+
// Initialise object attributes, passing in the root directory FD
128+
InitializeObjectAttributes(
129+
&obj_attr,
130+
&relative,
131+
OBJ_CASE_INSENSITIVE, // TODO: Double-check what flags need to be passed at this point.
132+
dir,
133+
NULL
134+
);
135+
136+
// Create the file
137+
r = NtCreatefile(
138+
&h,
139+
Int_val(v_desired_access) | FILE_READ_ATTRIBUTES,
140+
&obj_attr,
141+
&io_status,
142+
0, // Allocation size
143+
FILE_ATTRIBUTE_NORMAL, // TODO: Could check flags to see if we can do READONLY here a la OCaml
144+
(FILE_SHARE_READ | FILE_SHARE_WRITE),
145+
Int_val(v_create_disposition),
146+
(
147+
FILE_SYNCHRONOUS_IO_NONALERT
148+
| FILE_OPEN_FOR_BACKUP_INTENT
149+
| (Bool_val(v_nofollow) ? FILE_FLAG_OPEN_REPARSE_POINT : Int_val(v_create_options))),
150+
NULL, // Extended attribute buffer
151+
0 // Extended attribute buffer length
152+
);
153+
154+
// Free the allocated pathname
155+
caml_stat_free(pathname);
156+
157+
if (h == INVALID_HANDLE_VALUE) {
158+
caml_win32_maperr(RtlNtStatusToDosError(r));
159+
uerror("openat handle", v_pathname);
160+
}
161+
162+
if (!NT_SUCCESS(r)) {
163+
caml_win32_maperr(RtlNtStatusToDosError(r));
164+
uerror("openat", Nothing);
165+
}
166+
167+
// No follow check -- Windows doesn't actually have that ability
168+
// so we have to do it after the fact. This will raise if a symbolic
169+
// link is encountered and will close the handle.
170+
if (Bool_val(v_nofollow)) {
171+
no_follow(h);
172+
}
173+
174+
CAMLreturn(caml_win32_alloc_handle(h));
175+
}
176+
177+
value caml_eio_windows_openat_bytes(value* values, int argc) {
178+
return caml_eio_windows_openat(values[0], values[1], values[2], values[3], values[4], values[5]);
75179
}
76180

77-
CAMLprim value caml_eio_windows_unlinkat(value v_fd, value v_path, value v_dir)
181+
CAMLprim value caml_eio_windows_unlinkat(value v_dirfd, value v_pathname, value v_dir)
78182
{
79-
uerror("unlinkat is not supported on windows yet", Nothing);
183+
CAMLparam2(v_dirfd, v_pathname);
184+
HANDLE h, dir;
185+
OBJECT_ATTRIBUTES obj_attr;
186+
IO_STATUS_BLOCK io_status;
187+
wchar_t *pathname;
188+
UNICODE_STRING relative;
189+
NTSTATUS r;
190+
191+
// Not sure what the overhead of this is, but it allows us to have low-level control
192+
// over file creation. In particular, we can specify the HANDLE to the parent directory
193+
// of a relative path a la openat.
194+
pNtCreateFile NtCreatefile = (pNtCreateFile)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateFile");
195+
caml_unix_check_path(v_pathname, "openat");
196+
pathname = caml_stat_strdup_to_utf16(String_val(v_pathname));
197+
RtlInitUnicodeString(&relative, pathname);
198+
199+
// If NULL the filepath has to be absolute
200+
if (Is_some(v_dirfd)) {
201+
dir = Handle_val(Field(v_dirfd, 0));
202+
} else {
203+
dir = NULL;
204+
}
205+
206+
// Initialise object attributes, passing in the root directory FD
207+
InitializeObjectAttributes(
208+
&obj_attr,
209+
&relative,
210+
OBJ_CASE_INSENSITIVE, // TODO: Double-check what flags need to be passed at this point.
211+
dir,
212+
NULL
213+
);
214+
215+
// Create the file
216+
r = NtCreatefile(
217+
&h,
218+
(SYNCHRONIZE | DELETE),
219+
&obj_attr,
220+
&io_status,
221+
0, // Allocation size
222+
FILE_ATTRIBUTE_NORMAL, // TODO: Could check flags to see if we can do READONLY here a la OCaml
223+
(FILE_SHARE_DELETE),
224+
FILE_OPEN,
225+
((Bool_val(v_dir) ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE) | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE),
226+
NULL, // Extended attribute buffer
227+
0 // Extended attribute buffer length
228+
);
229+
230+
// Free the allocated pathname
231+
caml_stat_free(pathname);
232+
233+
if (h == INVALID_HANDLE_VALUE) {
234+
caml_win32_maperr(RtlNtStatusToDosError(r));
235+
uerror("openat", v_pathname);
236+
}
237+
238+
if (!NT_SUCCESS(r)) {
239+
caml_win32_maperr(RtlNtStatusToDosError(r));
240+
uerror("openat", Nothing);
241+
}
242+
243+
// Now close the file to delete it
244+
BOOL closed;
245+
closed = CloseHandle(h);
246+
247+
CAMLreturn(Val_unit);
80248
}
81249

82250
CAMLprim value caml_eio_windows_renameat(value v_old_fd, value v_old_path, value v_new_fd, value v_new_path)

0 commit comments

Comments
 (0)