types: add btrfs_subvol type

This commit is contained in:
Lily Foster 2022-12-17 12:09:10 -05:00 committed by lassulus
parent 1ecb428c86
commit 77c8f6460f
2 changed files with 132 additions and 30 deletions

View File

@ -27,11 +27,21 @@
end = "100%"; end = "100%";
content = { content = {
type = "btrfs"; type = "btrfs";
mountpoint = "/"; extraArgs = "-f"; # Override existing partition
subvolumes = [ subvolumes = {
"/home" # Subvolume name is different from mountpoint
"/test" "/rootfs" = {
]; mountpoint = "/";
};
# Mountpoints inferred from subvolume name
"/home" = {
mountOptions = ["compress=zstd"];
};
"/nix" = {
mountOptions = ["compress=zstd" "noatime"];
};
"/test" = {};
};
}; };
} }
]; ];

142
types.nix
View File

@ -306,7 +306,7 @@ rec {
}; };
}]; }];
}; };
_pkgs= mkOption { _pkgs = mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;
type = types.functionTo (types.listOf types.package); type = types.functionTo (types.listOf types.package);
@ -321,16 +321,103 @@ rec {
type = types.enum [ "btrfs" ]; type = types.enum [ "btrfs" ];
internal = true; internal = true;
}; };
extraArgs = mkOption {
type = types.str;
default = "";
};
mountOptions = mkOption { mountOptions = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ "defaults" ]; default = [ "defaults" ];
}; };
subvolumes = mkOption { subvolumes = mkOption {
type = types.listOf optionTypes.pathname; type = types.attrsOf btrfs_subvol;
default = []; default = {};
}; };
mountpoint = mkOption { mountpoint = mkOption {
type = optionTypes.absolute-pathname; type = types.nullOr optionTypes.absolute-pathname;
default = null;
};
_meta = mkOption {
internal = true;
readOnly = true;
type = types.functionTo diskoLib.jsonType;
default = dev:
diskoLib.deepMergeMap (subvol: subvol._meta dev) (attrValues config.subvolumes);
};
_create = mkOption {
internal = true;
readOnly = true;
type = types.functionTo types.str;
default = dev: ''
mkfs.btrfs ${dev} ${config.extraArgs}
${concatMapStrings (subvol: subvol._create dev) (attrValues config.subvolumes)}
'';
};
_mount = mkOption {
internal = true;
readOnly = true;
type = types.functionTo diskoLib.jsonType;
default = dev:
let
subvolMounts = diskoLib.deepMergeMap (subvol: subvol._mount dev config.mountpoint) (attrValues config.subvolumes);
in {
fs = subvolMounts.fs // optionalAttrs (!isNull config.mountpoint) {
${config.mountpoint} = ''
if ! findmnt ${dev} "/mnt${config.mountpoint}" > /dev/null 2>&1; then
mount ${dev} "/mnt${config.mountpoint}" \
${concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
-o X-mount.mkdir
fi
'';
};
};
};
_config = mkOption {
internal = true;
readOnly = true;
default = dev: [
(map (subvol: subvol._config dev config.mountpoint) (attrValues config.subvolumes))
(optional (!isNull config.mountpoint) {
fileSystems.${config.mountpoint} = {
device = dev;
fsType = "btrfs";
options = config.mountOptions;
};
})
];
};
_pkgs = mkOption {
internal = true;
readOnly = true;
type = types.functionTo (types.listOf types.package);
default = pkgs:
[ pkgs.btrfs-progs ] ++ flatten (map (subvolume: subvolume._pkgs pkgs) (attrValues config.subvolumes));
};
};
});
btrfs_subvol = types.submodule ({ config, ... }: {
options = {
name = mkOption {
type = types.str;
default = config._module.args.name;
};
type = mkOption {
type = types.enum [ "btrfs_subvol" ];
default = "btrfs_subvol";
internal = true;
};
extraArgs = mkOption {
type = types.str;
default = "";
};
mountOptions = mkOption {
type = types.listOf types.str;
default = [ "defaults" ];
};
mountpoint = mkOption {
type = types.nullOr optionTypes.absolute-pathname;
default = null;
}; };
_meta = mkOption { _meta = mkOption {
internal = true; internal = true;
@ -344,26 +431,27 @@ rec {
readOnly = true; readOnly = true;
type = types.functionTo types.str; type = types.functionTo types.str;
default = dev: '' default = dev: ''
mkfs.btrfs ${dev} MNTPOINT=$(mktemp -d)
${optionalString (!isNull config.subvolumes or null) '' (
MNTPOINT=$(mktemp -d) mount ${dev} "$MNTPOINT" -o subvol=/
( trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
mount ${dev} "$MNTPOINT" btrfs subvolume create "$MNTPOINT"/${config.name} ${config.extraArgs}
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT )
${concatMapStringsSep "\n" (subvolume: "btrfs subvolume create \"$MNTPOINT\"/${subvolume}") config.subvolumes}
)
''}
''; '';
}; };
_mount = mkOption { _mount = mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;
type = types.functionTo diskoLib.jsonType; type = types.functionTo (types.functionTo diskoLib.jsonType);
default = dev: { default = dev: parent: let
fs.${config.mountpoint} = '' mountpoint = if (!isNull config.mountpoint) then config.mountpoint
if ! findmnt ${dev} "/mnt${config.mountpoint}" > /dev/null 2>&1; then else if (isNull parent) then config.name
mount ${dev} "/mnt${config.mountpoint}" \ else null;
${concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \ in optionalAttrs (!isNull mountpoint) {
fs.${mountpoint} = ''
if ! findmnt ${dev} "/mnt${mountpoint}" > /dev/null 2>&1; then
mount ${dev} "/mnt${mountpoint}" \
${concatMapStringsSep " " (opt: "-o ${opt}") (config.mountOptions ++ [ "subvol=${config.name}" ])} \
-o X-mount.mkdir -o X-mount.mkdir
fi fi
''; '';
@ -372,19 +460,23 @@ rec {
_config = mkOption { _config = mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;
default = dev: [{ default = dev: parent: let
fileSystems.${config.mountpoint} = { mountpoint = if (!isNull config.mountpoint) then config.mountpoint
else if (isNull parent) then config.name
else null;
in optional (!isNull mountpoint) {
fileSystems.${mountpoint} = {
device = dev; device = dev;
fsType = "btrfs"; fsType = "btrfs";
options = config.mountOptions; options = config.mountOptions ++ [ "subvol=${config.name}" ];
}; };
}]; };
}; };
_pkgs= mkOption { _pkgs = mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;
type = types.functionTo (types.listOf types.package); type = types.functionTo (types.listOf types.package);
default = pkgs: []; default = pkgs: [ pkgs.coreutils ];
}; };
}; };
}); });