From 962e79ef32c95435eca1c84856fdc9941ce06720 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 7 Dec 2017 02:00:21 +0200 Subject: [PATCH] nixos/make-disk-image.nix: Support EFI images - Add a new parameter `imageType` that can specify either "efi" or "legacy" (the default which should see no change in behaviour by this patch). - EFI images get a GPT partition table (instead of msdos) with a mandatory ESP partition (so we add an assert that `partitioned` is true). - Use the partx tool from util-linux to determine exact start + size of the root partition. This is required because GPT stores a secondary partition table at the end of the disk, so we can't just have mkfs.ext4 create the filesystem until the end of the disk. - (Unrelated to any EFI changes) Since we're depending on the `-E offset=X` option to mkfs which is only supported by e2fsprogs, disallow any attempts of creating partitioned disk images where the root filesystem is not ext4. --- nixos/lib/make-disk-image.nix | 84 ++++++++++++++----- .../maintainers/scripts/ec2/amazon-image.nix | 2 +- .../virtualisation/virtualbox-image.nix | 2 +- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix index 636d0223fb77..8a3d8ed17701 100644 --- a/nixos/lib/make-disk-image.nix +++ b/nixos/lib/make-disk-image.nix @@ -13,10 +13,16 @@ # grafted in the file system at path `target'. , contents ? [] -, # Whether the disk should be partitioned (with a single partition - # containing the root filesystem) or contain the root filesystem - # directly. - partitioned ? true +, # Type of partition table to use; either "legacy", "efi", or "none". + # For "efi" images, the GPT partition table is used and a mandatory ESP + # partition of reasonable size is created in addition to the root partition. + # If `installBootLoader` is true, GRUB will be installed in EFI mode. + # For "legacy", the msdos partition table is used and a single large root + # partition is created. If `installBootLoader` is true, GRUB will be + # installed in legacy mode. + # For "none", no partition table is created. Enabling `installBootLoader` + # most likely fails as GRUB will probably refuse to install. + partitionTableType ? "legacy" # Whether to invoke switch-to-configuration boot during image creation , installBootLoader ? true @@ -37,6 +43,10 @@ format ? "raw" }: +assert partitionTableType == "legacy" || partitionTableType == "efi" || partitionTableType == "none"; +# We use -E offset=X below, which is only supported by e2fsprogs +assert partitionTableType != "none" -> fsType == "ext4"; + with lib; let format' = format; in let @@ -51,6 +61,27 @@ let format' = format; in let raw = "img"; }.${format}; + rootPartition = { # switch-case + legacy = "1"; + efi = "2"; + }.${partitionTableType}; + + partitionDiskScript = { # switch-case + legacy = '' + parted --script $diskImage -- \ + mklabel msdos \ + mkpart primary ext4 1MiB -1 + ''; + efi = '' + parted --script $diskImage -- \ + mklabel gpt \ + mkpart ESP fat32 8MiB 256MiB \ + set 1 boot on \ + mkpart primary ext4 256MiB -1 + ''; + none = ""; + }.${partitionTableType}; + nixpkgs = cleanSource pkgs.path; channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} '' @@ -79,20 +110,31 @@ let format' = format; in let targets = map (x: x.target) contents; prepareImage = '' - export PATH=${makeSearchPathOutput "bin" "bin" prepareImageInputs} + export PATH=${makeBinPath prepareImageInputs} + + # Yes, mkfs.ext4 takes different units in different contexts. Fun. + sectorsToKilobytes() { + echo $(( ( "$1" * 512 ) / 1024 )) + } + + sectorsToBytes() { + echo $(( "$1" * 512 )) + } mkdir $out diskImage=nixos.raw truncate -s ${toString diskSize}M $diskImage - ${if partitioned then '' - parted --script $diskImage -- mklabel msdos mkpart primary ext4 1M -1s - offset=$((2048*512)) - '' else '' - offset=0 - ''} + ${partitionDiskScript} - mkfs.${fsType} -F -L nixos -E offset=$offset $diskImage + ${if partitionTableType != "none" then '' + # Get start & length of the root partition in sectors to $START and $SECTORS. + eval $(partx $diskImage -o START,SECTORS --nr ${rootPartition} --pairs) + + mkfs.${fsType} -F -L nixos $diskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K + '' else '' + mkfs.${fsType} -F -L nixos $diskImage + ''} root="$PWD/root" mkdir -p $root @@ -133,12 +175,12 @@ let format' = format; in let find $root/nix/store -mindepth 1 -maxdepth 1 -type f -o -type d | xargs chmod -R a-w echo "copying staging root to image..." - cptofs ${optionalString partitioned "-P 1"} -t ${fsType} -i $diskImage $root/* / + cptofs -p ${optionalString (partitionTableType != "none") "-P ${rootPartition}"} -t ${fsType} -i $diskImage $root/* / ''; in pkgs.vmTools.runInLinuxVM ( pkgs.runCommand name { preVM = prepareImage; - buildInputs = with pkgs; [ utillinux e2fsprogs ]; + buildInputs = with pkgs; [ utillinux e2fsprogs dosfstools ]; exportReferencesGraph = [ "closure" metaClosure ]; postVM = '' ${if format == "raw" then '' @@ -152,11 +194,7 @@ in pkgs.vmTools.runInLinuxVM ( memSize = 1024; } '' - ${if partitioned then '' - rootDisk=/dev/vda1 - '' else '' - rootDisk=/dev/vda - ''} + rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"} # Some tools assume these exist ln -s vda /dev/xvda @@ -166,6 +204,14 @@ in pkgs.vmTools.runInLinuxVM ( mkdir $mountPoint mount $rootDisk $mountPoint + # Create the ESP and mount it. Unlike e2fsprogs, mkfs.vfat doesn't support an + # '-E offset=X' option, so we can't do this outside the VM. + ${optionalString (partitionTableType == "efi") '' + mkdir -p /mnt/boot + mkfs.vfat -n ESP /dev/vda1 + mount /dev/vda1 /mnt/boot + ''} + # Install a configuration.nix mkdir -p /mnt/etc/nixos ${optionalString (configFile != null) '' diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix index 2e67edf8ee3d..972c04453aef 100644 --- a/nixos/maintainers/scripts/ec2/amazon-image.nix +++ b/nixos/maintainers/scripts/ec2/amazon-image.nix @@ -46,7 +46,7 @@ in { inherit lib config; inherit (cfg) contents format name; pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package - partitioned = config.ec2.hvm; + partitionTableType = if config.ec2.hvm then "legacy" else "none"; diskSize = cfg.sizeMB; configFile = pkgs.writeText "configuration.nix" '' diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix index 00381c426d23..a544403e6bed 100644 --- a/nixos/modules/virtualisation/virtualbox-image.nix +++ b/nixos/modules/virtualisation/virtualbox-image.nix @@ -25,7 +25,7 @@ in { name = "nixos-ova-${config.system.nixosLabel}-${pkgs.stdenv.system}"; inherit pkgs lib config; - partitioned = true; + partitionTableType = "legacy"; diskSize = cfg.baseImageSize; postVM =