Skip to content

Commit 675254a

Browse files
committed
vhd_qcow_parsing: Add parse_header_interval for interval-based headers
Since the runtime feature flag vhd_legacy_blocks_format determines which block format is used to describe allocated VHD clusters, this requires duplicate parse_header_interval functions for VHD and QCOW. The right functions are selected in stream_vdi based on the feature flag. Signed-off-by: Andrii Sultanov <andriy.sultanov@vates.tech>
1 parent 8279624 commit 675254a

6 files changed

Lines changed: 119 additions & 32 deletions

File tree

ocaml/xapi/qcow_tool_wrapper.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ let parse_header qcow_path =
3939
let pipe_reader = read_header qcow_path in
4040
Vhd_qcow_parsing.parse_header pipe_reader
4141

42+
let parse_header_interval qcow_path =
43+
let pipe_reader = read_header qcow_path in
44+
Vhd_qcow_parsing.parse_header_interval pipe_reader
45+
4246
let send ?relative_to (progress_cb : int -> unit) (unix_fd : Unix.file_descr)
4347
(path : string) (_size : Int64.t) =
4448
let qcow_of_device =

ocaml/xapi/qcow_tool_wrapper.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ val send :
2525
-> unit
2626

2727
val parse_header : string -> int * int list
28+
29+
val parse_header_interval : string -> int * (int * int) list

ocaml/xapi/stream_vdi.ml

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -306,37 +306,86 @@ let send_one ofd (__context : Context.t) rpc session_id progress refresh_session
306306
| Ok (Some (driver, path)) when driver = "vhd" || driver = "qcow2"
307307
-> (
308308
try
309-
(* Read backing file headers, then only read and write
310-
allocated clusters from the bitmap *)
311-
let cluster_size, cluster_list =
312-
match driver with
313-
| "vhd" ->
314-
Vhd_tool_wrapper.parse_header path
315-
| "qcow2" ->
316-
Qcow_tool_wrapper.parse_header path
317-
| _ ->
318-
failwith (Printf.sprintf "%s: unreachable" __FUNCTION__)
319-
in
320-
let set =
321-
get_allocated_chunks_from_clusters cluster_size cluster_list
322-
in
323-
(* First and last chunks are always written - it's a limitation
324-
of the XVA format *)
325309
let last_chunk =
326310
Int64.((to_int size - to_int chunk_size + 1) / to_int chunk_size)
327311
in
328-
let set = set |> ChunkSet.add 0 |> ChunkSet.add last_chunk in
329-
ChunkSet.iter
330-
(fun this_chunk_no ->
331-
let offset = Int64.(mul (of_int this_chunk_no) chunk_size) in
332-
let _ =
333-
write_chunk this_chunk_no offset
334-
~write_check:(fun _ _ -> true)
335-
~seek:true ~timeout_workaround:false
336-
in
337-
()
338-
)
339-
set
312+
if !Xapi_globs.vhd_legacy_blocks_format then
313+
(* Read backing file headers, then only read and write
314+
allocated clusters from the bitmap *)
315+
let cluster_size, cluster_list =
316+
match driver with
317+
| "vhd" ->
318+
Vhd_tool_wrapper.parse_header path
319+
| "qcow2" ->
320+
Qcow_tool_wrapper.parse_header path
321+
| _ ->
322+
failwith (Printf.sprintf "%s: unreachable" __FUNCTION__)
323+
in
324+
let set =
325+
get_allocated_chunks_from_clusters cluster_size cluster_list
326+
in
327+
(* First and last chunks are always written - it's a limitation
328+
of the XVA format *)
329+
let set = set |> ChunkSet.add 0 |> ChunkSet.add last_chunk in
330+
ChunkSet.iter
331+
(fun this_chunk_no ->
332+
let offset =
333+
Int64.(mul (of_int this_chunk_no) chunk_size)
334+
in
335+
let _ =
336+
write_chunk this_chunk_no offset
337+
~write_check:(fun _ _ -> true)
338+
~seek:true ~timeout_workaround:false
339+
in
340+
()
341+
)
342+
set
343+
else
344+
let cluster_size, cluster_list =
345+
match driver with
346+
| "vhd" ->
347+
Vhd_tool_wrapper.parse_header_interval path
348+
| "qcow2" ->
349+
Qcow_tool_wrapper.parse_header_interval path
350+
| _ ->
351+
failwith (Printf.sprintf "%s: unreachable" __FUNCTION__)
352+
in
353+
let process_chunk chunk_no ~force =
354+
if force || (chunk_no <> 0 && chunk_no <> last_chunk) then
355+
let offset = Int64.(mul (of_int chunk_no) chunk_size) in
356+
let _ =
357+
write_chunk chunk_no offset
358+
~write_check:(fun _ _ -> true)
359+
~seek:true ~timeout_workaround:false
360+
in
361+
()
362+
in
363+
364+
process_chunk 0 ~force:true ;
365+
366+
let chunk_size = Int64.to_int chunk_size in
367+
let chunks_in_cluster =
368+
(cluster_size + chunk_size - 1) / chunk_size
369+
in
370+
(* Iterate over allocated intervals, copying every cluster inside *)
371+
List.iter
372+
(fun (cluster_no_left, cluster_no_right) ->
373+
let calc_chunk cluster =
374+
let cluster_offset = cluster * cluster_size in
375+
let chunk_no = cluster_offset / chunk_size in
376+
chunk_no
377+
in
378+
let left_chunk_no = calc_chunk cluster_no_left in
379+
let right_chunk_no =
380+
calc_chunk cluster_no_right + chunks_in_cluster - 1
381+
in
382+
for i = left_chunk_no to right_chunk_no do
383+
process_chunk i ~force:false
384+
done
385+
)
386+
cluster_list ;
387+
388+
process_chunk last_chunk ~force:true
340389
with e ->
341390
debug "%s: Falling back to reading the whole raw disk after %s"
342391
__FUNCTION__ (Printexc.to_string e) ;

ocaml/xapi/vhd_qcow_parsing.ml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,36 @@ let run_tool tool ?(replace_fds = []) ?input_fd ?output_fd
4444
error "%s output: %s" tool out ;
4545
raise (Api_errors.Server_error (Api_errors.vdi_io_error, [out]))
4646

47-
let parse_header pipe_reader =
47+
let parse_header_aux pipe_reader =
4848
let ic = Unix.in_channel_of_descr pipe_reader in
4949
let buf = Buffer.create 4096 in
5050
let json = Yojson.Basic.from_channel ~buf ~fname:"header.json" ic in
5151
In_channel.close ic ;
5252
let cluster_size =
5353
1 lsl Yojson.Basic.Util.(member "cluster_bits" json |> to_int)
5454
in
55+
(cluster_size, json)
56+
57+
let parse_header pipe_reader =
58+
let cluster_size, json = parse_header_aux pipe_reader in
5559
let cluster_list =
5660
Yojson.Basic.Util.(member "data_clusters" json |> to_list |> List.map to_int)
5761
in
5862
(cluster_size, cluster_list)
63+
64+
let parse_header_interval pipe_reader =
65+
let cluster_size, json = parse_header_aux pipe_reader in
66+
let cluster_list =
67+
Yojson.Basic.Util.(
68+
member "data_clusters" json
69+
|> to_list
70+
|> List.map (fun x ->
71+
match to_list x with
72+
| x :: y :: _ ->
73+
(to_int x, to_int y)
74+
| _ ->
75+
raise (Invalid_argument "Invalid JSON")
76+
)
77+
)
78+
in
79+
(cluster_size, cluster_list)

ocaml/xapi/vhd_qcow_parsing.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ val run_tool :
2222
-> unit
2323

2424
val parse_header : Unix.file_descr -> int * int list
25+
26+
val parse_header_interval : Unix.file_descr -> int * (int * int) list

ocaml/xapi/vhd_tool_wrapper.ml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,14 @@ let receive progress_cb format protocol (s : Unix.file_descr)
116116
in
117117
run_vhd_tool progress_cb args s s' path
118118

119-
let read_vhd_header path =
119+
let read_vhd_header path ~legacy =
120120
let vhd_tool = !Xapi_globs.vhd_tool in
121-
let args = ["read_headers"; path] in
121+
let args =
122+
if legacy then
123+
["read_headers"; path]
124+
else
125+
["read_headers_interval"; path]
126+
in
122127
let pipe_reader, pipe_writer = Unix.pipe ~cloexec:true () in
123128

124129
let progress_cb _ = () in
@@ -130,9 +135,13 @@ let read_vhd_header path =
130135
pipe_reader
131136

132137
let parse_header vhd_path =
133-
let pipe_reader = read_vhd_header vhd_path in
138+
let pipe_reader = read_vhd_header vhd_path ~legacy:true in
134139
Vhd_qcow_parsing.parse_header pipe_reader
135140

141+
let parse_header_interval vhd_path =
142+
let pipe_reader = read_vhd_header vhd_path ~legacy:false in
143+
Vhd_qcow_parsing.parse_header_interval pipe_reader
144+
136145
let send progress_cb ?relative_to (protocol : string) (dest_format : string)
137146
(s : Unix.file_descr) (path : string) (size : Int64.t) (prefix : string) =
138147
let __FUN = __FUNCTION__ in

0 commit comments

Comments
 (0)