X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=resize%2Fresize.ml;h=295d1210b3ed3c5b1a400c7b724417bee0904fe8;hp=dc5540f192261a7293d461f5521861bf064d77f6;hb=5d8e4102b46158f323fb1f6d76a342edcb997b73;hpb=61a42ff551073daf345bd03941797e6af1e97128 diff --git a/resize/resize.ml b/resize/resize.ml index dc5540f..295d121 100644 --- a/resize/resize.ml +++ b/resize/resize.ml @@ -30,7 +30,7 @@ let prog = Filename.basename Sys.executable_name let infile, outfile, copy_boot_loader, debug, deletes, dryrun, expand, expand_content, extra_partition, format, ignores, - lv_expands, output_format, + lv_expands, machine_readable, ntfsresize_force, output_format, quiet, resizes, resizes_force, shrink = let display_version () = let g = new G.guestfs () in @@ -57,6 +57,8 @@ let infile, outfile, copy_boot_loader, debug, deletes, dryrun, let format = ref "" in let ignores = ref [] in let lv_expands = ref [] in + let machine_readable = ref false in + let ntfsresize_force = ref false in let output_format = ref "" in let quiet = ref false in let resizes = ref [] in @@ -72,25 +74,27 @@ let infile, outfile, copy_boot_loader, debug, deletes, dryrun, "--no-copy-boot-loader", Arg.Clear copy_boot_loader, " Don't copy boot loader"; "-d", Arg.Set debug, " Enable debugging messages"; "--debug", Arg.Set debug, " -\"-"; - "--delete", Arg.String (add deletes), "dev Delete partition"; - "--expand", Arg.String set_expand, "dev Expand partition"; + "--delete", Arg.String (add deletes), "part Delete partition"; + "--expand", Arg.String set_expand, "part Expand partition"; "--no-expand-content", Arg.Clear expand_content, " Don't expand content"; "--no-extra-partition", Arg.Clear extra_partition, " Don't create extra partition"; "--format", Arg.Set_string format, "format Format of input disk"; - "--ignore", Arg.String (add ignores), "dev Ignore partition"; + "--ignore", Arg.String (add ignores), "part Ignore partition"; "--lv-expand", Arg.String (add lv_expands), "lv Expand logical volume"; "--LV-expand", Arg.String (add lv_expands), "lv -\"-"; "--lvexpand", Arg.String (add lv_expands), "lv -\"-"; "--LVexpand", Arg.String (add lv_expands), "lv -\"-"; + "--machine-readable", Arg.Set machine_readable, " Make output machine readable"; "-n", Arg.Set dryrun, " Don't perform changes"; "--dryrun", Arg.Set dryrun, " -\"-"; "--dry-run", Arg.Set dryrun, " -\"-"; + "--ntfsresize-force", Arg.Set ntfsresize_force, " Force ntfsresize"; "--output-format", Arg.Set_string format, "format Format of output disk"; "-q", Arg.Set quiet, " Don't print the summary"; "--quiet", Arg.Set quiet, " -\"-"; "--resize", Arg.String (add resizes), "part=size Resize partition"; "--resize-force", Arg.String (add resizes_force), "part=size Forcefully resize partition"; - "--shrink", Arg.String set_shrink, "dev Shrink partition"; + "--shrink", Arg.String set_shrink, "part Shrink partition"; "-V", Arg.Unit display_version, " Display version and exit"; "--version", Arg.Unit display_version, " -\"-"; ] in @@ -123,12 +127,34 @@ read the man page virt-resize(1). let format = match !format with "" -> None | str -> Some str in let ignores = List.rev !ignores in let lv_expands = List.rev !lv_expands in + let machine_readable = !machine_readable in + let ntfsresize_force = !ntfsresize_force in let output_format = match !output_format with "" -> None | str -> Some str in let quiet = !quiet in let resizes = List.rev !resizes in let resizes_force = List.rev !resizes_force in let shrink = match !shrink with "" -> None | str -> Some str in + (* No arguments and machine-readable mode? Print out some facts + * about what this binary supports. We only need to print out new + * things added since this option, or things which depend on features + * of the appliance. + *) + if !disks = [] && machine_readable then ( + printf "virt-resize\n"; + printf "ntfsresize-force\n"; + printf "32bitok\n"; + printf "128-sector-alignment\n"; + let g = new G.guestfs () in + g#add_drive_opts "/dev/null"; + g#launch (); + if feature_available g [| "ntfsprogs"; "ntfs3g" |] then + printf "ntfs\n"; + if feature_available g [| "btrfs" |] then + printf "btrfs\n"; + exit 0 + ); + (* Verify we got exactly 2 disks. *) let infile, outfile = match List.rev !disks with @@ -138,11 +164,12 @@ read the man page virt-resize(1). infile, outfile, copy_boot_loader, debug, deletes, dryrun, expand, expand_content, extra_partition, format, ignores, - lv_expands, output_format, + lv_expands, machine_readable, ntfsresize_force, output_format, quiet, resizes, resizes_force, shrink -(* Default to true, since NTFS support is usually available. *) +(* Default to true, since NTFS and btrfs support are usually available. *) let ntfs_available = ref true +let btrfs_available = ref true (* Add in and out disks to the handle and launch. *) let connect_both_disks () = @@ -150,7 +177,7 @@ let connect_both_disks () = if debug then g#set_trace true; g#add_drive_opts ?format ~readonly:true infile; g#add_drive_opts ?format:output_format ~readonly:false outfile; - if not quiet then Progress.set_up_progress_bar g; + if not quiet then Progress.set_up_progress_bar ~machine_readable g; g#launch (); (* Set the filter to /dev/sda, in case there are any rogue @@ -160,6 +187,7 @@ let connect_both_disks () = (* Update features available in the daemon. *) ntfs_available := feature_available g [|"ntfsprogs"; "ntfs3g"|]; + btrfs_available := feature_available g [|"btrfs"|]; g @@ -212,13 +240,16 @@ let () = (* Build a data structure describing the source disk's partition layout. *) type partition = { p_name : string; (* Device name, like /dev/sda1. *) - p_size : int64; (* Current size of this partition. *) - p_part : G.partition; (* Partition data from libguestfs. *) + p_part : G.partition; (* SOURCE partition data from libguestfs. *) p_bootable : bool; (* Is it bootable? *) p_mbr_id : int option; (* MBR ID, if it has one. *) p_type : partition_content; (* Content type and content size. *) - mutable p_operation : partition_operation; (* What we're going to do. *) - mutable p_target_partnum : int; (* Partition number on target. *) + + (* What we're going to do: *) + mutable p_operation : partition_operation; + p_target_partnum : int; (* TARGET partition number. *) + p_target_start : int64; (* TARGET partition start (sector num). *) + p_target_end : int64; (* TARGET partition end (sector num). *) } and partition_content = | ContentUnknown (* undetermined *) @@ -293,9 +324,10 @@ let partitions : partition list = with G.Error _ -> None in let typ = get_partition_content name in - { p_name = name; p_size = part.G.part_size; p_part = part; + { p_name = name; p_part = part; p_bootable = bootable; p_mbr_id = mbr_id; p_type = typ; - p_operation = OpCopy; p_target_partnum = 0 } + p_operation = OpCopy; p_target_partnum = 0; + p_target_start = 0L; p_target_end = 0L } ) parts in if debug then ( @@ -309,11 +341,13 @@ let partitions : partition list = *) List.iter ( function - | { p_name = name; p_size = size; p_type = ContentPV pv_size } + | { p_name = name; p_part = { G.part_size = size }; + p_type = ContentPV pv_size } when size < pv_size -> error "%s: partition size %Ld < physical volume size %Ld" name size pv_size - | { p_name = name; p_size = size; p_type = ContentFS (_, fs_size) } + | { p_name = name; p_part = { G.part_size = size }; + p_type = ContentFS (_, fs_size) } when size < fs_size -> error "%s: partition size %Ld < filesystem size %Ld" name size fs_size @@ -370,12 +404,14 @@ let lvs = (* These functions tell us if we know how to expand the content of * a particular partition or LV, and what method to use. *) -type expand_content_method = PVResize | Resize2fs | NTFSResize +type expand_content_method = + | PVResize | Resize2fs | NTFSResize | BtrfsFilesystemResize let string_of_expand_content_method = function | PVResize -> "pvresize" | Resize2fs -> "resize2fs" | NTFSResize -> "ntfsresize" + | BtrfsFilesystemResize -> "btrfs-filesystem-resize" let can_expand_content = if expand_content then @@ -384,6 +420,7 @@ let can_expand_content = | ContentPV _ -> true | ContentFS (("ext2"|"ext3"|"ext4"), _) -> true | ContentFS (("ntfs"), _) when !ntfs_available -> true + | ContentFS (("btrfs"), _) when !btrfs_available -> true | ContentFS (_, _) -> false else fun _ -> false @@ -395,6 +432,7 @@ let expand_content_method = | ContentPV _ -> PVResize | ContentFS (("ext2"|"ext3"|"ext4"), _) -> Resize2fs | ContentFS (("ntfs"), _) when !ntfs_available -> NTFSResize + | ContentFS (("btrfs"), _) when !btrfs_available -> BtrfsFilesystemResize | ContentFS (_, _) -> assert false else fun _ -> assert false @@ -454,7 +492,7 @@ let () = *) let mark_partition_for_resize ~option ?(force = false) p newsize = let name = p.p_name in - let oldsize = p.p_size in + let oldsize = p.p_part.G.part_size in (match p.p_operation with | OpResize _ -> @@ -508,7 +546,7 @@ let () = let p = find_partition ~option dev in (* Parse the size field. *) - let oldsize = p.p_size in + let oldsize = p.p_part.G.part_size in let newsize = parse_size oldsize sizefield in if newsize <= 0L then @@ -532,7 +570,7 @@ let calculate_surplus () = let nr_partitions = List.length partitions in let overhead = (Int64.of_int sectsize) *^ ( 2L *^ 64L +^ (* GPT start and end *) - (64L *^ (Int64.of_int (nr_partitions + 1))) (* Maximum alignment *) + (128L *^ (Int64.of_int (nr_partitions + 1))) (* Maximum alignment *) ) +^ (Int64.of_int (max_bootloader - 64 * 512)) in (* Bootloader *) @@ -540,7 +578,7 @@ let calculate_surplus () = fun total p -> let newsize = match p.p_operation with - | OpCopy | OpIgnore -> p.p_size + | OpCopy | OpIgnore -> p.p_part.G.part_size | OpDelete -> 0L | OpResize newsize -> newsize in total +^ newsize @@ -568,7 +606,7 @@ let () = let option = "--expand" in let p = find_partition ~option dev in - let oldsize = p.p_size in + let oldsize = p.p_part.G.part_size in mark_partition_for_resize ~option p (oldsize +^ surplus) ); (match shrink with @@ -579,7 +617,7 @@ let () = let option = "--shrink" in let p = find_partition ~option dev in - let oldsize = p.p_size in + let oldsize = p.p_part.G.part_size in mark_partition_for_resize ~option p (oldsize +^ surplus) ) ) @@ -622,7 +660,7 @@ let () = printf "Summary of changes:\n\n"; List.iter ( - fun ({ p_name = name; p_size = oldsize } as p) -> + fun ({ p_name = name; p_part = { G.part_size = oldsize }} as p) -> let text = match p.p_operation with | OpCopy -> @@ -762,116 +800,116 @@ let () = ) (* Repartition the target disk. *) -let () = - (* The first partition must start at the same position as the old - * first partition. Old virt-resize used to align this to 64 - * sectors, but I suspect this is the cause of boot failures, so - * let's not do this. - *) - let sectsize = Int64.of_int sectsize in - let start = ref ((List.hd partitions).p_part.G.part_start /^ sectsize) in - (* This counts the partition numbers on the target disk. *) - let nextpart = ref 1 in +(* Calculate the location of the partitions on the target disk. This + * also removes from the list any partitions that will be deleted, so + * the final list just contains partitions that need to be created + * on the target. + *) +let partitions = + let sectsize = Int64.of_int sectsize in - let rec repartition = function - | [] -> () + let rec loop partnum start = function | p :: ps -> - let target_partnum = - match p.p_operation with - | OpDelete -> None (* do nothing *) - | OpIgnore | OpCopy -> (* new partition, same size *) - (* Size in sectors. *) - let size = (p.p_size +^ sectsize -^ 1L) /^ sectsize in - Some (add_partition size) - | OpResize newsize -> (* new partition, resized *) - (* Size in sectors. *) - let size = (newsize +^ sectsize -^ 1L) /^ sectsize in - Some (add_partition size) in - - (match target_partnum with - | None -> (* OpDelete *) - () - | Some target_partnum -> (* not OpDelete *) - p.p_target_partnum <- target_partnum; - - (* Set bootable and MBR IDs *) - if p.p_bootable then - g#part_set_bootable "/dev/sdb" target_partnum true; - - (match p.p_mbr_id with - | None -> () - | Some mbr_id -> - g#part_set_mbr_id "/dev/sdb" target_partnum mbr_id - ); - ); - - repartition ps - - (* Add a partition, returns the partition number on the target. *) - and add_partition size (* in SECTORS *) = - let target_partnum, end_ = - if !nextpart <= 3 || parttype <> "msdos" then ( - let target_partnum = !nextpart in - let end_ = !start +^ size -^ 1L in - g#part_add "/dev/sdb" "primary" !start end_; - incr nextpart; - target_partnum, end_ - ) else ( - if !nextpart = 4 then ( - g#part_add "/dev/sdb" "extended" !start (-1L); - incr nextpart; - start := !start +^ 64L - ); - let target_partnum = !nextpart in - let end_ = !start +^ size -^ 1L in - g#part_add "/dev/sdb" "logical" !start end_; - incr nextpart; - target_partnum, end_ - ) in - - (* Start of next partition + alignment to 64 sectors. *) - start := ((end_ +^ 1L) +^ 63L) &^ (~^ 63L); - - target_partnum + (match p.p_operation with + | OpDelete -> loop partnum start ps (* skip p *) + + | OpIgnore | OpCopy -> (* same size *) + (* Size in sectors. *) + let size = (p.p_part.G.part_size +^ sectsize -^ 1L) /^ sectsize in + (* Start of next partition + alignment. *) + let end_ = start +^ size in + let next = (end_ +^ 127L) &^ (~^ 127L) in + + { p with p_target_start = start; p_target_end = end_ -^ 1L; + p_target_partnum = partnum } :: loop (partnum+1) next ps + + | OpResize newsize -> (* resized partition *) + (* New size in sectors. *) + let size = (newsize +^ sectsize -^ 1L) /^ sectsize in + (* Start of next partition + alignment. *) + let next = start +^ size in + let next = (next +^ 127L) &^ (~^ 127L) in + + { p with p_target_start = start; p_target_end = next -^ 1L; + p_target_partnum = partnum } :: loop (partnum+1) next ps + ) + + | [] -> + (* Create the surplus partition if there is room for it. *) + if extra_partition && surplus >= min_extra_partition then ( + [ { + (* Since this partition has no source, this data is + * meaningless and not used since the operation is + * OpIgnore. + *) + p_name = ""; + p_part = { G.part_num = 0l; part_start = 0L; part_end = 0L; + part_size = 0L }; + p_bootable = false; p_mbr_id = None; p_type = ContentUnknown; + + (* Target information is meaningful. *) + p_operation = OpIgnore; + p_target_partnum = partnum; + p_target_start = start; p_target_end = ~^ 64L + } ] + ) + else + [] in - repartition partitions; + (* The first partition must start at the same position as the old + * first partition. Old virt-resize used to align this to 64 + * sectors, but I suspect this is the cause of boot failures, so + * let's not do this. + *) + let start = (List.hd partitions).p_part.G.part_start /^ sectsize in + loop 1 start partitions - (* Create the surplus partition. *) - if extra_partition && surplus >= min_extra_partition then ( - let size = outsize /^ sectsize -^ 64L -^ !start in - ignore (add_partition size) - ) +(* Now partition the target disk. *) +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 + ); + ) partitions (* Copy over the data. *) let () = - let rec copy_data = function - | [] -> () + List.iter ( + fun p -> + match p.p_operation with + | OpCopy | OpResize _ -> + (* XXX Old code had 'when target_partnum > 0', but it appears + * to have served no purpose since the field could never be 0 + * at this point. + *) - | ({ p_name = source; p_target_partnum = target_partnum; - p_operation = (OpCopy | OpResize _) } as p) :: ps - when target_partnum > 0 -> - let oldsize = p.p_size in + 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 target = sprintf "/dev/sdb%d" target_partnum in + let source = p.p_name in + let target = sprintf "/dev/sdb%d" p.p_target_partnum in if not quiet then printf "Copying %s ...\n%!" source; g#copy_size source target copysize; - copy_data ps - - | _ :: ps -> - copy_data ps - in - - copy_data partitions + | _ -> () + ) partitions (* After copying the data over we must shut down and restart the * appliance in order to expand the content. The reason for this may @@ -901,7 +939,7 @@ let g = let g = new G.guestfs () in if debug then g#set_trace true; g#add_drive_opts ?format:output_format ~readonly:false outfile; - if not quiet then Progress.set_up_progress_bar g; + if not quiet then Progress.set_up_progress_bar ~machine_readable g; g#launch (); g (* Return new handle. *) @@ -916,7 +954,15 @@ let () = | Resize2fs -> g#e2fsck_f target; g#resize2fs target - | NTFSResize -> g#ntfsresize target + | NTFSResize -> g#ntfsresize_opts ~force:ntfsresize_force target + | BtrfsFilesystemResize -> + (* Complicated ... Btrfs forces us to mount the filesystem + * in order to resize it. + *) + assert (Array.length (g#mounts ()) = 0); + g#mount_options "" target "/"; + g#btrfs_filesystem_resize "/"; + g#umount "/" in (* Expand partition content as required. *)