mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
virt-resize: Handle extended and logical partitions (RHBZ#642821).
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user