PortValhalla/lib/modules/fileSystems/disks.nix

402 lines
13 KiB
Nix
Raw Normal View History

{ lib, config, ... }:
2024-10-06 19:25:34 +00:00
let
inherit (lib) types mkOption;
fs = import ./fs.nix;
2024-12-07 18:11:41 +00:00
cfg = config.valhalla.fileSystems;
2024-12-07 16:43:53 +00:00
deviceListVarName = "myDevices";
2024-10-06 19:25:34 +00:00
isSwap = partition: builtins.elem partition.type [ fs.swap 19 ];
probeScript = builtins.concatStringsSep "\n" [
"partprobe 2> /dev/null || true"
"udevadm trigger"
];
2024-12-07 16:43:53 +00:00
mkDeviceType = types.submodule (
2024-10-06 19:25:34 +00:00
{ config, name, ... }: {
options = {
id = mkOption {
type = types.str;
2024-12-07 16:43:53 +00:00
description = "The internal identifier of the device.";
2024-10-06 19:25:34 +00:00
internal = true;
};
2024-10-06 19:25:34 +00:00
wipe = mkOption {
type = types.bool;
2024-12-07 16:43:53 +00:00
description = "A value indicating whether the device should be wiped.";
2024-10-06 19:25:34 +00:00
default = !(lib.lists.any (_: _.keepExisting) (builtins.attrValues config.partitions));
};
2024-12-07 16:43:53 +00:00
name = mkOption {
2024-10-06 19:25:34 +00:00
type = types.nullOr types.str;
description = "The name of the device.";
2024-12-07 15:11:19 +00:00
default = name;
2024-10-06 19:25:34 +00:00
};
2024-12-07 16:43:53 +00:00
path = mkOption {
2024-12-07 15:11:19 +00:00
type = types.nullOr types.str;
2024-10-06 19:25:34 +00:00
description = "The path to the device.";
2024-11-29 00:13:58 +00:00
default =
2024-12-07 16:43:53 +00:00
if config.name == null then
2024-11-29 00:13:58 +00:00
null
else
2024-12-07 16:43:53 +00:00
"/dev/${config.name}";
2024-10-06 19:25:34 +00:00
};
2024-10-06 19:25:34 +00:00
deviceScript = mkOption {
type = types.str;
description = "A command for loading the device path into the device variable";
internal = true;
};
2024-10-06 19:25:34 +00:00
deviceVariable = mkOption {
type = types.str;
description = "The name of the variable holding the name of the disk";
internal = true;
};
2024-10-06 19:25:34 +00:00
partitions = mkOption {
type = types.attrsOf (types.nullOr partitionType);
description = "The partitions of the disk.";
default = { };
};
2024-10-06 19:25:34 +00:00
scripts = {
init = mkOption {
type = types.str;
description = "A script for loading the device path into the device variable";
};
partition = mkOption {
type = types.str;
description = "A script for partitioning and formatting the device.";
};
};
};
2024-11-29 00:13:58 +00:00
config =
let
2024-12-07 16:43:53 +00:00
deviceVarName = "${deviceListVarName}[${config.id}]";
deviceVar = "\${${deviceVarName}}";
2024-11-29 00:13:58 +00:00
2024-12-07 16:43:53 +00:00
deviceSelector = ''
2024-11-29 00:13:58 +00:00
result="$(mktemp)"
2024-12-07 16:43:53 +00:00
fish ${./choose-device.fish} "$result" "Please select the \"${name}\" device:" ${./select.fish}
${deviceVarName}="$(cat "$result")"
2024-11-29 00:13:58 +00:00
'';
partitions = lib.lists.sortOn (_: _.index)
(builtins.filter (_: _ != null)
(builtins.attrValues config.partitions));
mkType = type:
lib.strings.escapeShellArg (
if builtins.isInt type then
"${lib.trivial.toHexString type}"
else
type
);
fdiskCommand = arguments: "sudo sfdisk ${arguments}";
fdiskScript = script: args: append:
"echo ${script} | ${
2024-10-25 22:01:11 +00:00
fdiskCommand "${builtins.concatStringsSep " " args} ${
if append then "--append" else ""
2024-12-07 16:43:53 +00:00
} ${deviceVar}"
2024-10-25 22:01:11 +00:00
}";
2024-11-29 00:13:58 +00:00
appendScript = index: script: fdiskScript script [ "-N" (builtins.toString index) ] true;
cleanup = lib.strings.concatLines (builtins.map
2024-12-07 16:43:53 +00:00
(partition: "${fdiskCommand "--delete ${deviceVar} ${toString partition.index}"} || true")
2024-11-29 00:13:58 +00:00
(lib.lists.sortOn
(partition: partition.index * -1)
(builtins.filter (_: !_.keepExisting) partitions)));
fdiskCommands = lib.strings.concatLines
(lib.optionals config.wipe [
cleanup
2024-12-07 16:50:10 +00:00
(fdiskScript "label: gpt" [ ] false)
2024-11-29 00:13:58 +00:00
] ++ (builtins.concatMap
(
partition:
let
inherit (partition) format index keepExisting label sizeScript type;
partVarName = "myPartition";
partVar = "\${${partVarName}}";
sizeOption = ''
${sizeScript} | sed -e "s/.*[^[:space:]]/size=\0/"
'';
formatScripts = {
${fs.ext4} = "mkfs.ext4 -F ${partVar}";
2024-12-07 17:41:08 +00:00
${fs.btrfs} = "mkfs.btrfs ${partVar}";
2024-11-29 00:13:58 +00:00
${fs.swap} = "mkswap ${partVar}";
${fs.ntfs} = "mkfs.ntfs -F ${partVar}";
${fs.fat32} = "mkfs.fat -F 32 ${partVar}";
};
labelScripts = {
${fs.ext4} = label: "e2label ${partVar} ${label}";
2024-12-07 17:41:08 +00:00
${fs.btrfs} = label: "btrfs filesystem label ${partVar} ${label}";
2024-11-29 00:13:58 +00:00
${fs.swap} = label: "swaplabel ${partVar} --label ${label}";
${fs.ntfs} = label: "ntfslabel ${partVar} ${label}";
${fs.fat32} = label: "fatlabel ${partVar} ${label}";
};
2024-12-07 17:45:44 +00:00
create = lib.strings.concatLines ([
2024-11-29 00:13:58 +00:00
(appendScript index ''${toString index}: "$(${sizeOption})" type=${mkType type}'')
probeScript
2024-12-07 17:45:44 +00:00
] ++ (lib.optionals (format != null) [
2024-11-29 00:13:58 +00:00
"sudo ${formatScripts.${format}}"
2024-12-07 17:45:44 +00:00
]));
2024-11-29 00:13:58 +00:00
fallback = ''
if ! { ls "${partVar}" 2>&1; } > /dev/null
then
${create}
fi
'';
in
[
2024-12-07 16:43:53 +00:00
''local diskPath="$(find -L /dev/disk/by-diskseq -samefile ${deviceVar})"''
2024-11-29 00:13:58 +00:00
''local ${partVarName}="$diskPath-part${toString index}"''
(if keepExisting then fallback else create)
2024-12-07 17:45:44 +00:00
] ++ (lib.optionals (format != null) [
2024-11-29 00:13:58 +00:00
"sudo ${labelScripts.${format} label}"
2024-12-07 17:45:44 +00:00
])
2024-11-29 00:13:58 +00:00
)
partitions));
fixType = lib.strings.concatLines (builtins.concatMap
(
partition:
lib.optional
(partition.keepExisting && !(builtins.isNull partition.type))
2024-12-07 16:43:53 +00:00
''sudo sfdisk --part-type ${deviceVar} ${toString partition.index} ${mkType partition.type}''
2024-11-29 00:13:58 +00:00
)
partitions);
in
{
2024-12-07 15:11:19 +00:00
id = "disk-${name}";
2024-12-07 16:43:53 +00:00
deviceVariable = deviceVar;
2024-11-29 00:13:58 +00:00
scripts = {
init =
if config.path == null then ''
${deviceSelector}
'' else ''
${deviceVarName}=${config.path}
if [ ! -b ${deviceVar} ]; then
function fallback() {
echo "Couldn't find the specified disk \"${deviceVar}\"."
if fish ${./confirm.fish} "Do you want to choose a different \"${name}\" disk?"; then
${deviceSelector}
else
exit 1
fi
}
2024-11-29 00:13:58 +00:00
fallback
fi
'';
2024-11-29 00:13:58 +00:00
partition = lib.mkDefault ''
function partition() {
${if (!config.wipe) then cleanup else ""}
${probeScript}
${fdiskCommands}
${fixType}
}
partition
'';
};
2024-11-29 00:13:58 +00:00
};
}
);
2024-10-06 19:25:34 +00:00
partitionType = types.submodule (
{ name, config, ... }: {
options = {
index = mkOption {
type = types.int;
description = "The index of the partition.";
2024-07-11 17:31:50 +00:00
};
2024-10-06 19:25:34 +00:00
label = mkOption {
type = types.str;
description = "The label of the partition.";
default = name;
};
keepExisting = mkOption {
type = types.bool;
description = "A value indicating whether the partition should be left untouched if it already exists.";
default = false;
};
type = mkOption {
type = types.nullOr (types.either types.str types.int);
description = "The type of the partition.";
default = null;
};
format = mkOption {
2024-12-07 17:45:44 +00:00
type = types.nullOr (types.enum (builtins.attrValues fs));
2024-10-06 19:25:34 +00:00
description = "The file system format of the partition.";
2024-11-29 00:13:58 +00:00
default =
if (isSwap config) then
fs.swap
else
2024-12-07 17:45:44 +00:00
null;
2024-10-06 19:25:34 +00:00
};
size = mkOption {
type = types.nullOr types.str;
description = "The size of the partition.";
default = null;
};
sizeScript = mkOption {
type = types.str;
description = "A script for printing the size to the console.";
internal = true;
};
useSwap = mkOption {
type = types.bool;
description = "A value indicating whether this partition should be used as swap.";
default = isSwap config;
};
mountPoint = mkOption {
type = types.nullOr types.str;
description = "The mountpoint of the partition.";
default = null;
};
mountOptions = mkOption {
type = types.listOf types.str;
description = "The options to apply to the mount.";
default = [ ];
};
};
config = {
sizeScript = (if isSwap config then
''echo "$(cat /proc/meminfo | awk -F " " '/^MemTotal/ { print $2 }' | awk '{ print int((($1 / 1024 / 1024) * 0.75) + 0.5)}')"G''
else
"echo ${lib.strings.escapeShellArg (toString config.size)}");
};
2024-11-29 00:13:58 +00:00
}
);
in
{
2024-10-06 19:25:34 +00:00
options = {
valhalla = {
2024-12-07 11:24:10 +00:00
fileSystems = {
2024-10-06 19:25:34 +00:00
rootDir = mkOption {
type = types.str;
description = "The root of the installation directory to mount disks into.";
default = "/mnt";
};
2024-12-07 11:24:10 +00:00
diskSetup = {
2024-12-07 16:43:53 +00:00
devices = mkOption {
type = types.attrsOf (mkDeviceType);
description = "The disk devices to format.";
2024-12-07 11:24:10 +00:00
default = { };
};
scripts = {
init = mkOption {
type = types.str;
description = "The script for initializing the disk partitioning script.";
};
partition = mkOption {
type = types.str;
description = "The script for partitioning the disks.";
};
mount = mkOption {
type = types.str;
description = "The script for mounting the partitioned disks.";
};
};
2024-10-06 19:25:34 +00:00
};
};
};
};
config = {
valhalla = {
2024-12-07 18:11:41 +00:00
fileSystems = {
mounts = (lib.attrsets.concatMapAttrs
(
name: device:
lib.attrsets.concatMapAttrs
(
2024-12-07 18:11:41 +00:00
name: partition:
if partition.mountPoint != null then {
${partition.mountPoint} = {
device = "/dev/disk/by-label/${partition.label}";
fsType = partition.format;
options = partition.mountOptions;
};
} else { }
)
2024-12-07 18:11:41 +00:00
device.partitions
)
cfg.diskSetup.devices);
diskSetup = {
scripts =
let
inherit (cfg) rootDir;
inherit (lib.strings) normalizePath;
partPath = part: "/dev/disk/by-label/${part.label}";
disks = ((builtins.attrValues cfg.diskSetup.devices));
partitions = (builtins.concatMap (_: (builtins.attrValues _.partitions)) disks);
in
{
init = lib.strings.concatLines (builtins.map (_: _.scripts.init) disks);
partition = lib.strings.concatLines (builtins.map (_: _.scripts.partition) disks);
mount = lib.strings.concatLines (
(builtins.concatMap
(
_: [
probeScript
(builtins.concatStringsSep " " (
[ "sudo" "mount" "--mkdir" ] ++
(lib.optionals (_.format == "ntfs") [ "-t" "ntfs3" ]) ++
[
(builtins.concatStringsSep " " (builtins.map (_: "-o ${_}") _.mountOptions))
(partPath _)
(normalizePath "/${rootDir}/${_.mountPoint}")
]
))
]
)
(lib.lists.sortOn
(_: normalizePath "/${_.mountPoint}")
(builtins.filter (_: _.mountPoint != null) partitions))) ++
(builtins.map
(
_: ''
${probeScript}
sudo swapon ${partPath _}
''
)
(builtins.filter (_: _.useSwap) partitions))
);
};
};
};
};
2024-10-06 19:25:34 +00:00
};
}