diff --git a/example/bcachefs-multidisk.nix b/example/bcachefs-multidisk.nix new file mode 100644 index 00000000..4fdbc875 --- /dev/null +++ b/example/bcachefs-multidisk.nix @@ -0,0 +1,54 @@ +{ + disko.devices = { + disk = { + x = { + type = "disk"; + device = "/dev/sdx"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "64M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + bcachefs = { + size = "100%"; + content = { + type = "bcachefs"; + pool = "broot"; + }; + }; + }; + }; + }; + y = { + type = "disk"; + device = "/dev/sdy"; + content = { + type = "gpt"; + partitions = { + bcachefs = { + size = "100%"; + content = { + type = "bcachefs"; + pool = "broot"; + }; + }; + }; + }; + }; + }; + bcachefspool = { + broot = { + type = "bcachefspool"; + mountpoint = "/"; + }; + }; + }; +} + diff --git a/lib/default.nix b/lib/default.nix index 1647337a..92ea0495 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -2,6 +2,7 @@ , rootMountPoint ? "/mnt" , makeTest ? import , eval-config ? import +, toplevel-config ? {} }: with lib; with builtins; @@ -35,7 +36,7 @@ let # option for valid contents of partitions (basically like devices, but without tables) partitionType = extraArgs: lib.mkOption { type = lib.types.nullOr (diskoLib.subType { - types = { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; }; + types = { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap bcachefs; }; inherit extraArgs; }); default = null; @@ -45,7 +46,7 @@ let # option for valid contents of devices deviceType = extraArgs: lib.mkOption { type = lib.types.nullOr (diskoLib.subType { - types = { inherit (diskoLib.types) table gpt btrfs filesystem zfs mdraid luks lvm_pv swap; }; + types = { inherit (diskoLib.types) table gpt btrfs filesystem zfs mdraid luks lvm_pv swap bcachefs; }; inherit extraArgs; }); default = null; @@ -219,7 +220,7 @@ let postMountHook = diskoLib.mkHook "shell commands to run after mount"; }; config._module.args = { - inherit diskoLib rootMountPoint; + inherit diskoLib rootMountPoint toplevel-config; }; } ]; @@ -344,7 +345,7 @@ let */ toplevel = lib.types.submodule (cfg: let - devices = { inherit (cfg.config) disk mdadm zpool lvm_vg nodev; }; + devices = { inherit (cfg.config) disk mdadm zpool lvm_vg nodev bcachefspool; }; in { options = { @@ -363,6 +364,11 @@ let default = { }; description = "ZFS pool device"; }; + bcachefspool = lib.mkOption { + type = lib.types.attrsOf diskoLib.types.bcachefspool; + default = { }; + description = "BcacheFS pool device"; + }; lvm_vg = lib.mkOption { type = lib.types.attrsOf diskoLib.types.lvm_vg; default = { }; @@ -523,6 +529,16 @@ let in lib.genAttrs configKeys (key: lib.mkMerge (lib.catAttrs key collectedConfigs)); }; + _internal = { + bcachefspools = lib.mkOption { + internal = true; + type = lib.types.attrsOf (lib.types.listOf lib.types.str); + description = '' + Disko Internal List of BcacheFS pool's + ''; + default = {}; + }; + }; }; }); diff --git a/lib/types/bcachefs.nix b/lib/types/bcachefs.nix new file mode 100644 index 00000000..43c7f7a5 --- /dev/null +++ b/lib/types/bcachefs.nix @@ -0,0 +1,75 @@ +{ config, options, lib, diskoLib, parent, device, ... }: +{ + options = { + type = lib.mkOption { + type = lib.types.enum [ "bcachefs" ]; + internal = true; + description = "Type"; + }; + device = lib.mkOption { + type = lib.types.str; + description = "Device"; + default = device; + }; + + pool = lib.mkOption { + type = lib.types.str; + description = "Pool"; + }; + + label = lib.mkOption { + type = lib.types.str; + default = config._module.args.name; + description = "Label"; + }; + + formatArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Formating Arguments"; + }; + + _parent = lib.mkOption { + internal = true; + default = parent; + }; + _meta = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.functionTo diskoLib.jsonType; + default = dev: { + deviceDependencies.bcachefspool.${config.pool} = [ dev ]; + }; + description = "Metadata"; + }; + _create = diskoLib.mkCreateOption { + inherit config options; + default = '' + echo ${config.device} >>"$disko_devices_dir"/bcachefs_${config.pool}/devices + echo ${config.label} >>"$disko_devices_dir"/bcachefs_${config.pool}/labels + echo ${lib.concatStringsSep " " config.formatArgs} >>"$disko_devices_dir"/bcachefs_${config.pool}/format_args + ''; + }; + _mount = diskoLib.mkMountOption { + inherit config options; + default = { }; + }; + _config = lib.mkOption { + internal = true; + readOnly = true; + default = [ + { + disko.devices._internal.bcachefspools.${config.pool} = [ (lib.traceVal config.device) ]; + } + ]; + description = "NixOS configuration"; + }; + _pkgs = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.functionTo (lib.types.listOf lib.types.package); + default = pkgs: [ pkgs.bcachefs-tools ]; + description = "Packages"; + }; + }; +} diff --git a/lib/types/bcachefspool.nix b/lib/types/bcachefspool.nix new file mode 100644 index 00000000..bd61de05 --- /dev/null +++ b/lib/types/bcachefspool.nix @@ -0,0 +1,100 @@ +{ config, options, lib, rootMountPoint, diskoLib, toplevel-config, ... }: +{ + options = { + name = lib.mkOption { + type = lib.types.str; + default = config._module.args.name; + description = "Name"; + }; + + type = lib.mkOption { + type = lib.types.enum [ "bcachefspool" ]; + default = "bcachefspool"; + internal = true; + description = "Type"; + }; + + formatArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Formating Arguments"; + }; + + mountOptions = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "defaults" ]; + description = "Mount options"; + }; + + mountpoint = lib.mkOption { + type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname; + default = null; + description = "A path to mount the bcachefs filesystem to."; + }; + + _meta = lib.mkOption { + internal = true; + readOnly = true; + type = diskoLib.jsonType; + default = { }; + description = "Metadata"; + }; + + _create = diskoLib.mkCreateOption { + inherit config options; + default = '' + readarray -t bcachefs_devices < <(cat "$disko_devices_dir"/bcachefs_${config.name}/devices) + readarray -t bcachefs_labels < <(cat "$disko_devices_dir"/bcachefs_${config.name}/labels) + readarray -t bcachefs_format_options < <(cat "$disko_devices_dir"/bcachefs_${config.name}/format_args) + + device_configs=() + + for ((i=0; i<''${#bcachefs_devices[@]}; i++)); do + device=''${bcachefs_devices[$i]} + label=''${bcachefs_labels[$i]} + format_options=''${bcachefs_format_options[$i]} + device_configs+=("--label=$label $format_options $device") + done + + bcachefs format --fs_label=${config.name} ${lib.concatStringsSep " " config.formatArgs} \ + $(IFS=' \' ; echo "''${device_configs[*]}") + ''; + }; + + _mount = diskoLib.mkMountOption { + inherit config options; + default = { + fs = lib.optionalAttrs (config.mountpoint != null) { + ${config.mountpoint} = '' + readarray -t bcachefs_devices < <(cat "$disko_devices_dir"/bcachefs_${config.name}/devices) + + mount -t bcachefs $(IFS=':' ; echo ''${bcachefs_devices[*]}) "${rootMountPoint}${config.mountpoint}" \ + ${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \ + -o X-mount.mkdir + ''; + }; + }; + }; + + _config = lib.mkOption { + internal = true; + readOnly = true; + default = [ { + fileSystems.${config.mountpoint} = { + device = "${lib.concatStringsSep ":" (lib.traceVal toplevel-config.disko.devices._internal.bcachefspools).${config.name}}"; + fsType = "bcachefs"; + options = config.mountOptions; + }; + } + ]; + description = "NixOS configuration"; + }; + _pkgs = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.functionTo (lib.types.listOf lib.types.package); + default = pkgs: []; + description = "Packages"; + }; + }; +} diff --git a/lib/types/disk.nix b/lib/types/disk.nix index 60f31b50..77bf460d 100644 --- a/lib/types/disk.nix +++ b/lib/types/disk.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, toplevel-config, ... }: { options = { name = lib.mkOption { @@ -24,7 +24,7 @@ ''; default = "2G"; }; - content = diskoLib.deviceType { parent = config; device = config.device; }; + content = diskoLib.deviceType { parent = config; device = config.device; inherit toplevel-config; }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/gpt.nix b/lib/types/gpt.nix index d66aa480..eb946b8a 100644 --- a/lib/types/gpt.nix +++ b/lib/types/gpt.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, parent, device, ... }: +{ config, options, lib, diskoLib, parent, device, toplevel-config, ... }: let sortedPartitions = lib.sort (x: y: x.priority < y.priority) (lib.attrValues config.partitions); sortedHybridPartitions = lib.filter (p: p.hybrid != null) sortedPartitions; @@ -95,7 +95,7 @@ in or - for relative sizes from the disks end ''; }; - content = diskoLib.partitionType { parent = config; device = partition.config.device; }; + content = diskoLib.partitionType { parent = config; device = partition.config.device; inherit toplevel-config; }; hybrid = lib.mkOption { type = lib.types.nullOr (lib.types.submodule ({ ... } @ hp: { options = { diff --git a/lib/types/luks.nix b/lib/types/luks.nix index b75cb9a1..2026d51f 100644 --- a/lib/types/luks.nix +++ b/lib/types/luks.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, parent, device, ... }: +{ config, options, lib, diskoLib, parent, device, toplevel-config, ... }: let keyFile = if config.settings ? "keyFile" @@ -96,7 +96,7 @@ in description = "Extra arguments to pass to `cryptsetup luksOpen` when opening"; example = [ "--timeout 10" ]; }; - content = diskoLib.deviceType { parent = config; device = "/dev/mapper/${config.name}"; }; + content = diskoLib.deviceType { parent = config; device = "/dev/mapper/${config.name}"; inherit toplevel-config; }; _parent = lib.mkOption { internal = true; default = parent; diff --git a/lib/types/lvm_vg.nix b/lib/types/lvm_vg.nix index 5b76305a..4cb2a728 100644 --- a/lib/types/lvm_vg.nix +++ b/lib/types/lvm_vg.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, toplevel-config, ... }: { options = { name = lib.mkOption { @@ -42,7 +42,7 @@ default = [ ]; description = "Extra arguments"; }; - content = diskoLib.partitionType { parent = config; device = "/dev/${config.name}/${lv.config.name}"; }; + content = diskoLib.partitionType { parent = config; device = "/dev/${config.name}/${lv.config.name}"; inherit toplevel-config; }; }; })); default = { }; diff --git a/lib/types/mdadm.nix b/lib/types/mdadm.nix index 1564e178..7b843fa4 100644 --- a/lib/types/mdadm.nix +++ b/lib/types/mdadm.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, toplevel-config, ... }: { options = { name = lib.mkOption { @@ -22,7 +22,7 @@ default = "default"; description = "Metadata"; }; - content = diskoLib.deviceType { parent = config; device = "/dev/md/${config.name}"; }; + content = diskoLib.deviceType { parent = config; device = "/dev/md/${config.name}"; inherit toplevel-config; }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/table.nix b/lib/types/table.nix index f1815531..d520656a 100644 --- a/lib/types/table.nix +++ b/lib/types/table.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, parent, device, ... }: +{ config, options, lib, diskoLib, parent, device, toplevel-config, ... }: { options = lib.warn '' The legacy table is outdated and should not be used. We recommend using the gpt type instead. @@ -61,7 +61,7 @@ default = false; description = "Whether to make the partition bootable"; }; - content = diskoLib.partitionType { parent = config; device = diskoLib.deviceNumbering config.device partition.config._index; }; + content = diskoLib.partitionType { parent = config; device = diskoLib.deviceNumbering config.device partition.config._index; inherit toplevel-config; }; _index = lib.mkOption { internal = true; default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" name)); diff --git a/lib/types/zfs_volume.nix b/lib/types/zfs_volume.nix index 77f143fb..869d19e6 100644 --- a/lib/types/zfs_volume.nix +++ b/lib/types/zfs_volume.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, parent, ... }: +{ config, options, lib, diskoLib, parent, toplevel-config, ... }: { options = { name = lib.mkOption { @@ -30,7 +30,7 @@ description = "Size of the dataset"; }; - content = diskoLib.partitionType { parent = config; device = "/dev/zvol/${config._parent.name}/${config.name}"; }; + content = diskoLib.partitionType { parent = config; device = "/dev/zvol/${config._parent.name}/${config.name}"; inherit toplevel-config; }; _parent = lib.mkOption { internal = true; diff --git a/module.nix b/module.nix index 09893789..6a1618b9 100644 --- a/module.nix +++ b/module.nix @@ -5,6 +5,7 @@ let rootMountPoint = config.disko.rootMountPoint; makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix"); eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix"); + toplevel-config = config; }; cfg = config.disko; in diff --git a/tests/bcachefs-multidisk.nix b/tests/bcachefs-multidisk.nix new file mode 100644 index 00000000..73b0ae1f --- /dev/null +++ b/tests/bcachefs-multidisk.nix @@ -0,0 +1,16 @@ +{ pkgs ? import { } +, diskoLib ? pkgs.callPackage ../lib { } +}: +diskoLib.testLib.makeDiskoTest { + inherit pkgs; + name = "bcachefs-multidisk"; + disko-config = ../example/bcachefs-multidisk.nix; + extraTestScript = '' + machine.succeed("mountpoint /"); + machine.succeed("lsblk >&2"); + ''; + # so that the installer boots with a bcachefs enabled kernel + extraInstallerConfig = { + boot.supportedFilesystems = [ "bcachefs" ]; + }; +}