resize: Refresh the examples in the documentation.
[libguestfs.git] / tools / virt-resize
index 1c4006a..e89564f 100755 (executable)
@@ -60,7 +60,23 @@ L<virt-list-filesystems(1)> and
 L<virt-df(1)>,
 we recommend you go and read those manual pages first.
 
 L<virt-df(1)>,
 we recommend you go and read those manual pages first.
 
-=head2 BASIC USAGE
+=head2 EXAMPLES
+
+Copy C<olddisk> to C<newdisk>, extending one of the guest's partitions
+to fill the extra 5GB of space.
+
+ virt-list-partitions -lh olddisk
+ # Make a new blank disk which is larger than the old disk file.
+ dd if=/dev/zero of=newdisk bs=1024k count=15000
+ # Note "/dev/sda2" is a partition inside the "olddisk" file.
+ virt-resize --expand /dev/sda2 olddisk newdisk
+
+As above, but make the /boot partition 200MB bigger, while giving the
+remaining space to /dev/sda2:
+
+ virt-resize --resize /dev/sda1=+200M --expand /dev/sda2 olddisk newdisk
+
+=head2 DETAILED USAGE
 
 This describes the common case where you want to expand an image to
 give your guest more space.  Shrinking images is considerably more
 
 This describes the common case where you want to expand an image to
 give your guest more space.  Shrinking images is considerably more
@@ -411,12 +427,23 @@ if ($debug) {
     print "$outfile size $outsize bytes\n";
 }
 
     print "$outfile size $outsize bytes\n";
 }
 
+# In reality the number of sectors containing boot loader data will be
+# less than this (although Windows 7 defaults to putting the first
+# partition on sector 2048, and has quite a large boot loader).
+#
+# However make this large enough to be sure that we have copied over
+# the boot loader.  We could also do this by looking for the sector
+# offset of the first partition.
+#
+# It doesn't matter if we copy too much.
+my $boot_sectors = 4096;
+
 die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n",
         file => $infile, sz => $insize)
 die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n",
         file => $infile, sz => $insize)
-    if $insize < 64 * 512;
+    if $insize < $boot_sectors * 512;
 die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n",
         file => $outfile, sz => $outsize)
 die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n",
         file => $outfile, sz => $outsize)
-    if $outsize < 64 * 512;
+    if $outsize < $boot_sectors * 512;
 
 # Copy the boot loader across.
 do_copy_boot_loader () if $copy_boot_loader;
 
 # Copy the boot loader across.
 do_copy_boot_loader () if $copy_boot_loader;
@@ -426,12 +453,12 @@ sub do_copy_boot_loader
     print "copying boot loader ...\n" if $debug;
     open IFILE, $infile or die "$infile: $!";
     my $s;
     print "copying boot loader ...\n" if $debug;
     open IFILE, $infile or die "$infile: $!";
     my $s;
-    my $r = sysread (IFILE, $s, 64 * 512) or die "$infile: $!";
-    die "$infile: short read" if $r < 64 * 512;
+    my $r = sysread (IFILE, $s, $boot_sectors * 512) or die "$infile: $!";
+    die "$infile: short read" if $r < $boot_sectors * 512;
     open OFILE, "+<$outfile" or die "$outfile: $!";
     sysseek OFILE, 0, SEEK_SET or die "$outfile: seek: $!";
     open OFILE, "+<$outfile" or die "$outfile: $!";
     sysseek OFILE, 0, SEEK_SET or die "$outfile: seek: $!";
-    $r = syswrite (OFILE, $s, 64 * 512) or die "$outfile: $!";
-    die "$outfile: short write" if $r < 64 * 512;
+    $r = syswrite (OFILE, $s, $boot_sectors * 512) or die "$outfile: $!";
+    die "$outfile: short write" if $r < $boot_sectors * 512;
 }
 
 # Add them to the handle and launch the appliance.
 }
 
 # Add them to the handle and launch the appliance.
@@ -466,6 +493,8 @@ sub check_source_disk
 
         my %h = %$_;
         $h{name} = $name;
 
         my %h = %$_;
         $h{name} = $name;
+        $h{bootable} = $g->part_get_bootable ("/dev/sda", $h{part_num});
+        eval { $h{mbr_id} = $g->part_get_mbr_id ("/dev/sda", $h{part_num}); };
         $partitions{$name} = \%h;
     }
 }
         $partitions{$name} = \%h;
     }
 }
@@ -676,7 +705,11 @@ sub calculate_surplus
 
     # We need some overhead for partitioning.  Worst case would be for
     # EFI partitioning + massive per-partition alignment.
 
     # We need some overhead for partitioning.  Worst case would be for
     # EFI partitioning + massive per-partition alignment.
-    my $overhead = $sectsize * (2 * 64 + (64 * (@partitions + 1)) + 128);
+    my $overhead = $sectsize * (
+        2 * 64 +                   # GPT start and end
+        (64 * (@partitions + 1)) + # Maximum alignment
+        ($boot_sectors - 64)       # Boot loader
+        );
 
     my $required = 0;
     foreach (@partitions) {
 
     my $required = 0;
     foreach (@partitions) {
@@ -777,17 +810,36 @@ sub repartition
 
     if ($copy_boot_loader) {
         $parttype = $g->part_get_parttype ("/dev/sdb");
 
     if ($copy_boot_loader) {
         $parttype = $g->part_get_parttype ("/dev/sdb");
-        print "partition table type: $parttype\n" if $debug;
     } else {
     } else {
-        # Didn't copy over the initial boot loader, so we need
-        # to make a new partition type here.
         $parttype = "efi";
     }
         $parttype = "efi";
     }
+    print "partition table type: $parttype\n" if $debug;
+
+    # Delete any existing partitions on the destination disk,
+    # but leave the bootloader that we copied over intact.
+    if ($copy_boot_loader) {
+        # Delete in reverse as an easy way to deal with extended
+        # partitions.
+        foreach (sort { $b cmp $a } $g->list_partitions ()) {
+            if (m{^/dev/.db(\d+)$}) {
+                $g->part_del ("/dev/sdb", $1);
+            }
+        }
+    } else {
+        # Didn't copy over the initial boot loader, so we need
+        # to make a new partition table here.
+        $g->part_init ("/dev/sdb", $parttype);
+    }
+
+    # Work out where to start the first partition.
+    die __"virt-resize: source disk does not have a first partition\n"
+        unless exists ($partitions{"/dev/sda1"});
+    my $start = $partitions{"/dev/sda1"}->{part_start} / $sectsize;
 
 
-    # Delete any existing partitions on the destination disk.
-    $g->part_init ("/dev/sdb", $parttype);
+    # Align to 64.
+    $start = ($start + 63) & ~63;
 
 
-    my $start = 64;
+    print "starting to partition from $start\n" if $debug;
 
     # Create the new partitions.
     foreach my $part (@partitions) {
 
     # Create the new partitions.
     foreach my $part (@partitions) {
@@ -803,9 +855,18 @@ sub repartition
             }
 
             # Create it.
             }
 
             # Create it.
-            my ($target, $end) = add_partition ($start, $size);
+            my ($target, $end, $part_num) = add_partition ($start, $size);
             $partitions{$part}->{target} = $target;
 
             $partitions{$part}->{target} = $target;
 
+            if ($partitions{$part}->{bootable}) {
+                $g->part_set_bootable ("/dev/sdb", $part_num, 1);
+            }
+
+            if ($partitions{$part}->{mbr_id}) {
+                $g->part_set_mbr_id ("/dev/sdb", $part_num,
+                                     $partitions{$part}->{mbr_id});
+            }
+
             # Start of next partition + alignment.
             $start = $end + 1;
             $start = ($start + 63) & ~63;
             # Start of next partition + alignment.
             $start = $end + 1;
             $start = ($start + 63) & ~63;
@@ -825,26 +886,26 @@ sub add_partition
     my $start = shift;
     my $size = shift;
 
     my $start = shift;
     my $size = shift;
 
-    my ($target, $end);
+    my ($target, $end, $part_num);
 
     if ($nextpart <= 3 || $parttype ne "msdos") {
         $target = "/dev/sdb$nextpart";
         $end = $start + $size - 1;
         $g->part_add ("/dev/sdb", "primary", $start, $end);
 
     if ($nextpart <= 3 || $parttype ne "msdos") {
         $target = "/dev/sdb$nextpart";
         $end = $start + $size - 1;
         $g->part_add ("/dev/sdb", "primary", $start, $end);
-        $nextpart++;
+        $part_num = $nextpart++;
     } else {
         if ($nextpart == 4) {
             $g->part_add ("/dev/sdb", "extended", $start, -1);
     } else {
         if ($nextpart == 4) {
             $g->part_add ("/dev/sdb", "extended", $start, -1);
-            $nextpart++;
+            $part_num = $nextpart++;
             $start += 64;
         }
         $target = "/dev/sdb$nextpart";
         $end = $start + $size - 1;
         $g->part_add ("/dev/sdb", "logical", $start, $end);
             $start += 64;
         }
         $target = "/dev/sdb$nextpart";
         $end = $start + $size - 1;
         $g->part_add ("/dev/sdb", "logical", $start, $end);
-        $nextpart++;
+        $part_num = $nextpart++;
     }
 
     }
 
-    return ($target, $end);
+    return ($target, $end, $part_num);
 }
 
 # Copy over the data.
 }
 
 # Copy over the data.
@@ -881,6 +942,11 @@ sub copy_data
     }
 }
 
     }
 }
 
+# Sync disk and exit.
+$g->umount_all ();
+$g->sync ();
+undef $g;
+
 exit 0;
 
 sub sizebytes
 exit 0;
 
 sub sizebytes
@@ -945,6 +1011,22 @@ sub canonicalize
     $_;
 }
 
     $_;
 }
 
+=head1 ALTERNATIVE TOOLS
+
+There are several proprietary tools for resizing partitions.  We
+won't mention any here.
+
+L<parted(8)> and its graphical shell gparted can do some types of
+resizing operations on disk images.  They can resize and move
+partitions, but I don't think they can do anything with the contents,
+and they certainly don't understand LVM.
+
+L<guestfish(1)> can do everything that virt-resize can do and a lot
+more, but at a much lower level.  You will probably end up
+hand-calculating sector offsets, which is something that virt-resize
+was designed to avoid.  If you want to see the guestfish-equivalent
+commands that virt-resize runs, use the C<--debug> flag.
+
 =head1 SEE ALSO
 
 L<virt-list-partitions(1)>,
 =head1 SEE ALSO
 
 L<virt-list-partitions(1)>,
@@ -957,12 +1039,13 @@ L<pvresize(8)>,
 L<lvresize(8)>,
 L<resize2fs(8)>,
 L<virsh(1)>,
 L<lvresize(8)>,
 L<resize2fs(8)>,
 L<virsh(1)>,
+L<parted(8)>,
 L<Sys::Guestfs(3)>,
 L<http://libguestfs.org/>.
 
 =head1 AUTHOR
 
 L<Sys::Guestfs(3)>,
 L<http://libguestfs.org/>.
 
 =head1 AUTHOR
 
-Richard W.M. Jones L<http://et.redhat.com/~rjones/>
+Richard W.M. Jones L<http://people.redhat.com/~rjones/>
 
 =head1 COPYRIGHT
 
 
 =head1 COPYRIGHT