1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
let debug_io = ref false
let complete name offset op fd buffer =
let open Lwt in
let ofs = buffer.Cstruct.off in
let len = buffer.Cstruct.len in
let buf = buffer.Cstruct.buffer in
let rec loop acc fd buf ofs len =
op fd buf ofs len >>= fun n ->
let len' = len - n in
let acc' = acc + n in
if len' = 0 || n = 0
then return acc'
else loop acc' fd buf (ofs + n) len' in
loop 0 fd buf ofs len >>= fun n ->
if !debug_io
then Printf.fprintf stderr "%s offset=%s buffer = [%s](%d)\n%!"
name (match offset with Some x -> Int64.to_string x | None -> "None")
(if Cstruct.len buffer > 16
then (String.escaped (Cstruct.to_string (Cstruct.sub buffer 0 13))) ^ "..."
else (String.escaped (Cstruct.to_string buffer)))
(Cstruct.len buffer);
if n = 0 && len <> 0
then fail End_of_file
else return ()
module Fd = struct
open Lwt
type fd = {
fd: Lwt_unix.file_descr;
filename: string;
lock: Lwt_mutex.t;
}
let openfile filename rw =
let unix_fd = File.openfile filename rw 0o644 in
let fd = Lwt_unix.of_unix_file_descr unix_fd in
let lock = Lwt_mutex.create () in
return { fd; filename; lock }
let fsync { fd = fd; _ } =
let fd' = Lwt_unix.unix_file_descr fd in
File.fsync fd'
let _size_of_file t =
Lwt_unix.LargeFile.fstat t.fd >>= fun s ->
return s.Lwt_unix.LargeFile.st_size
let create filename =
Lwt_unix.openfile filename [ Unix.O_RDWR; Unix.O_CREAT; Unix.O_TRUNC ] 0o644 >>=
Lwt_unix.close >>= fun () ->
openfile filename true
let close t = Lwt_unix.close t.fd
exception Not_sector_aligned of int64
let assert_sector_aligned n =
if Int64.(mul(div n 512L) 512L) <> n then begin
Printf.fprintf stderr "ERROR: %Ld not sector aligned\n%!" n;
raise (Not_sector_aligned n)
end
let really_read { fd; lock; _ } offset buf =
assert_sector_aligned offset;
assert_sector_aligned (Int64.of_int buf.Cstruct.off);
assert_sector_aligned (Int64.of_int (Cstruct.len buf));
Lwt_mutex.with_lock lock
(fun () ->
Lwt.catch (fun () ->
Lwt_unix.LargeFile.lseek fd offset Unix.SEEK_SET >>= fun _ ->
complete "read" (Some offset) Lwt_bytes.read fd buf
) (function
| Unix.Unix_error(Unix.EINVAL, "read", "") as e ->
Printf.fprintf stderr "really_read offset = %Ld len = %d: EINVAL (alignment?)\n%!" offset (Cstruct.len buf);
fail e
| End_of_file as e ->
Printf.fprintf stderr "really_read offset = %Ld len = %d: End_of_file\n%!" offset (Cstruct.len buf);
fail e
| e ->
Printf.fprintf stderr "really_read offset = %Ld len = %d: %s\n%!"
offset (Cstruct.len buf) (Printexc.to_string e);
fail e
)
)
let really_write { fd; lock; _ } offset buf =
assert_sector_aligned offset;
assert_sector_aligned (Int64.of_int buf.Cstruct.off);
assert_sector_aligned (Int64.of_int (Cstruct.len buf));
Lwt_mutex.with_lock lock
(fun () ->
Lwt.catch (fun () ->
Lwt_unix.LargeFile.lseek fd offset Unix.SEEK_SET >>= fun _ ->
complete "write" (Some offset) Lwt_bytes.write fd buf
) (function
| Unix.Unix_error(Unix.EINVAL, "write", "") as e ->
Printf.fprintf stderr "really_write offset = %Ld len = %d: EINVAL (alignment?)\n%!" offset (Cstruct.len buf);
fail e
| End_of_file as e ->
Printf.fprintf stderr "really_write offset = %Ld len = %d: End_of_file\n%!" offset (Cstruct.len buf);
fail e
| e ->
Printf.fprintf stderr "really_write offset = %Ld len = %d: %s\n%!"
offset (Cstruct.len buf) (Printexc.to_string e);
fail e
)
)
let lseek { fd; _ } ofs cmd = Lwt_unix.LargeFile.lseek fd ofs cmd
let lseek_data { fd; _ } ofs = Lwt_preemptive.detach (File.lseek_data (Lwt_unix.unix_file_descr fd)) ofs
let lseek_hole { fd; _ } ofs = Lwt_preemptive.detach (File.lseek_hole (Lwt_unix.unix_file_descr fd)) ofs
end
module IO = struct
type 'a t = 'a Lwt.t
let (>>=) = Lwt.(>>=)
let return = Lwt.return
let fail = Lwt.fail
let exists path = return (try ignore(Unix.LargeFile.stat path); true with _ -> false)
let y2k = 946684800.0
let get_vhd_time time =
Int32.of_int (int_of_float (time -. y2k))
let now () =
let time = Unix.time() in
get_vhd_time time
let get_modification_time x =
Lwt_unix.LargeFile.stat x >>= fun st ->
return (get_vhd_time (st.Lwt_unix.LargeFile.st_mtime))
let get_file_size x =
try return (File.get_file_size x)
with e -> fail e
include Fd
end
include IO
let to_file_descr x = x.Fd.fd