[Libguestfs] [RFC PATCH] resize: add support for MBR logical partitions some question

Hu Tao hutao at cn.fujitsu.com
Tue Aug 26 10:16:50 UTC 2014


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 <root at localhost.localdomain>
Date: Mon, 2 Jun 2014 23:30:57 -0400
Subject: [PATCH] resize: add support for MBR logical partitions

Signed-off-by: Hu Tao <hutao at cn.fujitsu.com>
---
 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




More information about the Libguestfs mailing list