Hi, The attached patch adds support for resizing MBR logical partitions. The failure is still there, I can't get any helpful information from lsof. Any suggestions?
>From b30a847db88e5ce75aa04d656d05b6cdb01e54fd Mon Sep 17 00:00:00 2001 From: root <[email protected]> Date: Mon, 2 Jun 2014 23:30:57 -0400 Subject: [PATCH] resize: add support for MBR logical partitions Signed-off-by: Hu Tao <[email protected]> --- resize/resize.ml | 243 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 202 insertions(+), 41 deletions(-) diff --git a/resize/resize.ml b/resize/resize.ml index 9614ec7..a476076 100644 --- a/resize/resize.ml +++ b/resize/resize.ml @@ -50,6 +50,10 @@ type partition = { p_id : partition_id; (* Partition (MBR/GPT) ID. *) p_type : partition_content; (* Content type and content size. *) p_label : string option; (* Label/name. *) + p_part_num: int; (* partition number *) + + mutable p_partitions : partition list; (* MBR logical partitions. Non-empty + list implies extended partition *) (* What we're going to do: *) mutable p_operation : partition_operation; @@ -90,7 +94,8 @@ let rec debug_partition p = (match p.p_label with | Some label -> label | None -> "(none)" - ) + ); + List.iter debug_partition p.p_partitions and string_of_partition_content = function | ContentUnknown -> "unknown data" | ContentPV sz -> sprintf "LVM PV (%Ld bytes)" sz @@ -443,6 +448,14 @@ read the man page virt-resize(1). if List.length parts = 0 then error (f_"the source disk has no partitions"); + let logical_partitions = + match parttype with + | GPT -> [] + | MBR -> + List.filter (fun ({ G.part_num = part_num }) -> + part_num >= 5_l + ) parts in + (* Filter out logical partitions. See note above. *) let parts = match parttype with @@ -453,10 +466,8 @@ read the man page virt-resize(1). | _ -> true ) parts in - let partitions = - List.map ( - fun ({ G.part_num = part_num } as part) -> - let part_num = Int32.to_int part_num in + let to_partition part = + let part_num = Int32.to_int part.G.part_num in let name = sprintf "/dev/sda%d" part_num in let bootable = g#part_get_bootable "/dev/sda" part_num in let id = @@ -476,13 +487,58 @@ read the man page virt-resize(1). { p_name = name; p_part = part; p_bootable = bootable; p_id = id; p_type = typ; - p_label = label; + p_label = label; p_part_num = part_num; + p_partitions = []; p_operation = OpCopy; p_target_partnum = 0; p_target_start = 0L; p_target_end = 0L } + in + + let logical_partitions = + List.map ( + fun part -> to_partition part + ) logical_partitions in + + let logical_partitions_num = List.length logical_partitions in + + let partitions = + List.map ( + fun part -> to_partition part ) parts in + let extended_partitions = + match parttype with + | GPT -> [] + | MBR -> + List.filter (fun ({ p_type = typ }) -> + typ = ContentExtendedPartition + ) partitions in + + + (* Filter out extended partition. *) + let partitions = + match parttype with + | GPT -> partitions + | MBR -> + List.filter (fun ({ p_type = typ }) -> + typ <> ContentExtendedPartition + ) partitions in + + assert ((List.length extended_partitions) <= 1); + + let cmp_partition parta partb = + if parta.p_part_num < partb.p_part_num then -1 + else if parta.p_part_num = partb.p_part_num then 0 + else 1 in + + List.iter (fun part -> + part.p_partitions <- List.merge cmp_partition part.p_partitions logical_partitions + ) extended_partitions; + + let partitions = List.merge cmp_partition partitions extended_partitions in + if verbose then ( - eprintf "%d partitions found\n" (List.length partitions); + eprintf "%d partitions found\n" ((List.length partitions) + + logical_partitions_num); List.iter debug_partition partitions ); @@ -506,14 +562,27 @@ read the man page virt-resize(1). ) partitions; (* Check partitions don't overlap. *) - let rec loop end_of_prev = function + let rec loop end_of_prev prev_typ = function + | [] -> () + | { p_name = name; p_part = { G.part_start = part_start }; p_type = typ } :: _ + when end_of_prev > part_start && prev_typ <> ContentExtendedPartition -> + error (f_"%s: this partition overlaps the previous one") name + | { p_part = { G.part_end = part_end }; p_type = typ } :: parts -> loop part_end typ parts + in + loop 0L ContentUnknown partitions; + + (* check logical partitions don't overlap *) + let rec loop end_of_prev extended_part = function | [] -> () - | { p_name = name; p_part = { G.part_start = part_start } } :: _ - when end_of_prev > part_start -> + | {p_name = name; p_part = {G.part_start = part_start } } :: _ + when end_of_prev > part_start -> error (f_"%s: this partition overlaps the previous one") name - | { p_part = { G.part_end = part_end } } :: parts -> loop part_end parts + | { p_part = { G.part_end = part_end; G.part_start = part_start } } :: parts -> + loop part_end extended_part parts in - loop 0L partitions; + List.iter (fun part -> + loop part.p_part.G.part_start part part.p_partitions + ) extended_partitions; partitions in @@ -578,6 +647,12 @@ read the man page virt-resize(1). let hash = Hashtbl.create 13 in List.iter (fun ({ p_name = name } as p) -> Hashtbl.add hash name p) partitions; + List.iter (fun p -> + if p.p_type = ContentExtendedPartition then ( + List.iter (fun ({ p_name = name } as p) -> Hashtbl.add hash name p) + p.p_partitions + ) + ) partitions; fun ~option name -> let name = if String.length name < 5 || String.sub name 0 5 <> "/dev/" then @@ -692,6 +767,26 @@ read the man page virt-resize(1). List.iter (do_resize ~option:"--resize") resizes; List.iter (do_resize ~option:"--resize-force" ~force:true) resizes_force; + (* handle resizing of logical partitions *) + List.iter ( + fun p -> + if p.p_type = ContentExtendedPartition then ( + let sectsize = Int64.of_int sectsize in + let size = roundup64 p.p_part.G.part_size sectsize in + let logical_sizes = List.fold_left ( + fun total p -> + match p.p_operation with + | OpDelete -> total +^ 0L + | OpCopy | OpIgnore -> total +^ p.p_part.G.part_size + | OpResize newsize -> total +^ newsize + ) 0L p.p_partitions in + if logical_sizes > size then + p.p_operation <- OpResize logical_sizes + (* don't touch the extended partition if logical sizes less + * then the original size *) + ) + ) partitions; + (* Helper function calculates the surplus space, given the total * required so far for the current partition layout, compared to * the size of the target disk. If the return value >= 0 then it's @@ -816,29 +911,31 @@ read the man page virt-resize(1). printf "**********\n\n"; printf "Summary of changes:\n\n"; - List.iter ( - fun ({ p_name = name; p_part = { G.part_size = oldsize }} as p) -> + let rec print_summary p = let text = match p.p_operation with | OpCopy -> - sprintf (f_"%s: This partition will be left alone.") name + sprintf (f_"%s: This partition will be left alone.") p.p_name | OpIgnore -> - sprintf (f_"%s: This partition will be created, but the contents will be ignored (ie. not copied to the target).") name + sprintf (f_"%s: This partition will be created, but the contents will be ignored (ie. not copied to the target).") p.p_name | OpDelete -> - sprintf (f_"%s: This partition will be deleted.") name + sprintf (f_"%s: This partition will be deleted.") p.p_name | OpResize newsize -> sprintf (f_"%s: This partition will be resized from %s to %s.") - name (human_size oldsize) (human_size newsize) ^ + p.p_name (human_size p.p_part.G.part_size) (human_size newsize) ^ if can_expand_content p.p_type then ( sprintf (f_" The %s on %s will be expanded using the '%s' method.") (string_of_partition_content_no_size p.p_type) - name + p.p_name (string_of_expand_content_method (expand_content_method p.p_type)) ) else "" in - wrap ~indent:4 (text ^ "\n\n") - ) partitions; + wrap ~indent:4 (text ^ "\n\n"); + + List.iter print_summary p.p_partitions in + + List.iter print_summary partitions; List.iter ( fun ({ lv_name = name } as lv) -> @@ -1009,10 +1106,11 @@ read the man page virt-resize(1). let partitions = let sectsize = Int64.of_int sectsize in - let rec loop partnum start = function + let rec loop partnum start gap create_surplus = function | p :: ps -> + let start = start +^ gap in (match p.p_operation with - | OpDelete -> loop partnum start ps (* skip p *) + | OpDelete -> loop partnum start 0L create_surplus ps (* skip p *) | OpIgnore | OpCopy -> (* same size *) (* Size in sectors. *) @@ -1026,26 +1124,28 @@ read the man page virt-resize(1). partnum start (end_ -^ 1L); { p with p_target_start = start; p_target_end = end_ -^ 1L; - p_target_partnum = partnum } :: loop (partnum+1) next ps + p_target_partnum = partnum; p_partitions = loop 5 start 1L + false p.p_partitions } :: loop (partnum+1) next 0L create_surplus ps | OpResize newsize -> (* resized partition *) + let oldsize = p.p_part.G.part_size in (* New size in sectors. *) let size = (newsize +^ sectsize -^ 1L) /^ sectsize in (* Start of next partition + alignment. *) let next = start +^ size in let next = roundup64 next alignment in - if verbose then - eprintf "target partition %d: resize: newsize=%Ld start=%Ld end=%Ld\n%!" - partnum newsize start (next -^ 1L); + eprintf "target partition %d: resize: oldsize=%Ld newsize=%Ld start=%Ld end=%Ld\n%!" + partnum oldsize newsize start (next -^ 1L); { p with p_target_start = start; p_target_end = next -^ 1L; - p_target_partnum = partnum } :: loop (partnum+1) next ps + p_target_partnum = partnum; p_partitions = loop 5 start 1L + false p.p_partitions } :: loop (partnum+1) next 0L create_surplus ps ) | [] -> (* Create the surplus partition if there is room for it. *) - if extra_partition && surplus >= min_extra_partition then ( + if create_surplus && extra_partition && surplus >= min_extra_partition then ( [ { (* Since this partition has no source, this data is * meaningless and not used since the operation is @@ -1056,6 +1156,8 @@ read the man page virt-resize(1). part_size = 0L }; p_bootable = false; p_id = No_ID; p_type = ContentUnknown; p_label = None; + p_part_num = 0; + p_partitions = []; (* Target information is meaningful. *) p_operation = OpIgnore; @@ -1078,14 +1180,52 @@ read the man page virt-resize(1). (* Preserve the existing start, but convert to sectors. *) (List.hd partitions).p_part.G.part_start /^ sectsize in - loop 1 start partitions in + loop 1 start 0L true partitions in + + let mbr_part_type x = + if x.p_part_num <= 4 && x.p_type <> ContentExtendedPartition then "primary" + else if x.p_part_num <= 4 && x.p_type = ContentExtendedPartition then "extended" + else "logical" in (* Now partition the target disk. *) List.iter ( fun p -> - g#part_add "/dev/sdb" "primary" p.p_target_start p.p_target_end + try + if p.p_operation <> OpIgnore then ( + g#part_add "/dev/sdb" (mbr_part_type p) p.p_target_start p.p_target_end + ); + if p.p_type = ContentExtendedPartition then + List.iter ( + fun p -> + if p.p_operation <> OpIgnore then + try + g#part_add "/dev/sdb" "logical" p.p_target_start p.p_target_end + with G.Error msg -> () + ) p.p_partitions + with + G.Error msg -> + (* eprintf "lsof:\n---\n%s\n---\n" (g#debug "sh" [| "lsof" * |]); *) + eprintf "original error: %s\n" msg; ) partitions; + let g = + g#shutdown (); + g#close (); + + let g = new G.guestfs () in + if trace then g#set_trace true; + if verbose then g#set_verbose true; + let _, { URI.path = path; protocol = protocol; + server = server; username = username; + password = password } = infile in + g#add_drive ?format ~readonly:true ~protocol ?server ?username ?secret:password path; + (* The output disk is being created, so use cache=unsafe here. *) + g#add_drive ?format:output_format ~readonly:false ~cachemode:"unsafe" + outfile; + if not quiet then Progress.set_up_progress_bar ~machine_readable g; + g#launch (); + g in + (* Copy over the data. *) List.iter ( fun p -> @@ -1113,13 +1253,31 @@ read the man page virt-resize(1). g#copy_device_to_device ~size:copysize ~sparse source target | ContentExtendedPartition -> - (* You can't just copy an extended partition by name, eg. - * source = "/dev/sda2", because the device name only covers - * the first 1K of the partition. Instead, copy the - * source bytes from the parent disk (/dev/sda). - *) - let srcoffset = p.p_part.G.part_start in - g#copy_device_to_device ~srcoffset ~size:copysize "/dev/sda" target + List.iter ( + fun p -> + match p.p_operation with + | OpCopy | OpResize _ -> + let oldsize = p.p_part.G.part_size in + let newsize = + match p.p_operation with OpResize s -> s | _ -> oldsize in + + let copysize = if newsize < oldsize then + newsize else oldsize in + + let source = p.p_name in + let target = sprintf "/dev/sdb%d" p.p_target_partnum in + + if not quiet then + printf (f_"Copying %s ...\n%!") source; + + (match p.p_type with + | ContentUnknown | ContentPV _ | ContentFS _ -> + g#copy_device_to_device ~size:copysize ~sparse source target + | _ -> () + ) + | OpIgnore | OpDelete -> () + ) p.p_partitions + ) | OpIgnore | OpDelete -> () ) partitions; @@ -1240,8 +1398,7 @@ read the man page virt-resize(1). in (* Expand partition content as required. *) - List.iter ( - function + let expand_partition = function | ({ p_operation = OpResize _ } as p) when can_expand_content p.p_type -> let source = p.p_name in @@ -1256,7 +1413,11 @@ read the man page virt-resize(1). do_expand_content target meth | { p_operation = (OpCopy | OpIgnore | OpDelete | OpResize _) } - -> () + -> () in + + List.iter ( + fun p -> expand_partition p; List.iter expand_partition + p.p_partitions ) partitions; (* Expand logical volume content as required. *) -- 1.9.3 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
