summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--LICENSE21
-rw-r--r--README.md233
-rw-r--r--ci.nix2
-rw-r--r--cli.nix37
-rw-r--r--default.nix50
-rwxr-xr-xdisk-deactivate/disk-deactivate6
-rw-r--r--disk-deactivate/disk-deactivate.jq77
-rwxr-xr-xdisko125
-rw-r--r--example/boot-raid1.nix117
-rw-r--r--example/btrfs-subvolumes.nix52
-rw-r--r--example/complex.nix199
-rw-r--r--example/config.nix112
-rw-r--r--example/default.nix9
-rw-r--r--example/gpt-bios-compat.nix37
-rw-r--r--example/luks-lvm.nix76
-rw-r--r--example/lvm-raid.nix110
-rw-r--r--example/mdadm.nix83
-rw-r--r--example/multi-device-no-deps.nix50
-rw-r--r--example/negative-size.nix27
-rw-r--r--example/simple-efi.nix40
-rw-r--r--example/stand-alone/configuration.nix55
-rw-r--r--example/stand-alone/tsp-disk.json22
-rw-r--r--example/swap.nix50
-rw-r--r--example/tmpfs.nix48
-rw-r--r--example/with-lib.nix35
-rw-r--r--example/zfs-over-legacy.nix66
-rw-r--r--example/zfs.nix95
-rw-r--r--flake.lock27
-rw-r--r--flake.nix38
-rw-r--r--lib/default.nix128
-rw-r--r--module.nix47
-rw-r--r--package.nix34
-rw-r--r--tests/boot-raid1.nix10
-rw-r--r--tests/btrfs-subvolumes.nix11
-rw-r--r--tests/cli.nix28
-rw-r--r--tests/complex.nix27
-rw-r--r--tests/default.nix28
-rw-r--r--tests/gpt-bios-compat.nix11
-rw-r--r--tests/lib.nix177
-rw-r--r--tests/luks-lvm.nix15
-rw-r--r--tests/lvm-raid.nix12
-rw-r--r--tests/mdadm.nix12
-rw-r--r--tests/module.nix28
-rw-r--r--tests/multi-device-no-deps.nix12
-rw-r--r--tests/negative-size.nix11
-rw-r--r--tests/simple-efi.nix9
-rw-r--r--tests/swap.nix22
-rw-r--r--tests/test.nix97
-rw-r--r--tests/tmpfs.nix10
-rw-r--r--tests/with-lib.nix11
-rw-r--r--tests/zfs-over-legacy.nix11
-rw-r--r--tests/zfs.nix29
-rw-r--r--types.nix1414
54 files changed, 3714 insertions, 380 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b2be92b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+result
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2596a2e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Nix community projects
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 70058f1..845fcd6 100644
--- a/README.md
+++ b/README.md
@@ -1,71 +1,188 @@
-disko
-=====
+# disko - declarative disk partitioning
-nix-powered automatic disk partitioning
+Disko takes the NixOS module system and makes it work for disk partitioning
+as well.
-Usage
-=====
+I wanted to write a curses NixOS installer, and that was the first step that I
+hit; the disk formatting is a manual process. Once that's done, the NixOS
+system itself is declarative, but the actual formatting of disks is manual.
-Master Boot Record
-------------------
-This is how your iso configuation may look like
+## Features
-```nix
-{ pkgs, ... }:
-let
- disko = (builtins.fetchGit {
- url = https://cgit.lassul.us/disko/;
- rev = "88f56a0b644dd7bfa8438409bea5377adef6aef4";
- }) + "/lib";
- cfg = builtins.fromJSON ./tsp-disk.json;
-in {
- imports = [
- <nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix>
- ];
- environment.systemPackages = with pkgs;[
- (pkgs.writeScriptBin "tsp-create" (disko.mount cfg))
- (pkgs.writeScriptBin "tsp-mount" (disko.mount cfg))
- ];
- # Optional: Automatically creates a service which runs at startup to perform the partitioning
- systemd.services.install-to-hd = {
- enable = true;
- wantedBy = ["multi-user.target"];
- after = ["getty@tty1.service" ];
- serviceConfig = {
- Type = "oneshot";
- ExecStart = [ (disko.create cfg) (disk.mount cfg) (];
- StandardInput = "null";
- StandardOutput = "journal+console";
- StandardError = "inherit";
+* supports LVM, ZFS, btrfs, GPT, mdadm, ext4, ...
+* supports recursive layouts
+* outputs a NixOS-compatible module
+* CLI
+
+## How-to guides
+
+### NixOS installation
+
+During the NixOS installation process, replace the [Partitioning and
+formatting](https://nixos.org/manual/nixos/stable/index.html#sec-installation-partitioning)
+steps with the following:
+
+1. Find a disk layout in ./examples that you like.
+2. Write the config based on the example and your disk layout.
+4. Run the CLI (`nix run github:nix-community/disko`) to apply the changes.
+5. FIXME: Copy the disko module and disk layout around.
+6. Continue the NixOS installation.
+
+### Using without NixOS
+
+## Reference
+
+### Module options
+
+TODO: link to generated module options
+
+### Examples
+
+./examples
+
+### CLI
+
+TODO: output of the cli --help
+
+## Installing NixOS module
+
+You can use the NixOS module in one of the following ways:
+
+<details>
+ <summary>Flakes (Current recommendation)</summary>
+
+If you use nix flakes support:
+
+``` nix
+{
+ inputs.disko.url = "github:nix-community/disko";
+ inputs.disko.inputs.nixpkgs.follows = "nixpkgs";
+
+ outputs = { self, nixpkgs, disko }: {
+ # change `yourhostname` to your actual hostname
+ nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
+ # change to your system:
+ system = "x86_64-linux";
+ modules = [
+ ./configuration.nix
+ disko.nixosModules.disko
+ ];
};
};
}
```
-tsp-disk.json (TODO: find the correct disk)
-```json
+</details>
+<details>
+ <summary>niv</summary>
+
+ First add it to [niv](https://github.com/nmattia/niv):
+
+```console
+$ niv add nix-community/disko
+```
+
+ Then add the following to your configuration.nix in the `imports` list:
+
+```nix
{
- "type": "devices",
- "content": {
- "sda": {
- "type": "table",
- "format": "msdos",
- "partitions": [
- { "type": "partition",
- "start": "1M",
- "end": "100%",
- "bootable": true,
- "content": {
- "type": "filesystem",
- "format": "ext4",
- "mountpoint": "/"
+ imports = [ "${(import ./nix/sources.nix).disko}/modules/disko.nix" ];
+}
+```
+</details>
+<details>
+ <summary>nix-channel</summary>
+
+ As root run:
+
+```console
+$ nix-channel --add https://github.com/nix-community/disko/archive/main.tar.gz disko
+$ nix-channel --update
+```
+
+ Then add the following to your configuration.nix in the `imports` list:
+
+```nix
+{
+ imports = [ <disko/modules/disko.nix> ];
+}
+```
+</details>
+<details>
+ <summary>fetchTarball</summary>
+
+ Add the following to your configuration.nix:
+
+``` nix
+{
+ imports = [ "${builtins.fetchTarball "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix" ];
+}
+```
+
+ or with pinning:
+
+```nix
+{
+ imports = let
+ # replace this with an actual commit id or tag
+ commit = "f2783a8ef91624b375a3cf665c3af4ac60b7c278";
+ in [
+ "${builtins.fetchTarball {
+ url = "https://github.com/nix-community/disko/archive/${commit}.tar.gz";
+ # replace this with an actual hash
+ sha256 = "0000000000000000000000000000000000000000000000000000";
+ }}/module.nix"
+ ];
+}
+```
+</details>
+
+## Using the NixOS module
+
+```nix
+{
+ # checkout the example folder for how to configure different diska layouts
+ disko.devices = {
+ disk.sda = {
+ device = "/dev/sda";
+ type = "disk";
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ type = "partition";
+ name = "ESP";
+ start = "1MiB";
+ end = "100MiB";
+ bootable = true;
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ };
}
- }
- ]
- }
- }
+ {
+ name = "root";
+ type = "partition";
+ start = "100MiB";
+ end = "100%";
+ part-type = "primary";
+ bootable = true;
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ };
+ }
+ ];
+ };
+ };
+ };
}
```
-GUID Partition Table, LVM and dm-crypt
---------------------------------------
-See `examples/`
+this will configure `fileSystems` and other required NixOS options to boot the specified configuration.
+
+If you are on an installer, you probably want to disable `enableConfig`.
+
+disko will create the scripts `disko-create` and `disko-mount` which can be used to create/mount the configured disk layout.
diff --git a/ci.nix b/ci.nix
index cdb3198..3c49ab1 100644
--- a/ci.nix
+++ b/ci.nix
@@ -3,6 +3,6 @@ let
in {
test = pkgs.writeScript "test" ''
#!/bin/sh
- nix-build "${toString ./tests/test.nix}";
+ nix-build "${toString ./tests}";
'';
}
diff --git a/cli.nix b/cli.nix
new file mode 100644
index 0000000..006779e
--- /dev/null
+++ b/cli.nix
@@ -0,0 +1,37 @@
+{ pkgs ? import <nixpkgs> {}
+, mode ? "mount"
+, flake ? null
+, flakeAttr ? null
+, diskoFile ? null
+, noDeps ? false
+, ... }@args:
+let
+ disko = import ./. {
+ lib = pkgs.lib;
+ };
+
+ diskFormat = if flake != null then
+ (pkgs.lib.attrByPath [ "diskoConfigurations" flakeAttr ] (builtins.abort "${flakeAttr} does not exist") (builtins.getFlake flake)) args
+ else
+ import diskoFile args;
+
+ diskoEval = if noDeps then
+ if (mode == "create") then
+ disko.createScriptNoDeps diskFormat pkgs
+ else if (mode == "mount") then
+ disko.mountScriptNoDeps diskFormat pkgs
+ else if (mode == "zap_create_mount") then
+ disko.zapCreateMountScriptNoDeps diskFormat pkgs
+ else
+ builtins.abort "invalid mode"
+ else
+ if (mode == "create") then
+ disko.createScript diskFormat pkgs
+ else if (mode == "mount") then
+ disko.mountScript diskFormat pkgs
+ else if (mode == "zap_create_mount") then
+ disko.zapCreateMount diskFormat pkgs
+ else
+ builtins.abort "invalid mode"
+ ;
+in diskoEval
diff --git a/default.nix b/default.nix
index 6d08b8d..ada33e4 100644
--- a/default.nix
+++ b/default.nix
@@ -1,3 +1,49 @@
-{
- inherit (import ./lib) config create mount;
+{ lib ? import <nixpkgs/lib> }:
+let
+ types = import ./types.nix { inherit lib; };
+ eval = cfg: lib.evalModules {
+ modules = lib.singleton {
+ # _file = toString input;
+ imports = lib.singleton { devices = cfg; };
+ options = {
+ devices = lib.mkOption {
+ type = types.devices;
+ };
+ };
+ };
+ };
+in {
+ types = types;
+ create = cfg: types.diskoLib.create (eval cfg).config.devices;
+ createScript = cfg: pkgs: pkgs.writeScript "disko-create" ''
+ #!/usr/bin/env bash
+ export PATH=${lib.makeBinPath (types.diskoLib.packages (eval cfg).config.devices pkgs)}:$PATH
+ ${types.diskoLib.create (eval cfg).config.devices}
+ '';
+ createScriptNoDeps = cfg: pkgs: pkgs.writeScript "disko-create" ''
+ #!/usr/bin/env bash
+ ${types.diskoLib.create (eval cfg).config.devices}
+ '';
+ mount = cfg: types.diskoLib.mount (eval cfg).config.devices;
+ mountScript = cfg: pkgs: pkgs.writeScript "disko-mount" ''
+ #!/usr/bin/env bash
+ export PATH=${lib.makeBinPath (types.diskoLib.packages (eval cfg).config.devices pkgs)}:$PATH
+ ${types.diskoLib.mount (eval cfg).config.devices}
+ '';
+ mountScriptNoDeps = cfg: pkgs: pkgs.writeScript "disko-mount" ''
+ #!/usr/bin/env bash
+ ${types.diskoLib.mount (eval cfg).config.devices}
+ '';
+ zapCreateMount = cfg: types.diskoLib.zapCreateMount (eval cfg).config.devices;
+ zapCreateMountScript = cfg: pkgs: pkgs.writeScript "disko-zap-create-mount" ''
+ #!/usr/bin/env bash
+ export PATH=${lib.makeBinPath (types.diskoLib.packages (eval cfg).config.devices pkgs)}:$PATH
+ ${types.diskoLib.zapCreateMount (eval cfg).config.devices}
+ '';
+ zapCreateMountScriptNoDeps = cfg: pkgs: pkgs.writeScript "disko-zap-create-mount" ''
+ #!/usr/bin/env bash
+ ${types.diskoLib.zapCreateMount (eval cfg).config.devices}
+ '';
+ config = cfg: { imports = types.diskoLib.config (eval cfg).config.devices; };
+ packages = cfg: types.diskoLib.packages (eval cfg).config.devices;
}
diff --git a/disk-deactivate/disk-deactivate b/disk-deactivate/disk-deactivate
new file mode 100755
index 0000000..7dcd753
--- /dev/null
+++ b/disk-deactivate/disk-deactivate
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -efux
+# dependencies: jq util-linux lvm2 mdadm zfs
+disk=$1
+
+lsblk --output-all --json | jq -r --arg disk_to_clear "$disk" -f "$(dirname $0)/disk-deactivate.jq"
diff --git a/disk-deactivate/disk-deactivate.jq b/disk-deactivate/disk-deactivate.jq
new file mode 100644
index 0000000..54d98f7
--- /dev/null
+++ b/disk-deactivate/disk-deactivate.jq
@@ -0,0 +1,77 @@
+# since lsblk lacks zfs support, we have to do it this way
+def remove:
+ if .fstype == "zfs_member" then
+ "zpool destroy -f \(.label)"
+ elif .fstype == "LVM2_member" then
+ [
+ "vg=$(pvs \(.path) --noheadings --options vg_name | grep -o '[a-zA-Z0-9-]*')",
+ "vgchange -a n \"$vg\"",
+ "vgremove -f \"$vg\""
+ ]
+ elif .fstype == "swap" then
+ "swapoff \(.path)"
+ elif .fstype == null then
+ # maybe its zfs
+ [
+ # the next line has some horrible escaping
+ "zpool=$(zdb -l \(.path) | sed -nr $'s/ +name: \\'(.*)\\'/\\\\1/p')",
+ "if [[ -n \"${zpool}\" ]]; then zpool destroy -f \"$zpool\"; fi",
+ "unset zpool"
+ ]
+ else
+ []
+ end
+;
+
+def deactivate:
+ if .type == "disk" then
+ [
+ "wipefs --all -f \(.path)"
+ ]
+ elif .type == "part" then
+ [
+ "wipefs --all -f \(.path)"
+ ]
+ elif .type == "crypt" then
+ [
+ "cryptsetup luksClose \(.path)",
+ "wipefs --all -f \(.path)"
+ ]
+ elif .type == "lvm" then
+ (.name | split("-")[0]) as $vgname |
+ (.name | split("-")[1]) as $lvname |
+ [
+ "lvremove -fy \($vgname)/\($lvname)"
+ ]
+ elif .type == "raid1" then
+ [
+ "mdadm --stop \(.name)"
+ ]
+ else
+ []
+ end
+;
+
+def walk:
+ [
+ (.mountpoints[] | "umount -R \(.)"),
+ ((.children // []) | map(walk)),
+ remove,
+ deactivate
+ ]
+;
+
+def init:
+ "/dev/\(.name)" as $disk |
+ if $disk == $disk_to_clear then
+ [
+ "set -fu",
+ walk
+ ]
+ else
+ []
+ end
+;
+
+.blockdevices | map(init) | flatten | join("\n")
+
diff --git a/disko b/disko
new file mode 100755
index 0000000..7e4f0bf
--- /dev/null
+++ b/disko
@@ -0,0 +1,125 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+readonly libexec_dir="${0%/*}"
+
+# a file with the disko config
+declare disko_config
+
+# a flake uri, if present disko config is relative to the flake root
+declare from_flake
+
+# mount was chosen as the default mode because it's less destructive
+mode=mount
+nix_args=()
+
+showUsage() {
+ cat <<USAGE
+Usage: $0 [options] disk-config.nix
+or $0 [options] --flake github:somebody/somewhere
+
+Options:
+
+* -m, --mode mode
+ set the mode, either create or mount
+* -f, --flake uri
+ fetch the disko config relative to this flake's root
+* --arg name value
+ pass value to nix-build. can be used to set disk-names for example
+* --argstr name value
+ pass value to nix-build as string
+* --dry-run
+ just show the path to the script instead of running it
+* --debug
+ run with set -x
+USAGE
+}
+
+abort() {
+ echo "aborted: $*" >&2
+ exit 1
+}
+
+## Main ##
+
+[[ $# -eq 0 ]] && {
+ showUsage
+ exit 1
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --debug)
+ set -x
+ ;;
+ -m | --mode)
+ mode=$2
+ shift
+ ;;
+ -f | --flake)
+ flake="$2"
+ shift
+ ;;
+ --argstr | --arg)
+ nix_args+=("$1" "$2" "$3")
+ shift
+ shift
+ ;;
+ --help)
+ showUsage
+ exit 0
+ ;;
+ --dry-run)
+ dry_run=y
+ ;;
+ --no-deps)
+ nix_args+=(--arg noDeps true)
+ ;;
+ --show-trace)
+ nix_args+=("$1")
+ ;;
+ *)
+ if [ -z ${disko_config+x} ]; then
+ disko_config=$1
+ else
+ showUsage
+ exit 1
+ fi
+ ;;
+ esac
+ shift
+done
+
+if ! ([[ $mode = "create" ]] || [[ $mode = "mount" ]] || [[ $mode = "zap_create_mount" ]]); then
+ abort "mode must be either create, mount or zap_create_mount"
+fi
+
+if [[ ! -z "${flake+x}" ]]; then
+ if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
+ flake="${BASH_REMATCH[1]}"
+ flakeAttr="${BASH_REMATCH[2]}"
+ fi
+ if [[ -z "$flakeAttr" ]]; then
+ echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri."
+ echo "For example, to use the output diskoConfigurations.foo from the flake.nix, append \"#foo\" to the flake-uri."
+ exit 1
+ fi
+ nix_args+=("--arg" "flake" "$flake")
+ nix_args+=("--argstr" "flakeAttr" "$flakeAttr")
+ nix_args+=(--extra-experimental-features flakes)
+elif [[ ! -z "${disko_config+x}" ]] && [[ -e "$disko_config" ]]; then
+ nix_args+=("--arg" "diskoFile" "$disko_config")
+else
+ abort "disko config must be an existing file or flake must be set"
+fi
+
+script=$(nix-build "${libexec_dir}"/cli.nix \
+ --no-out-link \
+ --argstr mode "$mode" \
+ "${nix_args[@]}"
+)
+if [[ ! -z "${dry_run+x}" ]]; then
+ echo "$script"
+else
+ exec "$script"
+fi
diff --git a/example/boot-raid1.nix b/example/boot-raid1.nix
new file mode 100644
index 0000000..dfd2e6c
--- /dev/null
+++ b/example/boot-raid1.nix
@@ -0,0 +1,117 @@
+{ disks ? [ "/dev/vdb" "/dev/vdc" ], ... }: {
+ disk = {
+ one = {
+ type = "disk";
+ device = builtins.elemAt disks 0;
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ name = "boot";
+ type = "partition";
+ start = "0";
+ end = "1M";
+ part-type = "primary";
+ flags = ["bios_grub"];
+ }
+ {
+ type = "partition";
+ name = "ESP";
+ start = "1MiB";
+ end = "128MiB";
+ fs-type = "fat32";
+ bootable = true;
+ content = {
+ type = "mdraid";
+ name = "boot";
+ };
+ }
+ {
+ type = "partition";
+ name = "mdadm";
+ start = "128MiB";
+ end = "100%";
+ content = {
+ type = "mdraid";
+ name = "raid1";
+ };
+ }
+ ];
+ };
+ };
+ two = {
+ type = "disk";
+ device = builtins.elemAt disks 1;
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ name = "boot";
+ type = "partition";
+ start = "0";
+ end = "1M";
+ part-type = "primary";
+ flags = ["bios_grub"];
+ }
+ {
+ type = "partition";
+ name = "ESP";
+ start = "1MiB";
+ end = "128MiB";
+ fs-type = "fat32";
+ bootable = true;
+ content = {
+ type = "mdraid";
+ name = "boot";
+ };
+ }
+ {
+ type = "partition";
+ name = "mdadm";
+ start = "128MiB";
+ end = "100%";
+ content = {
+ type = "mdraid";
+ name = "raid1";
+ };
+ }
+ ];
+ };
+ };
+ };
+ mdadm = {
+ boot = {
+ type = "mdadm";
+ level = 1;
+ metadata = "1.0";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ };
+ };
+ raid1 = {
+ type = "mdadm";
+ level = 1;
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ type = "partition";
+ name = "primary";
+ start = "1MiB";
+ end = "100%";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ };
+ }
+ ];
+ };
+ };
+ };
+}
diff --git a/example/btrfs-subvolumes.nix b/example/btrfs-subvolumes.nix
new file mode 100644
index 0000000..25994df
--- /dev/null
+++ b/example/btrfs-subvolumes.nix
@@ -0,0 +1,52 @@
+{ disks ? [ "/dev/vdb" ], ... }: {
+ disk = {
+ vdb = {
+ type = "disk";
+ device = builtins.elemAt disks 0;
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ type = "partition";
+ name = "ESP";
+ start = "1MiB";
+ end = "128MiB";
+ fs-type = "fat32";
+ bootable = true;
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ };
+ }
+ {
+ name = "root";
+ type = "partition";
+ start = "128MiB";
+ end = "100%";
+ content = {
+ type = "btrfs";
+ extraArgs = "-f"; # Override existing partition
+ subvolumes = {
+ # Subvolume name is different from mountpoint
+ "/rootfs" = {
+ mountpoint = "/";
+ };
+ # Mountpoints inferred from subvolume name
+ "/home" = {
+ mountOptions = ["compress=zstd"];
+ };
+ "/nix" = {
+ mountOptions = ["compress=zstd" "noatime"];
+ };
+ "/test" = {};
+ };
+ };
+ }
+ ];
+ };
+ };
+ };
+}
+
diff --git a/example/complex.nix b/example/complex.nix
new file mode 100644
index 0000000..939f71e
--- /dev/null
+++ b/example/complex.nix
@@ -0,0 +1,199 @@
+{ disks ? [ "/dev/vdb" "/dev/vdc" "/dev/vdd" ], ... }: {
+ disk = {
+ disk0 = {
+ type = "disk";
+ device = builtins.elemAt disks 0;
+ content = {
+ type = "table";
+ format = "gpt";
+ partitions = [
+ {
+ type = "partition";
+ name = "ESP";
+ start = "1MiB";
+ end = "128MiB";
+ fs-type = "fat32";
+ bootable = true;
+ content = {
+ type = "filesystem";
+ format = "vfat