summary refs log tree commit diff
path: root/nixos/lib/make-disk-image.nix
diff options
context:
space:
mode:
authorTuomas Tynkkynen <tuomas.tynkkynen@iki.fi>2017-12-07 02:00:21 +0200
committerTuomas Tynkkynen <tuomas.tynkkynen@iki.fi>2018-01-22 11:18:23 +0200
commit962e79ef32c95435eca1c84856fdc9941ce06720 (patch)
tree4efe7988be7c5a67cf210072000f686885033dee /nixos/lib/make-disk-image.nix
parent73695300bc0ad15d01e5ac52329b9668b1a33704 (diff)
downloadnixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar.gz
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar.bz2
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar.lz
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar.xz
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.tar.zst
nixpkgs-962e79ef32c95435eca1c84856fdc9941ce06720.zip
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.
Diffstat (limited to 'nixos/lib/make-disk-image.nix')
-rw-r--r--nixos/lib/make-disk-image.nix82
1 files changed, 64 insertions, 18 deletions
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 636d0223fb7..8a3d8ed1770 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,21 +110,32 @@ 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))
+    ${partitionDiskScript}
+
+    ${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 ''
-      offset=0
+      mkfs.${fsType} -F -L nixos $diskImage
     ''}
 
-    mkfs.${fsType} -F -L nixos -E offset=$offset $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) ''