=head2 SETTING ALIGNMENT
-Currently there is no virt tool for fixing alignment problems in
-guests. This is a difficult problem to fix because simply moving
-partitions around breaks the bootloader, necessitating either manual
-reinstallation of the bootloader using a rescue disk, or complex and
-error-prone hacks.
-
-L<virt-resize(1)> does not change the alignment of the first
-partition, but it does align the second and subsequent partitions to a
-multiple of 64 or 128 sectors (depending on the version of
-virt-resize, 128 in virt-resize E<ge> 1.13.19). For operating systems
-that have a separate boot partition, virt-resize could be used to
-align the main OS partition, so that the majority of OS accesses
-except at boot will be aligned.
-
-The easiest way to correct partition alignment problems is to
-reinstall your guest operating systems. If you install operating
-systems from templates, ensure these have correct partition alignment
-too.
+L<virt-resize(1)> can change the alignment of the partitions of some
+guests. Currently it can fully align all the partitions of all
+Windows guests, and it will fix the bootloader where necessary. For
+Linux guests, it can align the second and subsequent partitions, so
+the majority of OS accesses except at boot will be aligned.
+
+Another way to correct partition alignment problems is to reinstall
+your guest operating systems. If you install operating systems from
+templates, ensure these have correct partition alignment too.
For older versions of Windows, the following NetApp document contains
useful information: L<http://media.netapp.com/documents/tr-3747.pdf>
(* Command line argument parsing. *)
let prog = Filename.basename Sys.executable_name
-let infile, outfile, alignment, copy_boot_loader, debug, deletes, dryrun,
- expand, expand_content, extra_partition, format, ignores,
+type align_first_t = [ `Never | `Always | `Auto ]
+
+let infile, outfile, align_first, alignment, copy_boot_loader, debug, deletes,
+ dryrun, expand, expand_content, extra_partition, format, ignores,
lv_expands, machine_readable, ntfsresize_force, output_format,
quiet, resizes, resizes_force, shrink =
let display_version () =
let add xs s = xs := s :: !xs in
+ let align_first = ref "auto" in
let alignment = ref 128 in
let copy_boot_loader = ref true in
let debug = ref false in
in
let argspec = Arg.align [
+ "--align-first", Arg.Set_string align_first, "never|always|auto Align first partition (default: auto)";
"--alignment", Arg.Set_int alignment, "sectors Set partition alignment (default: 128 sectors)";
"--no-copy-boot-loader", Arg.Clear copy_boot_loader, " Don't copy boot loader";
"-d", Arg.Set debug, " Enable debugging messages";
error "alignment cannot be < 1";
let alignment = Int64.of_int alignment in
+ let align_first =
+ match !align_first with
+ | "never" -> `Never
+ | "always" -> `Always
+ | "auto" -> `Auto
+ | _ ->
+ error "unknown --align-first option: use never|always|auto" 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
printf "32bitok\n";
printf "128-sector-alignment\n";
printf "alignment\n";
+ printf "align-first\n";
let g = new G.guestfs () in
g#add_drive_opts "/dev/null";
g#launch ();
| _ ->
error "usage is: %s [--options] indisk outdisk" prog in
- infile, outfile, alignment, copy_boot_loader, debug, deletes, dryrun,
- expand, expand_content, extra_partition, format, ignores,
+ infile, outfile, align_first, alignment, copy_boot_loader, debug, deletes,
+ dryrun, expand, expand_content, extra_partition, format, ignores,
lv_expands, machine_readable, ntfsresize_force, output_format,
quiet, resizes, resizes_force, shrink
ignore (g#pwrite_device "/dev/sdb" loader start)
)
+(* Are we going to align the first partition and fix the bootloader? *)
+let align_first_partition_and_fix_bootloader =
+ (* Bootloaders that we know how to fix. *)
+ let can_fix_boot_loader =
+ match partitions with
+ | { p_type = ContentFS ("ntfs", _); p_bootable = true;
+ p_operation = OpCopy | OpIgnore | OpResize _ } :: _ -> true
+ | _ -> false
+ in
+
+ match align_first, can_fix_boot_loader with
+ | `Never, _
+ | `Auto, false -> false
+ | `Always, _
+ | `Auto, true -> true
+
(* Repartition the target disk. *)
(* Calculate the location of the partitions on the target disk. This
[]
in
- (* 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.
+ (* Choose the alignment of the first partition based on the
+ * '--align-first' option. Old virt-resize used to always align this
+ * to 64 sectors, but this causes boot failures unless we are able to
+ * adjust the bootloader accordingly.
*)
- let start = (List.hd partitions).p_part.G.part_start /^ sectsize in
+ let start =
+ if align_first_partition_and_fix_bootloader then
+ alignment
+ else
+ (* Preserve the existing start, but convert to sectors. *)
+ (List.hd partitions).p_part.G.part_start /^ sectsize in
+
loop 1 start partitions
(* Now partition the target disk. *)
| _ -> ()
) partitions
+(* Fix the bootloader if we aligned the first partition. *)
+let () =
+ if align_first_partition_and_fix_bootloader then (
+ (* See can_fix_boot_loader above. *)
+ match partitions with
+ | { p_type = ContentFS ("ntfs", _); p_bootable = true;
+ p_target_partnum = partnum; p_target_start = start } :: _ ->
+ (* If the first partition is NTFS and bootable, set the "Number of
+ * Hidden Sectors" field in the NTFS Boot Record so that the
+ * filesystem is still bootable.
+ *)
+
+ (* Should always be /dev/sdb1? *)
+ let target = sprintf "/dev/sdb%d" partnum in
+
+ (* Sanity check: it contains the NTFS magic. *)
+ let magic = g#pread_device target 8 3L in
+ if magic <> "NTFS " then
+ eprintf "warning: first partition is NTFS but does not contain NTFS boot loader magic\n%!"
+ else (
+ if not quiet then
+ printf "Fixing first NTFS partition boot record ...\n%!";
+
+ if debug then (
+ let old_hidden = int_of_le32 (g#pread_device target 4 0x1c_L) in
+ eprintf "old hidden sectors value: 0x%Lx\n%!" old_hidden
+ );
+
+ let new_hidden = le32_of_int start in
+ ignore (g#pwrite_device target new_hidden 0x1c_L)
+ )
+
+ | _ -> ()
+ )
+
(* After copying the data over we must shut down and restart the
* appliance in order to expand the content. The reason for this may
* not be obvious, but it's because otherwise we'll have duplicate VGs
Display help.
+=item B<--align-first auto>
+
+=item B<--align-first never>
+
+=item B<--align-first always>
+
+Align the first partition for improved performance (see also the
+I<--alignment> option).
+
+The default is I<--align-first auto> which only aligns the first
+partition if it is safe to do so. That is, only when we know how to
+fix the bootloader automatically, and at the moment that can only be
+done for Windows guests.
+
+I<--align-first never> means we never move the first partition.
+This is the safest option. Try this if the guest does not boot
+after resizing.
+
+I<--align-first always> means we always align the first partition (if
+it needs to be aligned). For some guests this will break the
+bootloader, making the guest unbootable.
+
=item B<--alignment N>
Set the alignment of partitions to C<N> sectors. The default in
In Windows Vista and later versions, Microsoft switched to using a
separate boot partition. In these VMs, typically C</dev/sda1> is the
-boot partition and C</dev/sda2> is the main (C:) drive. We have not
-had any luck resizing the boot partition. Doing so seems to break the
-guest completely. However expanding the second partition (ie. C:
-drive) should work.
+boot partition and C</dev/sda2> is the main (C:) drive. Resizing the
+first (boot) partition causes the bootloader to fail with
+C<0xC0000225> error. Resizing the second partition (ie. C: drive)
+should work.
Windows may initiate a lengthy "chkdsk" on first boot after a resize,
if NTFS partitions have been expanded. This is just a safety check
=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.
+after printing C<GRUB> on the console, try reinstalling grub.
guestfish -i -a newdisk
><fs> cat /boot/grub/device.map