virt-resize: Handle extended and logical partitions (RHBZ#642821).

This commit is contained in:
Richard W.M. Jones
2011-10-25 19:03:38 +01:00
parent f4b3351692
commit 119e67a92e
2 changed files with 89 additions and 16 deletions

View File

@@ -271,7 +271,13 @@ let parttype, parttype_string =
| _ ->
error "%s: unknown partition table type\nvirt-resize only supports MBR (DOS) and GPT partition tables." infile
(* Build a data structure describing the source disk's partition layout. *)
(* Build a data structure describing the source disk's partition layout.
*
* NOTE: For MBR, only primary/extended partitions are tracked here.
* Logical partitions are contained within an extended partition, and
* we don't track them (they are just copied within the extended
* partition). For the same reason we cannot resize logical partitions.
*)
type partition = {
p_name : string; (* Device name, like /dev/sda1. *)
p_part : G.partition; (* SOURCE partition data from libguestfs. *)
@@ -289,6 +295,7 @@ and partition_content =
| ContentUnknown (* undetermined *)
| ContentPV of int64 (* physical volume (size of PV) *)
| ContentFS of string * int64 (* mountable filesystem (FS type, FS size) *)
| ContentExtendedPartition (* MBR extended partition *)
and partition_operation =
| OpCopy (* copy it as-is, no resizing *)
| OpIgnore (* ignore it (create on target, but don't
@@ -309,10 +316,12 @@ and string_of_partition_content = function
| ContentUnknown -> "unknown data"
| ContentPV sz -> sprintf "LVM PV (%Ld bytes)" sz
| ContentFS (fs, sz) -> sprintf "filesystem %s (%Ld bytes)" fs sz
| ContentExtendedPartition -> "extended partition"
and string_of_partition_content_no_size = function
| ContentUnknown -> "unknown data"
| ContentPV _ -> sprintf "LVM PV"
| ContentFS (fs, _) -> sprintf "filesystem %s" fs
| ContentExtendedPartition -> "extended partition"
let get_partition_content =
let pvs_full = Array.to_list (g#pvs_full ()) in
@@ -341,12 +350,26 @@ let get_partition_content =
with
G.Error _ -> ContentUnknown
let is_extended_partition = function
| Some (0x05|0x0f) -> true
| _ -> false
let partitions : partition list =
let parts = Array.to_list (g#part_list "/dev/sda") in
if List.length parts = 0 then
error "the source disk has no partitions";
(* Filter out logical partitions. See note above. *)
let parts =
match parttype with
| GPT -> parts
| MBR ->
List.filter (function
| { G.part_num = part_num } when part_num >= 5_l -> false
| _ -> true
) parts in
let partitions =
List.map (
fun ({ G.part_num = part_num } as part) ->
@@ -356,7 +379,9 @@ let partitions : partition list =
let mbr_id =
try Some (g#part_get_mbr_id "/dev/sda" part_num)
with G.Error _ -> None in
let typ = get_partition_content name in
let typ =
if is_extended_partition mbr_id then ContentExtendedPartition
else get_partition_content name in
{ p_name = name; p_part = part;
p_bootable = bootable; p_mbr_id = mbr_id; p_type = typ;
@@ -408,7 +433,8 @@ type logvol = {
lv_type : logvol_content;
mutable lv_operation : logvol_operation
}
and logvol_content = partition_content (* except ContentPV cannot occur *)
(* ContentPV, ContentExtendedPartition cannot occur here *)
and logvol_content = partition_content
and logvol_operation =
| LVOpNone (* nothing *)
| LVOpExpand (* expand it *)
@@ -423,7 +449,10 @@ let lvs =
let lvs = List.map (
fun name ->
let typ = get_partition_content name in
assert (match typ with ContentPV _ -> false | _ -> true);
assert (
match typ with ContentPV _ | ContentExtendedPartition -> false
| _ -> true
);
{ lv_name = name; lv_type = typ; lv_operation = LVOpNone }
) lvs in
@@ -456,6 +485,7 @@ let can_expand_content =
| ContentFS (("ntfs"), _) when !ntfs_available -> true
| ContentFS (("btrfs"), _) when !btrfs_available -> true
| ContentFS (_, _) -> false
| ContentExtendedPartition -> false
else
fun _ -> false
@@ -468,6 +498,7 @@ let expand_content_method =
| ContentFS (("ntfs"), _) when !ntfs_available -> NTFSResize
| ContentFS (("btrfs"), _) when !btrfs_available -> BtrfsFilesystemResize
| ContentFS (_, _) -> assert false
| ContentExtendedPartition -> assert false
else
fun _ -> assert false
@@ -559,6 +590,9 @@ let mark_partition_for_resize ~option ?(force = false) p newsize =
error "%s: This partition has contains a %s filesystem which will be damaged by shrinking it below %Ld bytes (user asked to shrink it to %Ld bytes). If you want to shrink this partition, you need to use the '--resize-force' option, but that could destroy any data on this partition. (This error came from '%s' option on the command line.)"
name fstype size newsize option
| ContentFS _ -> ()
| ContentExtendedPartition ->
error "%s: This extended partition contains logical partitions which might be damaged by shrinking it. If you want to shrink this partition, you need to use the '--resize-force' option, but that could destroy logical partitions within this partition. (This error came from '%s' option on the command line.)"
name option
);
p.p_operation <- OpResize newsize
@@ -926,17 +960,7 @@ let partitions =
let () =
List.iter (
fun p ->
g#part_add "/dev/sdb" "primary" p.p_target_start p.p_target_end;
(* Set bootable and MBR IDs *)
if p.p_bootable then
g#part_set_bootable "/dev/sdb" p.p_target_partnum true;
(match p.p_mbr_id with
| None -> ()
| Some mbr_id ->
g#part_set_mbr_id "/dev/sdb" p.p_target_partnum mbr_id
);
g#part_add "/dev/sdb" "primary" p.p_target_start p.p_target_end
) partitions
(* Copy over the data. *)
@@ -962,11 +986,39 @@ let () =
if not quiet then
printf "Copying %s ...\n%!" source;
g#copy_size source target copysize;
(match p.p_type with
| ContentUnknown | ContentPV _ | ContentFS _ ->
g#copy_device_to_device ~size:copysize 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
)
| _ -> ()
) partitions
(* Set bootable and MBR IDs. Do this *after* copying over the data,
* so that we can magically change the primary partition to an extended
* partition if necessary.
*)
let () =
List.iter (
fun p ->
if p.p_bootable then
g#part_set_bootable "/dev/sdb" p.p_target_partnum true;
(match p.p_mbr_id with
| None -> ()
| Some mbr_id ->
g#part_set_mbr_id "/dev/sdb" p.p_target_partnum mbr_id
);
) partitions
(* Fix the bootloader if we aligned the first partition. *)
let () =
if align_first_partition_and_fix_bootloader then (

View File

@@ -238,6 +238,27 @@ Similarly, to get non-sparse raw output use:
(on older systems that don't have the L<fallocate(1)> command use
C<dd if=/dev/zero of=outdisk bs=1M count=..>)
=head2 LOGICAL PARTITIONS
Logical partitions (a.k.a. C</dev/sda5+> on disks using DOS partition
tables) cannot be resized.
To understand what is going on, firstly one of the four partitions
C</dev/sda1-4> will have MBR partition type C<05> or C<0f>. This is
called the B<extended partition>. Use L<virt-filesystems(1)> to see
the MBR partition type.
Logical partitions live inside the extended partition.
The extended partition can be expanded, but not shrunk (unless you
force it, which is not advisable). When the extended partition is
copied across, all the logical partitions contained inside are copied
over implicitly. Virt-resize does not look inside the extended
partition, so it copies the logical partitions blindly.
You cannot specify a logical partition (C</dev/sda5+>) at all on the
command line. Doing so will give an error.
=head1 OPTIONS
=over 4