inspect: Add support for Linux Mint and Mandriva.
[libguestfs.git] / tools / virt-resize
index 5ced4dd..8a473ca 100755 (executable)
@@ -33,6 +33,8 @@ $Data::Dumper::Sortkeys = 1;
 die __"virt-resize: sorry this program does not work on a 32 bit host\n"
     if ~1 == 4294967294;
 
 die __"virt-resize: sorry this program does not work on a 32 bit host\n"
     if ~1 == 4294967294;
 
+$| = 1;
+
 =encoding utf8
 
 =head1 NAME
 =encoding utf8
 
 =head1 NAME
@@ -61,7 +63,27 @@ 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.
 
-=head1 BASIC USAGE
+=head1 EXAMPLES
+
+Copy C<olddisk> to C<newdisk>, extending one of the guest's partitions
+to fill the extra 5GB of space.
+
+ truncate -r olddisk newdisk; truncate -s +5G newdisk
+ virt-list-partitions -lht olddisk
+ # 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
+
+As above, but the output format will be uncompressed qcow2:
+
+ qemu-img create -f qcow2 newdisk.qcow2 15G
+ virt-resize --expand /dev/sda2 olddisk newdisk.qcow2
+
+=head1 DETAILED USAGE
 
 =head2 EXPANDING A VIRTUAL MACHINE DISK
 
 
 =head2 EXPANDING A VIRTUAL MACHINE DISK
 
@@ -213,6 +235,25 @@ contents of a partition.  Deleting a partition removes it completely,
 but note that it also renumbers any partitions after the one which is
 deleted, which can leave some guests unbootable.
 
 but note that it also renumbers any partitions after the one which is
 deleted, which can leave some guests unbootable.
 
+=head2 QCOW2 AND NON-SPARSE RAW FORMATS
+
+If the input disk is in qcow2 format, then you may prefer that the
+output is in qcow2 format as well.  Alternately, virt-resize can
+convert the format on the fly.  The output format is simply determined
+by the format of the empty output container that you provide.  Thus to
+create qcow2 output, use:
+
+ qemu-img create [-c] -f qcow2 outdisk [size]
+
+instead of the truncate command (use C<-c> for a compressed disk).
+
+Similarly, to get non-sparse raw output use:
+
+ fallocate -l size outdisk
+
+(on older systems that don't have the L<fallocate(1)> command use
+C<dd if=/dev/zero of=outdisk bs=1M count=..>)
+
 =head1 OPTIONS
 
 =over 4
 =head1 OPTIONS
 
 =over 4
@@ -383,6 +424,34 @@ You can give this option multiple times.
 
 =cut
 
 
 =cut
 
+my @lv_expand;
+
+=item B<--LV-expand logvol>
+
+This takes the logical volume and, as a final step, expands it to fill
+all the space available in its volume group.  A typical usage,
+assuming a Linux guest with a single PV C</dev/sda2> and a root device
+called C</dev/vg_guest/lv_root> would be:
+
+ virt-resize indisk outdisk \
+   --expand /dev/sda2 --LV-expand /dev/vg_guest/lv_root
+
+This would first expand the partition (and PV), and then expand the
+root device to fill the extra space in the PV.
+
+The contents of the LV are also resized if virt-resize knows how to do
+that.  You can stop virt-resize from trying to expand the content by
+using the option C<--no-expand-content>.
+
+Use L<virt-list-filesystems(1)> to list the filesystems in
+the guest.
+
+You can give this option multiple times, I<but> it doesn't
+make sense to do this unless the logical volumes you specify
+are all in different volume groups.
+
+=cut
+
 my $copy_boot_loader = 1;
 
 =item B<--no-copy-boot-loader>
 my $copy_boot_loader = 1;
 
 =item B<--no-copy-boot-loader>
@@ -447,6 +516,36 @@ my $quiet;
 
 Don't print the summary.
 
 
 Don't print the summary.
 
+=cut
+
+my $format;
+
+=item B<--format> raw
+
+Specify the format of the input disk image.  If this flag is not
+given then it is auto-detected from the image itself.
+
+If working with untrusted raw-format guest disk images, you should
+ensure the format is always specified.
+
+Note that this option I<does not> affect the output format.
+See L</QCOW2 AND NON-SPARSE RAW FORMATS>.
+
+=cut
+
+my $output_format;
+
+=item B<--output-format> raw
+
+Specify the format of the output disk image.  If this flag is not
+given then it is auto-detected from the image itself.
+
+If working with untrusted raw-format guest disk images, you should
+ensure the format is always specified.
+
+Note that you still need to create the output disk with the right
+format.  See L</QCOW2 AND NON-SPARSE RAW FORMATS>.
+
 =back
 
 =cut
 =back
 
 =cut
@@ -459,12 +558,15 @@ GetOptions ("help|?" => \$help,
             "shrink=s" => \$shrink,
             "ignore=s" => \@ignore,
             "delete=s" => \@delete,
             "shrink=s" => \$shrink,
             "ignore=s" => \@ignore,
             "delete=s" => \@delete,
+            "lv-expand=s" => \@lv_expand,
             "copy-boot-loader!" => \$copy_boot_loader,
             "extra-partition!" => \$extra_partition,
             "expand-content!" => \$expand_content,
             "d|debug" => \$debug,
             "n|dryrun|dry-run" => \$dryrun,
             "q|quiet" => \$quiet,
             "copy-boot-loader!" => \$copy_boot_loader,
             "extra-partition!" => \$extra_partition,
             "expand-content!" => \$expand_content,
             "d|debug" => \$debug,
             "n|dryrun|dry-run" => \$dryrun,
             "q|quiet" => \$quiet,
+            "format=s" => \$format,
+            "output-format=s" => \$output_format,
     ) or pod2usage (2);
 pod2usage (1) if $help;
 if ($version) {
     ) or pod2usage (2);
 pod2usage (1) if $help;
 if ($version) {
@@ -484,17 +586,71 @@ die __x("virt-resize: {file}: does not exist or is not readable\n", file => $inf
 die __x("virt-resize: {file}: does not exist or is not writable\nYou have to create the destination disk before running this program.\nPlease read the virt-resize(1) manpage for more information.\n", file => $outfile)
     unless -w $outfile;
 
 die __x("virt-resize: {file}: does not exist or is not writable\nYou have to create the destination disk before running this program.\nPlease read the virt-resize(1) manpage for more information.\n", file => $outfile)
     unless -w $outfile;
 
-my @s;
-@s = stat $infile;
-my $insize = S_ISREG ($s[2]) ? $s[7] : host_blockdevsize ($infile);
-@s = stat $outfile;
-my $outsize = S_ISREG ($s[2]) ? $s[7] : host_blockdevsize ($outfile);
+# Add them to the handle and launch the appliance.
+my $g;
+launch_guestfs ();
+
+sub launch_guestfs
+{
+    $g = Sys::Guestfs->new ();
+    $g->set_trace (1) if $debug;
+    my @args = ($infile);
+    push @args, readonly => 1;
+    push @args, format => $format if defined $format;
+    $g->add_drive_opts (@args);
+    @args = ($outfile);
+    push @args, format => $output_format if defined $output_format;
+    $g->add_drive_opts (@args);
+    $g->set_progress_callback (\&progress_callback) unless $quiet;
+    $g->launch ();
+}
+
+my $sectsize = $g->blockdev_getss ("/dev/sdb");
+
+# Get the size in bytes of each disk.
+#
+# Originally we computed this by looking at the same of the host file,
+# but of course this failed for qcow2 images (RHBZ#633096).  The right
+# way to do it is with $g->blockdev_getsize64.
+my $insize = $g->blockdev_getsize64 ("/dev/sda");
+my $outsize = $g->blockdev_getsize64 ("/dev/sdb");
 
 if ($debug) {
     print "$infile size $insize bytes\n";
     print "$outfile size $outsize bytes\n";
 }
 
 
 if ($debug) {
     print "$infile size $insize bytes\n";
     print "$outfile size $outsize bytes\n";
 }
 
+# Create a partition table.
+#
+# We *must* do this before copying the bootloader across, and copying
+# the bootloader must be careful not to disturb this partition table
+# (RHBZ#633766).  There are two reasons for this:
+#
+# (1) The 'parted' library is stupid and broken.  In many ways.  In
+# this particular instance the stupid and broken bit is that it
+# overwrites the whole boot sector when initializating a partition
+# table.  (Upstream don't consider this obvious problem to be a bug).
+#
+# (2) GPT has a backup partition table located at the end of the disk.
+# It's non-movable, because the primary GPT contains fixed references
+# to both the size of the disk and the backup partition table at the
+# end.  This would be a problem for any resize that didn't either
+# carefully move the backup GPT (and rewrite those references) or
+# recreate the whole partition table from scratch.
+
+my $parttype;
+create_partition_table ();
+
+sub create_partition_table
+{
+    local $_;
+
+    $parttype = $g->part_get_parttype ("/dev/sda");
+    print "partition table type: $parttype\n" if $debug;
+
+    $g->part_init ("/dev/sdb", $parttype);
+}
+
 # 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).
 # 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).
@@ -504,14 +660,14 @@ if ($debug) {
 # offset of the first partition.
 #
 # It doesn't matter if we copy too much.
 # offset of the first partition.
 #
 # It doesn't matter if we copy too much.
-my $boot_sectors = 4096;
+my $max_bootloader = 4096 * 512;
 
 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 < $boot_sectors * 512;
+    if $insize < $max_bootloader;
 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 < $boot_sectors * 512;
+    if $outsize < $max_bootloader;
 
 # 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;
@@ -519,30 +675,30 @@ do_copy_boot_loader () if $copy_boot_loader;
 sub do_copy_boot_loader
 {
     print "copying boot loader ...\n" if $debug;
 sub do_copy_boot_loader
 {
     print "copying boot loader ...\n" if $debug;
-    open IFILE, $infile or die "$infile: $!";
-    my $s;
-    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: $!";
-    $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.
-my $g;
-launch_guestfs ();
+    # Don't disturb the partition table that we just wrote.
+    # https://secure.wikimedia.org/wikipedia/en/wiki/Master_Boot_Record
+    # https://secure.wikimedia.org/wikipedia/en/wiki/GUID_Partition_Table
 
 
-sub launch_guestfs
-{
-    $g = Sys::Guestfs->new ();
-    $g->set_trace (1) if $debug;
-    $g->add_drive_ro ($infile);
-    $g->add_drive ($outfile);
-    $g->launch ();
+    my $bootsect = $g->pread_device ("/dev/sda", 446, 0);
+    die __"virt-resize: short read" if length ($bootsect) < 446;
+
+    $g->pwrite_device ("/dev/sdb", $bootsect, 0);
+
+    my $start = 512;
+    if ($parttype eq "gpt") {
+        # XXX With 4K sectors does GPT just fit more entries in a
+        # sector, or does it always use 34 sectors?
+        $start = 17408;
+    }
+
+    my $loader = $g->pread_device ("/dev/sda", $max_bootloader, $start);
+    die __"virt-resize: short read" if length ($loader) < $max_bootloader;
+
+    $g->pwrite_device ("/dev/sdb", $loader, $start);
 }
 
 }
 
-my $sectsize = $g->blockdev_getss ("/dev/sdb");
+my $to_be_expanded = 0;
 
 # Get the partitions on the source disk.
 my @partitions;
 
 # Get the partitions on the source disk.
 my @partitions;
@@ -615,7 +771,7 @@ sub examine_partition
         if ($type eq "LVM2_member") {
             $partitions{$part}->{can_expand_content} = 1;
             $partitions{$part}->{expand_content_method} = "pvresize";
         if ($type eq "LVM2_member") {
             $partitions{$part}->{can_expand_content} = 1;
             $partitions{$part}->{expand_content_method} = "pvresize";
-        } elsif ($type =~ /^ext[234]/) {
+        } elsif ($type =~ /^ext[234]$/) {
             $partitions{$part}->{can_expand_content} = 1;
             $partitions{$part}->{expand_content_method} = "resize2fs";
         } elsif ($type eq "ntfs" && feature_available ($g, "ntfsprogs")) {
             $partitions{$part}->{can_expand_content} = 1;
             $partitions{$part}->{expand_content_method} = "resize2fs";
         } elsif ($type eq "ntfs" && feature_available ($g, "ntfsprogs")) {
@@ -638,6 +794,50 @@ if ($debug) {
     }
 }
 
     }
 }
 
+# Examine the LVs (for --lv-expand option).
+my @lvs = $g->lvs ();
+my %lvs;
+examine_lv ($_) foreach @lvs;
+mark_lvs_to_expand ();
+
+sub examine_lv
+{
+    local $_ = shift;
+
+    $lvs{$_}->{name} = $_;
+
+    my $type = "unknown";
+    eval {
+        $type = $g->vfs_type ($_);
+    };
+    $lvs{$_}->{type} = $type;
+
+    if ($expand_content) {
+        if ($type =~ /^ext[234]$/) {
+            $lvs{$_}->{can_expand_content} = 1;
+            $lvs{$_}->{expand_content_method} = "resize2fs";
+        } elsif ($type eq "ntfs" && feature_available ($g, "ntfsprogs")) {
+            $lvs{$_}->{can_expand_content} = 1;
+            $lvs{$_}->{expand_content_method} = "ntfsresize";
+        }
+    }
+}
+
+sub mark_lvs_to_expand {
+    local $_;
+
+    foreach (@lv_expand) {
+        die __x("virt-resize: no logical volume called {n}\n",
+                n => $_)
+            unless exists $lvs{$_};
+
+        if ($lvs{$_}->{can_expand_content}) {
+            $lvs{$_}->{will_expand_content} = 1;
+            $to_be_expanded++;
+        }
+    }
+}
+
 sub find_partition
 {
     local $_ = shift;
 sub find_partition
 {
     local $_ = shift;
@@ -685,8 +885,6 @@ sub do_delete
 }
 
 # Handle --resize and --resize-force.
 }
 
 # Handle --resize and --resize-force.
-my $to_be_expanded = 0;
-
 do_resize ($_, 0, "--resize") foreach @resize;
 do_resize ($_, 1, "--resize-force") foreach @resize_force;
 
 do_resize ($_, 0, "--resize") foreach @resize;
 do_resize ($_, 1, "--resize-force") foreach @resize_force;
 
@@ -797,9 +995,9 @@ sub calculate_surplus
     # EFI partitioning + massive per-partition alignment.
     my $overhead = $sectsize * (
         2 * 64 +                   # GPT start and end
     # EFI partitioning + massive per-partition alignment.
     my $overhead = $sectsize * (
         2 * 64 +                   # GPT start and end
-        (64 * (@partitions + 1)) + # Maximum alignment
-        ($boot_sectors - 64)       # Boot loader
-        );
+        (64 * (@partitions + 1))   # Maximum alignment
+        ) +
+        ($max_bootloader - 64 * 512); # boot loader
 
     my $required = 0;
     foreach (@partitions) {
 
     my $required = 0;
     foreach (@partitions) {
@@ -871,6 +1069,19 @@ sub print_summary
         }
     }
 
         }
     }
 
+    foreach my $lv (@lv_expand) {
+        print __x("{n}: LV will be expanded to maximum size\n",
+                  n => $lv);
+    }
+
+    foreach my $lv (@lvs) {
+        if ($lvs{$lv}->{will_expand_content}) {
+            print __x("{n}: content will be expanded using the '{meth}' method\n",
+                      n => $lv,
+                      meth => $lvs{$lv}->{expand_content_method});
+        }
+    }
+
     if ($surplus > 0) {
         print __x("There is a surplus of {spl} bytes ({h}).\n",
                   spl => $surplus,
     if ($surplus > 0) {
         print __x("There is a surplus of {spl} bytes ({h}).\n",
                   spl => $surplus,
@@ -895,36 +1106,12 @@ exit 0 if $dryrun;
 
 # Repartition the target disk.
 my $nextpart = 1;
 
 # Repartition the target disk.
 my $nextpart = 1;
-my $parttype;
 repartition ();
 
 sub repartition
 {
     local $_;
 
 repartition ();
 
 sub repartition
 {
     local $_;
 
-    if ($copy_boot_loader) {
-        $parttype = $g->part_get_parttype ("/dev/sdb");
-    } else {
-        $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"});
     # 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"});
@@ -1021,16 +1208,11 @@ sub copy_data
                 }
 
                 if (!$quiet && !$debug) {
                 }
 
                 if (!$quiet && !$debug) {
-                    local $| = 1;
-                    print __x("Copying {p} ...", p => $part);
+                    print __x("Copying {p} ...\n", p => $part);
                 }
 
                 $g->copy_size ($part, $target,
                                $newsize < $oldsize ? $newsize : $oldsize);
                 }
 
                 $g->copy_size ($part, $target,
                                $newsize < $oldsize ? $newsize : $oldsize);
-
-                if (!$quiet && !$debug) {
-                    print " ", __"done", "\n";
-                }
             }
         }
     }
             }
         }
     }
@@ -1046,6 +1228,8 @@ sub copy_data
 if ($to_be_expanded > 0) {
     restart_appliance ();
     expand_partitions ();
 if ($to_be_expanded > 0) {
     restart_appliance ();
     expand_partitions ();
+    expand_lvs ();
+    expand_lvs_content ();
 }
 
 sub restart_appliance
 }
 
 sub restart_appliance
@@ -1057,7 +1241,9 @@ sub restart_appliance
 
     $g = Sys::Guestfs->new ();
     $g->set_trace (1) if $debug;
 
     $g = Sys::Guestfs->new ();
     $g->set_trace (1) if $debug;
-    $g->add_drive ($outfile);
+    my @args = ($outfile);
+    push @args, format => $output_format if defined $output_format;
+    $g->add_drive_opts (@args);
     $g->launch ();
 
     # Target partitions have changed from /dev/sdb to /dev/sda,
     $g->launch ();
 
     # Target partitions have changed from /dev/sdb to /dev/sda,
@@ -1085,7 +1271,7 @@ sub expand_partitions
                 # Expand if requested.
                 if ($partitions{$part}->{will_expand_content}) {
                     if (!$quiet && !$debug) {
                 # Expand if requested.
                 if ($partitions{$part}->{will_expand_content}) {
                     if (!$quiet && !$debug) {
-                        print __x("Expanding {p} using the '{meth}' method",
+                        print __x("Expanding {p} using the '{meth}' method\n",
                                   p => $part,
                                   meth => $partitions{$part}->{expand_content_method});
                     }
                                   p => $part,
                                   meth => $partitions{$part}->{expand_content_method});
                     }
@@ -1126,6 +1312,38 @@ sub expand_target_partition
     }
 }
 
     }
 }
 
+sub expand_lvs
+{
+    local $_;
+
+    foreach (@lv_expand) {
+        $g->lvresize_free ($_, 100);
+    }
+}
+
+sub expand_lvs_content
+{
+    local $_;
+
+    foreach (@lvs) {
+        if ($lvs{$_}->{will_expand_content}) {
+            my $method = $lvs{$_}->{expand_content_method};
+            if (!$quiet && !$debug) {
+                print __x("Expanding {p} using the '{meth}' method\n",
+                          p => $_, meth => $method);
+                    }
+            if ($method eq "resize2fs") {
+                $g->e2fsck_f ($_);
+                $g->resize2fs ($_);
+            } elsif ($method eq "ntfsresize") {
+                $g->ntfsresize ($_);
+            } else {
+                die "internal error: unknown method: $method";
+            }
+        }
+    }
+}
+
 # Sync disk and exit.
 $g->umount_all ();
 $g->sync ();
 # Sync disk and exit.
 $g->umount_all ();
 $g->sync ();
@@ -1170,19 +1388,6 @@ sub human_size
     }
 }
 
     }
 }
 
-# Return the size in bytes of a HOST block device.
-sub host_blockdevsize
-{
-    local $_;
-    my $dev = shift;
-
-    open BD, "PATH=/usr/sbin:/sbin:\$PATH blockdev --getsize64 $dev |"
-        or die "blockdev: $!";
-    $_ = <BD>;
-    chomp $_;
-    $_;
-}
-
 # The reverse of device name translation, see
 # BLOCK DEVICE NAMING in guestfs(3).
 sub canonicalize
 # The reverse of device name translation, see
 # BLOCK DEVICE NAMING in guestfs(3).
 sub canonicalize
@@ -1195,6 +1400,25 @@ sub canonicalize
     $_;
 }
 
     $_;
 }
 
+# Not as sophisticated as the guestfish progress bar, because
+# I intend to use an external library for this at some point (XXX).
+sub progress_callback
+{
+    my $proc_nr = shift;
+    my $serial = shift;
+    my $position = shift;
+    my $total = shift;
+
+    my $ratio = $position / $total;
+    if ($ratio < 0) { $ratio = 0 }
+    elsif ($ratio > 1) { $ratio = 1 }
+
+    my $dots = int ($ratio * 76);
+
+    print "[", "#"x$dots, "-"x(76-$dots), "]\r";
+    print "\n" if $ratio == 1;
+}
+
 =head1 NOTES
 
 =head2 "Partition 1 does not end on cylinder boundary."
 =head1 NOTES
 
 =head2 "Partition 1 does not end on cylinder boundary."
@@ -1219,6 +1443,23 @@ Windows may initiate a lengthy "chkdsk" on first boot after a resize,
 if NTFS partitions have been expanded.  This is just a safety check
 and (unless it find errors) is nothing to worry about.
 
 if NTFS partitions have been expanded.  This is just a safety check
 and (unless it find errors) is nothing to worry about.
 
+=head2 GUEST BOOT STUCK AT "GRUB"
+
+If a Linux guest does not boot after resizing, and the boot is stuck
+after printing C<GRUB> on the console, try reinstalling grub.  This
+sometimes happens on older (RHEL 5-era) guests, for reasons we don't
+fully understand, although we think is to do with partition alignment.
+
+ guestfish -i -a newdisk
+ ><fs> cat /boot/grub/device.map
+ # check the contents of this file are sensible or
+ # edit the file if necessary
+ ><fs> grub-install / /dev/vda
+ ><fs> exit
+
+For more flexible guest reconfiguration, including if you need to
+specify other parameters to grub-install, use L<virt-rescue(1)>.
+
 =head1 ALTERNATIVE TOOLS
 
 There are several proprietary tools for resizing partitions.  We
 =head1 ALTERNATIVE TOOLS
 
 There are several proprietary tools for resizing partitions.  We
@@ -1235,6 +1476,13 @@ 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.
 
 was designed to avoid.  If you want to see the guestfish-equivalent
 commands that virt-resize runs, use the C<--debug> flag.
 
+=head1 SHELL QUOTING
+
+Libvirt guest names can contain arbitrary characters, some of which
+have meaning to the shell such as C<#> and space.  You may need to
+quote or escape these characters on the command line.  See the shell
+manual page L<sh(1)> for details.
+
 =head1 SEE ALSO
 
 L<virt-list-partitions(1)>,
 =head1 SEE ALSO
 
 L<virt-list-partitions(1)>,
@@ -1249,6 +1497,11 @@ L<resize2fs(8)>,
 L<ntfsresize(8)>,
 L<virsh(1)>,
 L<parted(8)>,
 L<ntfsresize(8)>,
 L<virsh(1)>,
 L<parted(8)>,
+L<truncate(1)>,
+L<fallocate(1)>,
+L<grub(8)>,
+L<grub-install(8)>,
+L<virt-rescue(1)>,
 L<Sys::Guestfs(3)>,
 L<http://libguestfs.org/>.
 
 L<Sys::Guestfs(3)>,
 L<http://libguestfs.org/>.