From 1f79052695f885af3ca1c106b24c93d649e1a898 Mon Sep 17 00:00:00 2001
From: jopejoe1 <johannes@joens.email>
Date: Wed, 5 Jun 2024 23:49:48 +0200
Subject: [PATCH 1/2] WIP bcachefs

---
 lib/types/bcachefs.nix     | 70 +++++++++++++++++++++++++++++
 lib/types/bcachefspool.nix | 90 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)
 create mode 100644 lib/types/bcachefs.nix
 create mode 100644 lib/types/bcachefspool.nix

diff --git a/lib/types/bcachefs.nix b/lib/types/bcachefs.nix
new file mode 100644
index 00000000..2ecfedec
--- /dev/null
+++ b/lib/types/bcachefs.nix
@@ -0,0 +1,70 @@
+{ 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;
+    };
+
+    name = lib.mkOption {
+      type = lib.types.str;
+      description = "Name";
+    };
+
+    label = lib.mkOption {
+      type = lib.types.str;
+      description = "Label";
+    };
+
+    formatOptions = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ "defaults" ];
+      description = "Format options";
+    };
+
+    _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.name} = [ dev ];
+      };
+      description = "Metadata";
+    };
+    _create = diskoLib.mkCreateOption {
+      inherit config options;
+      default = ''
+        echo ${config.device} >>"$disko_devices_dir"/bcachefs_${config.name}/devices
+        echo ${config.label} >>"$disko_devices_dir"/bcachefs_${config.name}/labels
+        echo ${lib.concatStringsSep " " config.formatOptions} >>"$disko_devices_dir"/bcachefs_${config.name}/format_options
+      '';
+    };
+    _mount = diskoLib.mkMountOption {
+      inherit config options;
+      default = { };
+    };
+    _config = lib.mkOption {
+      internal = true;
+      readOnly = true;
+      default = [ ];
+      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..dc974fa5
--- /dev/null
+++ b/lib/types/bcachefspool.nix
@@ -0,0 +1,90 @@
+{ config, options, lib, diskoLib, ... }:
+{
+  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";
+    };
+
+    formatOptions = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ "defaults" ];
+      description = "Format options";
+    };
+
+    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 =
+        lib.optionalAttrs (config.content != null) (config.content._meta [ "bcachefspool" config.name ]);
+      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_options)
+
+        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.formatOptions} \
+          $(IFS=' \' ; echo "''${device_configs[*]}")
+      '';
+    };
+
+    _mount = diskoLib.mkMountOption {
+      inherit config options;
+      default = "TODO";
+    };
+
+    _config = lib.mkOption {
+      internal = true;
+      readOnly = true;
+      default = lib.optional (config.options.mountpoint or "" != "none") {
+          fileSystems.${config.mountpoint} = {
+            device = "${lib.concatStringsSep ":" config.formatOptions}";
+            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";
+    };
+  };
+}

From e46a467358f89ab0677bbffa95f82d64f124c3c2 Mon Sep 17 00:00:00 2001
From: jopejoe1 <johannes@joens.email>
Date: Wed, 12 Jun 2024 22:03:07 +0200
Subject: [PATCH 2/2] WIP bcachefs

---
 example/bcachefs-multidisk.nix | 54 ++++++++++++++++++++++++++++++++++
 lib/default.nix                | 24 ++++++++++++---
 lib/types/bcachefs.nix         | 25 +++++++++-------
 lib/types/bcachefspool.nix     | 34 +++++++++++++--------
 lib/types/disk.nix             |  4 +--
 lib/types/gpt.nix              |  4 +--
 lib/types/luks.nix             |  4 +--
 lib/types/lvm_vg.nix           |  4 +--
 lib/types/mdadm.nix            |  4 +--
 lib/types/table.nix            |  4 +--
 lib/types/zfs_volume.nix       |  4 +--
 module.nix                     |  1 +
 tests/bcachefs-multidisk.nix   | 16 ++++++++++
 13 files changed, 142 insertions(+), 40 deletions(-)
 create mode 100644 example/bcachefs-multidisk.nix
 create mode 100644 tests/bcachefs-multidisk.nix

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 <nixpkgs/nixos/tests/make-test-python.nix>
 , eval-config ? import <nixpkgs/nixos/lib/eval-config.nix>
+, 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
index 2ecfedec..43c7f7a5 100644
--- a/lib/types/bcachefs.nix
+++ b/lib/types/bcachefs.nix
@@ -12,20 +12,21 @@
       default = device;
     };
 
-    name = lib.mkOption {
+    pool = lib.mkOption {
       type = lib.types.str;
-      description = "Name";
+      description = "Pool";
     };
 
     label = lib.mkOption {
       type = lib.types.str;
+      default = config._module.args.name;
       description = "Label";
     };
 
-    formatOptions = lib.mkOption {
+    formatArgs = lib.mkOption {
       type = lib.types.listOf lib.types.str;
-      default = [ "defaults" ];
-      description = "Format options";
+      default = [];
+      description = "Formating Arguments";
     };
 
     _parent = lib.mkOption {
@@ -37,16 +38,16 @@
       readOnly = true;
       type = lib.types.functionTo diskoLib.jsonType;
       default = dev: {
-        deviceDependencies.bcachefspool.${config.name} = [ dev ];
+        deviceDependencies.bcachefspool.${config.pool} = [ dev ];
       };
       description = "Metadata";
     };
     _create = diskoLib.mkCreateOption {
       inherit config options;
       default = ''
-        echo ${config.device} >>"$disko_devices_dir"/bcachefs_${config.name}/devices
-        echo ${config.label} >>"$disko_devices_dir"/bcachefs_${config.name}/labels
-        echo ${lib.concatStringsSep " " config.formatOptions} >>"$disko_devices_dir"/bcachefs_${config.name}/format_options
+        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 {
@@ -56,7 +57,11 @@
     _config = lib.mkOption {
       internal = true;
       readOnly = true;
-      default = [ ];
+      default = [
+        {
+          disko.devices._internal.bcachefspools.${config.pool} = [ (lib.traceVal config.device) ];
+        }
+      ];
       description = "NixOS configuration";
     };
     _pkgs = lib.mkOption {
diff --git a/lib/types/bcachefspool.nix b/lib/types/bcachefspool.nix
index dc974fa5..bd61de05 100644
--- a/lib/types/bcachefspool.nix
+++ b/lib/types/bcachefspool.nix
@@ -1,4 +1,4 @@
-{ config, options, lib, diskoLib, ... }:
+{ config, options, lib, rootMountPoint, diskoLib, toplevel-config, ... }:
 {
   options = {
     name = lib.mkOption {
@@ -14,10 +14,10 @@
       description = "Type";
     };
 
-    formatOptions = lib.mkOption {
+    formatArgs = lib.mkOption {
       type = lib.types.listOf lib.types.str;
-      default = [ "defaults" ];
-      description = "Format options";
+      default = [];
+      description = "Formating Arguments";
     };
 
     mountOptions = lib.mkOption {
@@ -36,8 +36,7 @@
       internal = true;
       readOnly = true;
       type = diskoLib.jsonType;
-      default =
-        lib.optionalAttrs (config.content != null) (config.content._meta [ "bcachefspool" config.name ]);
+      default = { };
       description = "Metadata";
     };
 
@@ -46,7 +45,7 @@
       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_options)
+        readarray -t bcachefs_format_options < <(cat "$disko_devices_dir"/bcachefs_${config.name}/format_args)
 
         device_configs=()
 
@@ -57,26 +56,37 @@
             device_configs+=("--label=$label $format_options $device")
         done
 
-        bcachefs format --fs_label=${config.name} ${lib.concatStringsSep " " config.formatOptions} \
+        bcachefs format --fs_label=${config.name} ${lib.concatStringsSep " " config.formatArgs} \
           $(IFS=' \' ; echo "''${device_configs[*]}")
       '';
     };
 
     _mount = diskoLib.mkMountOption {
       inherit config options;
-      default = "TODO";
+      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 = lib.optional (config.options.mountpoint or "" != "none") {
+      default = [ {
           fileSystems.${config.mountpoint} = {
-            device = "${lib.concatStringsSep ":" config.formatOptions}";
+            device = "${lib.concatStringsSep ":" (lib.traceVal toplevel-config.disko.devices._internal.bcachefspools).${config.name}}";
             fsType = "bcachefs";
             options = config.mountOptions;
           };
-        };
+        }
+      ];
       description = "NixOS configuration";
     };
     _pkgs = lib.mkOption {
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 <nixpkgs> { }
+, 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" ];
+  };
+}