Skip to content

Commit c6184d1

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 e694cbf commit c6184d1

6 files changed

Lines changed: 133 additions & 33 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: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -306,37 +306,99 @@ 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
309+
let last_chunk = Int64.((to_int size - 1) / to_int chunk_size) in
310+
if !Xapi_globs.vhd_legacy_blocks_format then
311+
(* Read backing file headers, then only read and write
310312
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
313+
let cluster_size, cluster_list =
314+
match driver with
315+
| "vhd" ->
316+
Vhd_tool_wrapper.parse_header path
317+
| "qcow2" ->
318+
Qcow_tool_wrapper.parse_header path
319+
| _ ->
320+
failwith (Printf.sprintf "%s: unreachable" __FUNCTION__)
321+
in
322+
let set =
323+
get_allocated_chunks_from_clusters cluster_size cluster_list
324+
in
325+
(* First and last chunks are always written - it's a limitation
324326
of the XVA format *)
325-
let last_chunk =
326-
Int64.((to_int size - to_int chunk_size + 1) / to_int chunk_size)
327-
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
327+
let set = set |> ChunkSet.add 0 |> ChunkSet.add last_chunk in
328+
ChunkSet.iter
329+
(fun this_chunk_no ->
330+
let offset =
331+
Int64.(mul (of_int this_chunk_no) chunk_size)
332+
in
333+
let _ =
334+
write_chunk this_chunk_no offset
335+
~write_check:(fun _ _ -> true)
336+
~seek:true ~timeout_workaround:false
337+
in
338+
()
339+
)
340+
set
341+
else
342+
let cluster_size, cluster_list =
343+
match driver with
344+
| "vhd" ->
345+
Vhd_tool_wrapper.parse_header_interval path
346+
| "qcow2" ->
347+
Qcow_tool_wrapper.parse_header_interval path
348+
| _ ->
349+
failwith (Printf.sprintf "%s: unreachable" __FUNCTION__)
350+
in
351+
let process_chunk chunk_no ~force =
352+
if force || (chunk_no <> 0 && chunk_no <> last_chunk) then
353+
let offset = Int64.(mul (of_int chunk_no) chunk_size) in
354+
let _ =
355+
write_chunk chunk_no offset
356+
~write_check:(fun _ _ -> true)
357+
~seek:true ~timeout_workaround:false
358+
in
359+
()
360+
in
361+
362+
process_chunk 0 ~force:true ;
363+
364+
let chunk_size = Int64.to_int chunk_size in
365+
let chunks_in_cluster =
366+
(cluster_size + chunk_size - 1) / chunk_size
367+
in
368+
(* Iterate over allocated intervals, copying every cluster inside *)
369+
let _ =
370+
List.fold_left
371+
(fun prev_chunk (cluster_no_left, cluster_no_right) ->
372+
let calc_chunk cluster =
373+
let cluster_offset = cluster * cluster_size in
374+
let chunk_no = cluster_offset / chunk_size in
375+
chunk_no
376+
in
377+
let left_chunk_no = calc_chunk cluster_no_left in
378+
let right_chunk_no =
379+
calc_chunk cluster_no_right + chunks_in_cluster - 1
380+
in
381+
382+
(* If a chunk contains multiple clusters, we could have
383+
already copied it. In that case, start with the
384+
following chunk. *)
385+
let left_chunk_no =
386+
if left_chunk_no = prev_chunk then
387+
left_chunk_no + 1
388+
else
389+
left_chunk_no
390+
in
391+
392+
for i = left_chunk_no to right_chunk_no do
393+
process_chunk i ~force:false
394+
done ;
395+
396+
right_chunk_no
397+
)
398+
(-1) cluster_list
399+
in
400+
401+
process_chunk last_chunk ~force:true
340402
with e ->
341403
debug "%s: Falling back to reading the whole raw disk after %s"
342404
__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)