mirror of
https://github.com/NiklasGollenstede/nixos-installer.git
synced 2024-11-25 01:14:13 +01:00
make partitioning reproducible, add sgdisk patch,
do partitioning during build instead of install, fix qemu's efi vars, add config.wip.dropbear.hostKeys
This commit is contained in:
parent
e46b671f8f
commit
b51cf19f4a
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@ -34,6 +34,7 @@
|
|||||||
"dedup", // zfs
|
"dedup", // zfs
|
||||||
"deps", // abbr dependencies
|
"deps", // abbr dependencies
|
||||||
"devs", // abbr (devices)
|
"devs", // abbr (devices)
|
||||||
|
"dmask", // mount
|
||||||
"dnodesize", // zfs
|
"dnodesize", // zfs
|
||||||
"dontUnpack", // nixos
|
"dontUnpack", // nixos
|
||||||
"dosfstools", // package
|
"dosfstools", // package
|
||||||
@ -49,11 +50,13 @@
|
|||||||
"fetchurl", // nix function
|
"fetchurl", // nix function
|
||||||
"filesystems", // plural
|
"filesystems", // plural
|
||||||
"findutils", // package
|
"findutils", // package
|
||||||
|
"fmask", // mount
|
||||||
"foldl", // nix (fold left)
|
"foldl", // nix (fold left)
|
||||||
"foldr", // nix (fold right)
|
"foldr", // nix (fold right)
|
||||||
"FUNCNAME", // bash var
|
"FUNCNAME", // bash var
|
||||||
"fw_printenv", // program
|
"fw_printenv", // program
|
||||||
"fw_setenv", // program
|
"fw_setenv", // program
|
||||||
|
"gcroots", // Nix
|
||||||
"gdisk", // program
|
"gdisk", // program
|
||||||
"getsize64", // cli arg
|
"getsize64", // cli arg
|
||||||
"getty", // serice
|
"getty", // serice
|
||||||
@ -64,6 +67,7 @@
|
|||||||
"gptfdisk", // package
|
"gptfdisk", // package
|
||||||
"headlessly", // word
|
"headlessly", // word
|
||||||
"hostbus", // cli arg
|
"hostbus", // cli arg
|
||||||
|
"hostfwd", // cli arg
|
||||||
"hostiocache", // virtual box
|
"hostiocache", // virtual box
|
||||||
"hostport", // cli arg
|
"hostport", // cli arg
|
||||||
"inetutils", // package
|
"inetutils", // package
|
||||||
@ -74,6 +78,7 @@
|
|||||||
"keyformat", // zfs
|
"keyformat", // zfs
|
||||||
"keylocation", // zfs
|
"keylocation", // zfs
|
||||||
"keystatus", // zfs
|
"keystatus", // zfs
|
||||||
|
"kmod", // linux
|
||||||
"lazytime", // f2fs
|
"lazytime", // f2fs
|
||||||
"libubootenv", // package
|
"libubootenv", // package
|
||||||
"logbias", // zfs
|
"logbias", // zfs
|
||||||
@ -86,6 +91,7 @@
|
|||||||
"mktemp", // program / function
|
"mktemp", // program / function
|
||||||
"modifyvm", // virtual box
|
"modifyvm", // virtual box
|
||||||
"mountpoint", // program / function
|
"mountpoint", // program / function
|
||||||
|
"mtab", // linux
|
||||||
"namespacing", // word
|
"namespacing", // word
|
||||||
"netbootxyz", // option
|
"netbootxyz", // option
|
||||||
"netdev", // cli arg
|
"netdev", // cli arg
|
||||||
@ -100,6 +106,7 @@
|
|||||||
"noexec", // mount option
|
"noexec", // mount option
|
||||||
"nofail", // cli arg
|
"nofail", // cli arg
|
||||||
"nographic", // cli arg
|
"nographic", // cli arg
|
||||||
|
"noheadings", // cli arg
|
||||||
"nosuid", // mount option
|
"nosuid", // mount option
|
||||||
"oneshot", // systemd
|
"oneshot", // systemd
|
||||||
"ostype", // virtual box
|
"ostype", // virtual box
|
||||||
@ -107,6 +114,7 @@
|
|||||||
"partlabel", // linux
|
"partlabel", // linux
|
||||||
"partprobe", // program / function
|
"partprobe", // program / function
|
||||||
"pbkdf", // cli arg
|
"pbkdf", // cli arg
|
||||||
|
"pflash", // cli arg
|
||||||
"pkgs", // nix
|
"pkgs", // nix
|
||||||
"pname", // nix/abbr (package name)
|
"pname", // nix/abbr (package name)
|
||||||
"portcount", // virtual box
|
"portcount", // virtual box
|
||||||
@ -136,6 +144,7 @@
|
|||||||
"stdenv", // nix
|
"stdenv", // nix
|
||||||
"storageattach", // virtual box
|
"storageattach", // virtual box
|
||||||
"stty", // program / function
|
"stty", // program / function
|
||||||
|
"sublist", // Nix
|
||||||
"syncoid", // program
|
"syncoid", // program
|
||||||
"temproot", // abbr (temporary root (FS))
|
"temproot", // abbr (temporary root (FS))
|
||||||
"timesync", // systemd
|
"timesync", // systemd
|
||||||
@ -149,9 +158,11 @@
|
|||||||
"udev", // program
|
"udev", // program
|
||||||
"udevadm", // program
|
"udevadm", // program
|
||||||
"udptunnel", // program
|
"udptunnel", // program
|
||||||
|
"UEFI", // abbr
|
||||||
"uids", // abbr/plural (group IDs)
|
"uids", // abbr/plural (group IDs)
|
||||||
"unencrypted", // ~= not encrypted / decrypted
|
"unencrypted", // ~= not encrypted / decrypted
|
||||||
"upperdir", // mount overlay option
|
"upperdir", // mount overlay option
|
||||||
|
"upstreamed", // word
|
||||||
"urandom", // linux
|
"urandom", // linux
|
||||||
"vboxusers", // virtual box
|
"vboxusers", // virtual box
|
||||||
"vdev", "vdevs", // zfs
|
"vdev", "vdevs", // zfs
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
}; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@ { self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let
|
}; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@ { self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let
|
||||||
|
|
||||||
systemsFlake = lib.wip.mkSystemsFlake (rec {
|
systemsFlake = lib.wip.mkSystemsFlake (rec {
|
||||||
#systems = { dir = "${inputs.self.outPath}/hosts"; exclude = [ ]; };
|
#systems = { dir = "${inputs.self}/hosts"; exclude = [ ]; }; # (implicit)
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
scripts = (lib.attrValues lib.wip.setup-scripts) ++ [ ./example/install.sh.md ];
|
scripts = (lib.attrValues lib.wip.setup-scripts) ++ [ ./example/install.sh.md ];
|
||||||
});
|
});
|
||||||
@ -32,5 +32,5 @@ in [ # Run »nix flake show --allow-import-from-derivation« to see what this me
|
|||||||
packages = lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays;
|
packages = lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays;
|
||||||
defaultPackage = systemsFlake.packages.${localSystem}.all-systems;
|
defaultPackage = systemsFlake.packages.${localSystem}.all-systems;
|
||||||
}))
|
}))
|
||||||
{ patches = import "${inputs.self.outPath}/patches" "${inputs.self.outPath}/patches" inputs; }
|
{ patches = import "${inputs.self}/patches" "${inputs.self}/patches" inputs; }
|
||||||
]); }
|
]); }
|
||||||
|
@ -121,5 +121,7 @@ in { imports = [ ({ ## Hardware
|
|||||||
wip.services.dropbear.enable = true;
|
wip.services.dropbear.enable = true;
|
||||||
#wip.services.dropbear.rootKeys = [ ''${lib.readFile "${dirname}/....pub"}'' ];
|
#wip.services.dropbear.rootKeys = [ ''${lib.readFile "${dirname}/....pub"}'' ];
|
||||||
|
|
||||||
|
#wip.fs.disks.devices.primary.gptOffset = 64;
|
||||||
|
#wip.fs.disks.devices.primary.size = "250059096K"; # 256GB Intel H10
|
||||||
|
|
||||||
}) ]; }
|
}) ]; }
|
||||||
|
@ -22,9 +22,15 @@ in rec {
|
|||||||
# Lists will be declared as bash arrays, attribute sets will be declared as associative arrays using »asBashDict«.
|
# Lists will be declared as bash arrays, attribute sets will be declared as associative arrays using »asBashDict«.
|
||||||
# Bash does not support any nested data structures. Lists or attrsets in within lists or attrsets are therefore (recursively) encoded and escaped as strings, such that calling »eval« on them is safe if (but only if) they are known to be encoded from nested lists/attrsets. Example: »eval 'declare -A fs='"@{config.fileSystems['/']}" ; root=${fs[device]}«.
|
# Bash does not support any nested data structures. Lists or attrsets in within lists or attrsets are therefore (recursively) encoded and escaped as strings, such that calling »eval« on them is safe if (but only if) they are known to be encoded from nested lists/attrsets. Example: »eval 'declare -A fs='"@{config.fileSystems['/']}" ; root=${fs[device]}«.
|
||||||
# Any other value (functions), and things that »builtins.toString« doesn't like, will throw here.
|
# Any other value (functions), and things that »builtins.toString« doesn't like, will throw here.
|
||||||
substituteImplicit = args@{ pkgs, scripts, context, helpers ? { }, trace ? (m: v: v), }: let
|
substituteImplicit = args@{
|
||||||
|
scripts, # List of paths to scripts to process and then source in the returned script. Can also be an attrset, of which (only) the values will be used, and each script may also be an attrset »{ name; text; }« instead of a path.
|
||||||
|
context, # The root attrset for the resolution of substitutions.
|
||||||
|
pkgs, # Instantiated »nixpkgs«, as fallback location for helpers, and to grab »writeScript« etc from.
|
||||||
|
helpers ? { }, # Attrset of (highest priority) helper functions.
|
||||||
|
trace ? (m: v: v), # Function that gets called with the names and values as they are processed. Pass »builtins.trace« for debugging, esp. when evaluating one of the accessed values fails.
|
||||||
|
}: let
|
||||||
scripts = map (source: rec {
|
scripts = map (source: rec {
|
||||||
text = builtins.readFile source; inherit source;
|
text = if builtins.isAttrs source then source.text else builtins.readFile source; name = if builtins.isAttrs source then source.name else builtins.baseNameOf source;
|
||||||
parsed = builtins.split ''@\{([#!]?)([a-zA-Z][a-zA-Z0-9_.-]*[a-zA-Z0-9](![a-zA-Z][a-zA-Z0-9_.-]*[a-zA-Z0-9])?)([:*@\[#%/^,\}])'' text; # (first part of a bash parameter expansion, with »@« instead of »$«)
|
parsed = builtins.split ''@\{([#!]?)([a-zA-Z][a-zA-Z0-9_.-]*[a-zA-Z0-9](![a-zA-Z][a-zA-Z0-9_.-]*[a-zA-Z0-9])?)([:*@\[#%/^,\}])'' text; # (first part of a bash parameter expansion, with »@« instead of »$«)
|
||||||
}) (if builtins.isAttrs args.scripts then builtins.attrValues args.scripts else args.scripts);
|
}) (if builtins.isAttrs args.scripts then builtins.attrValues args.scripts else args.scripts);
|
||||||
decls = lib.unique (map (match: builtins.elemAt match 1) (builtins.filter builtins.isList (builtins.concatMap (script: script.parsed) scripts)));
|
decls = lib.unique (map (match: builtins.elemAt match 1) (builtins.filter builtins.isList (builtins.concatMap (script: script.parsed) scripts)));
|
||||||
@ -56,7 +62,7 @@ in rec {
|
|||||||
); in trace final final)) decls));
|
); in trace final final)) decls));
|
||||||
in ''
|
in ''
|
||||||
source ${vars}
|
source ${vars}
|
||||||
${lib.concatMapStringsSep "\n" (script: "source ${pkgs.writeScript (builtins.baseNameOf script.source) (
|
${lib.concatMapStringsSep "\n" (script: "source ${pkgs.writeScript script.name (
|
||||||
lib.concatMapStringsSep "" (seg: if builtins.isString seg then seg else (
|
lib.concatMapStringsSep "" (seg: if builtins.isString seg then seg else (
|
||||||
"$"+"{"+(builtins.head seg)+(builtins.replaceStrings [ "." "!" "-" ] [ "_" "1" "0" ] (builtins.elemAt seg 1))+(toString (builtins.elemAt seg 3))
|
"$"+"{"+(builtins.head seg)+(builtins.replaceStrings [ "." "!" "-" ] [ "_" "1" "0" ] (builtins.elemAt seg 1))+(toString (builtins.elemAt seg 3))
|
||||||
)) script.parsed
|
)) script.parsed
|
||||||
|
@ -46,13 +46,15 @@ function partition-disks { { # 1: diskPaths
|
|||||||
|
|
||||||
local name ; for name in "@{!config.wip.fs.disks.devices[@]}" ; do
|
local name ; for name in "@{!config.wip.fs.disks.devices[@]}" ; do
|
||||||
if [[ ! ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name not provided" ; exit 1 ; fi
|
if [[ ! ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name not provided" ; exit 1 ; fi
|
||||||
|
eval 'local -A disk='"@{config.wip.fs.disks.devices[$name]}"
|
||||||
if [[ ${blockDevs[$name]} != /dev/* ]] ; then
|
if [[ ${blockDevs[$name]} != /dev/* ]] ; then
|
||||||
local outFile=${blockDevs[$name]} ; ( set -eu
|
local outFile=${blockDevs[$name]} &&
|
||||||
eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}"
|
install -o root -g root -m 640 -T /dev/null "$outFile" && truncate -s "${disk[size]}" "$outFile" &&
|
||||||
install -o root -g root -m 640 -T /dev/null "$outFile" && truncate -s "${disk[size]}" "$outFile"
|
blockDevs[$name]=$(losetup --show -f "$outFile") && prepend_trap "losetup -d '${blockDevs[$name]}'" EXIT # NOTE: this must not be inside a sub-shell!
|
||||||
) && blockDevs[$name]=$(losetup --show -f "$outFile") && prepend_trap "losetup -d '${blockDevs[$name]}'" EXIT # NOTE: this must not be inside a sub-shell!
|
|
||||||
else
|
else
|
||||||
if [[ ! "$(blockdev --getsize64 "${blockDevs[$name]}")" ]] ; then echo "Block device $name does not exist at ${blockDevs[$name]}" ; exit 1 ; fi
|
local size=$( blockdev --getsize64 "${blockDevs[$name]}" || : )
|
||||||
|
if [[ ! $size ]] ; then echo "Block device $name does not exist at ${blockDevs[$name]}" ; exit 1 ; fi
|
||||||
|
if [[ $size != "${disk[size]}" ]] ; then echo "Block device ${blockDevs[$name]}'s size $size does not match the size ${disk[size]} declared for $name" ; exit 1 ; fi
|
||||||
blockDevs[$name]=$(realpath "${blockDevs[$name]}")
|
blockDevs[$name]=$(realpath "${blockDevs[$name]}")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@ -67,10 +69,12 @@ function partition-disks { { # 1: diskPaths
|
|||||||
for name in "@{!config.wip.fs.disks.devices[@]}" ; do
|
for name in "@{!config.wip.fs.disks.devices[@]}" ; do
|
||||||
eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}"
|
eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}"
|
||||||
if [[ ${disk[serial]:-} ]] ; then
|
if [[ ${disk[serial]:-} ]] ; then
|
||||||
actual=$(udevadm info --query=property --name="$blockDev" | grep -oP 'ID_SERIAL_SHORT=\K.*')
|
actual=$( udevadm info --query=property --name="$blockDev" | grep -oP 'ID_SERIAL_SHORT=\K.*' || echo '<none>' )
|
||||||
if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev does not match the serial declared for ${disk[name]}" ; exit 1 ; fi
|
if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev's serial ($actual) does not match the serial (${disk[serial]}) declared for ${disk[name]}" ; exit 1 ; fi
|
||||||
fi
|
fi
|
||||||
partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}"
|
# can (and probably should) restore the backup:
|
||||||
|
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup "${blockDevs[${disk[name]}]}" >$beQuiet )
|
||||||
|
#partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}"
|
||||||
done
|
done
|
||||||
@{native.parted}/bin/partprobe "${blockDevs[@]}"
|
@{native.parted}/bin/partprobe "${blockDevs[@]}"
|
||||||
@{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet
|
@{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet
|
||||||
@ -86,13 +90,25 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
|||||||
devSize=${3:-$( @{native.util-linux}/bin/blockdev --getsize64 "$blockDev" )}
|
devSize=${3:-$( @{native.util-linux}/bin/blockdev --getsize64 "$blockDev" )}
|
||||||
|
|
||||||
declare -a sgdisk=( --zap-all ) # delete existing part tables
|
declare -a sgdisk=( --zap-all ) # delete existing part tables
|
||||||
|
if [[ ${disk[gptOffset]} != 0 ]] ; then
|
||||||
|
sgdisk+=( --move-main-table=$(( 2 + ${disk[gptOffset]} )) ) # this is incorrectly documented as --adjust-main-table in the man pages (at least versions 1.05 to 1.09 incl)
|
||||||
|
sgdisk+=( --move-secondary-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) )
|
||||||
|
fi
|
||||||
|
sgdisk+=( --disk-guid="${disk[guid]}" )
|
||||||
|
|
||||||
for partDecl in "@{config.wip.fs.disks.partitionList[@]}" ; do
|
for partDecl in "@{config.wip.fs.disks.partitionList[@]}" ; do
|
||||||
eval 'declare -A part='"$partDecl"
|
eval 'declare -A part='"$partDecl"
|
||||||
if [[ ${part[disk]} != "${disk[name]}" ]] ; then continue ; fi
|
if [[ ${part[disk]} != "${disk[name]}" ]] ; then continue ; fi
|
||||||
if [[ ${part[size]:-} =~ ^[0-9]+%$ ]] ; then
|
if [[ ${part[size]:-} =~ ^[0-9]+%$ ]] ; then
|
||||||
part[size]=$(( $devSize / 1024 * ${part[size]:0:(-1)} / 100 ))K
|
part[size]=$(( $devSize / 1024 * ${part[size]:0:(-1)} / 100 ))K
|
||||||
fi
|
fi
|
||||||
sgdisk+=( -a "${part[alignment]:-${disk[alignment]}}" -n "${part[index]:-0}":"${part[position]}":+"${part[size]:-}" -t 0:"${part[type]}" -c 0:"${part[name]}" )
|
sgdisk+=(
|
||||||
|
--set-alignment="${part[alignment]:-${disk[alignment]}}"
|
||||||
|
--new="${part[index]:-0}":"${part[position]}":+"${part[size]:-}"
|
||||||
|
--partition-guid=0:"${part[guid]}"
|
||||||
|
--typecode=0:"${part[type]}"
|
||||||
|
--change-name=0:"${part[name]}"
|
||||||
|
)
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ${disk[mbrParts]:-} ]] ; then
|
if [[ ${disk[mbrParts]:-} ]] ; then
|
||||||
|
@ -67,8 +67,12 @@ function run-qemu {( set -eu # 1: diskImages
|
|||||||
qemu+=( -drive format=raw,file="${decl/*=/}" ) #,if=none,index=0,media=disk,id=disk0 -device "virtio-blk-pci,drive=disk0,disable-modern=on,disable-legacy=off" )
|
qemu+=( -drive format=raw,file="${decl/*=/}" ) #,if=none,index=0,media=disk,id=disk0 -device "virtio-blk-pci,drive=disk0,disable-modern=on,disable-legacy=off" )
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then
|
if [[ @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots something much like a classic BIOS?
|
||||||
qemu+=( -bios @{pkgs.OVMF.fd}/FV/OVMF.fd ) # UEFI. Otherwise it boots something much like a classic BIOS?
|
#qemu+=( -bios @{pkgs.OVMF.fd}/FV/OVMF.fd ) # This works, but is a legacy fallback that stores the EFI vars in /NvVars on the EFI partition (which is really bad).
|
||||||
|
qemu+=( -drive file=@{pkgs.OVMF.fd}/FV/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on )
|
||||||
|
qemu+=( -drive file=/tmp/qemu-@{config.networking.hostName}-VARS.fd,if=pflash,format=raw,unit=1 )
|
||||||
|
if [[ ! -e /tmp/qemu-@{config.networking.hostName}-VARS.fd ]] ; then cat @{pkgs.OVMF.fd}/FV/OVMF_VARS.fd > /tmp/qemu-@{config.networking.hostName}-VARS.fd ; fi
|
||||||
|
# https://lists.gnu.org/archive/html/qemu-discuss/2018-04/msg00045.html
|
||||||
fi
|
fi
|
||||||
if [[ @{config.preface.hardware} == aarch64 ]] ; then
|
if [[ @{config.preface.hardware} == aarch64 ]] ; then
|
||||||
qemu+=( -kernel @{config.system.build.kernel}/Image -initrd @{config.system.build.initialRamdisk}/initrd -append "$(echo -n "@{config.boot.kernelParams[@]}")" )
|
qemu+=( -kernel @{config.system.build.kernel}/Image -initrd @{config.system.build.initialRamdisk}/initrd -append "$(echo -n "@{config.boot.kernelParams[@]}")" )
|
||||||
@ -79,7 +83,7 @@ function run-qemu {( set -eu # 1: diskImages
|
|||||||
fi ; done
|
fi ; done
|
||||||
|
|
||||||
if [[ ! ${args[no-nat]:-} ]] ; then
|
if [[ ! ${args[no-nat]:-} ]] ; then
|
||||||
qemu+=( -nic user,model=virtio-net-pci ) # NATed, IPs: 10.0.2.15+/32, gateway: 10.0.2.2
|
qemu+=( -nic user,model=virtio-net-pci${args[nat-fw]:+,hostfwd=tcp::${args[nat-fw]//,/,hostfwd=tcp::}} ) # NATed, IPs: 10.0.2.15+/32, gateway: 10.0.2.2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# TODO: network bridging:
|
# TODO: network bridging:
|
||||||
@ -91,7 +95,11 @@ function run-qemu {( set -eu # 1: diskImages
|
|||||||
qemu+=( -usb -device usb-host,hostbus="${decl/-*/}",hostport="${decl/*-/}" )
|
qemu+=( -usb -device usb-host,hostbus="${decl/-*/}",hostport="${decl/*-/}" )
|
||||||
done ; fi
|
done ; fi
|
||||||
|
|
||||||
|
if [[ ${args[dry-run]:-} ]] ; then
|
||||||
|
( echo "${qemu[@]}" )
|
||||||
|
else
|
||||||
( set -x ; "${qemu[@]}" )
|
( set -x ; "${qemu[@]}" )
|
||||||
|
fi
|
||||||
|
|
||||||
# https://askubuntu.com/questions/54814/how-can-i-ctrl-alt-f-to-get-to-a-tty-in-a-qemu-session
|
# https://askubuntu.com/questions/54814/how-can-i-ctrl-alt-f-to-get-to-a-tty-in-a-qemu-session
|
||||||
|
|
||||||
@ -107,7 +115,6 @@ function add-bootkey-to-keydev {( set -eu # 1: blockDev, 2?: hostHash
|
|||||||
</dev/urandom tr -dc 0-9a-f | head -c 512 >/dev/disk/by-partlabel/"$bootkeyPartlabel"
|
</dev/urandom tr -dc 0-9a-f | head -c 512 >/dev/disk/by-partlabel/"$bootkeyPartlabel"
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Tries to open and mount the systems keystore from its LUKS partition. If successful, adds the traps to close it when the parent shell exits.
|
## Tries to open and mount the systems keystore from its LUKS partition. If successful, adds the traps to close it when the parent shell exits.
|
||||||
# See »open-system«'s implementation for some example calls to this function.
|
# See »open-system«'s implementation for some example calls to this function.
|
||||||
function mount-keystore-luks { # ...: cryptsetupOptions
|
function mount-keystore-luks { # ...: cryptsetupOptions
|
||||||
|
@ -88,6 +88,13 @@ in rec {
|
|||||||
|
|
||||||
removeTailingNewline = string: if lastChar string == "\n" then builtins.substring 0 (builtins.stringLength string - 1) string else string;
|
removeTailingNewline = string: if lastChar string == "\n" then builtins.substring 0 (builtins.stringLength string - 1) string else string;
|
||||||
|
|
||||||
|
## Reproducibly generates a GUID by sha256-hashing a prefixed name. The result looks like a RFC 4122 GUID "generated by [SHA1] hashing a namespace identifier and name".
|
||||||
|
# E.g.: sha256guid "gpt-disk:primary:${hostname}" => "xxxxxxxx-xxxx-5xxx-8xxx-xxxxxxxxxxxx"
|
||||||
|
sha256guid = name: let
|
||||||
|
hash = builtins.hashString "sha256" "nixos-guid:${name}";
|
||||||
|
s = from: to: builtins.substring from (to - from) hash;
|
||||||
|
in "${s 0 8}-${s 8 12}-5${s 13 16}-8${s 17 20}-${s 20 32}";
|
||||||
|
|
||||||
|
|
||||||
## Math
|
## Math
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ Options to declare devices and partitions to be picked up by the installer scrip
|
|||||||
dirname: inputs: { config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let
|
dirname: inputs: { config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let
|
||||||
prefix = inputs.config.prefix;
|
prefix = inputs.config.prefix;
|
||||||
cfg = config.${prefix}.fs.disks;
|
cfg = config.${prefix}.fs.disks;
|
||||||
|
types.guid = lib.types.strMatching ''^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'';
|
||||||
in {
|
in {
|
||||||
|
|
||||||
options.${prefix} = { fs.disks = {
|
options.${prefix} = { fs.disks = {
|
||||||
@ -19,12 +20,14 @@ in {
|
|||||||
description = "Set of disk devices that this host will be installed on.";
|
description = "Set of disk devices that this host will be installed on.";
|
||||||
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
||||||
name = lib.mkOption { description = "Name that this device is being referred to as in other places."; type = lib.types.str; default = name; readOnly = true; };
|
name = lib.mkOption { description = "Name that this device is being referred to as in other places."; type = lib.types.str; default = name; readOnly = true; };
|
||||||
size = lib.mkOption { description = "The size of the image to create, when using an image for this device, as argument to »truncate -s«."; type = lib.types.str; default = "16G"; };
|
guid = lib.mkOption { description = "GPT disk GUID of the disk."; type = types.guid; default = lib.wip.sha256guid ("gpt-disk:${name}"+":${config.networking.hostName}"); };
|
||||||
serial = lib.mkOption { description = "Serial number of the specific hardware device to use. If set the device path passed to the installer must point to the device with this serial. Use »udevadm info --query=property --name=$DISK | grep -oP 'ID_SERIAL_SHORT=\K.*'« to get the serial."; type = lib.types.nullOr lib.types.str; default = null; };
|
size = lib.mkOption { description = "The size of the disk, either as number in bytes or as argument to »parseSizeSuffix«. When installing to a physical device, its size must match; images are created with this size."; type = lib.types.either lib.types.ints.unsigned lib.types.str; apply = lib.wip.parseSizeSuffix; default = "16G"; };
|
||||||
|
serial = lib.mkOption { description = "Serial number of the specific hardware device to use. If set the device path passed to the installer must point to the device with this serial. Use » udevadm info --query=property --name=$DISK | grep -oP 'ID_SERIAL_SHORT=\K.*' || echo '<none>' « to get the serial."; type = lib.types.nullOr lib.types.str; default = null; };
|
||||||
alignment = lib.mkOption { description = "Default alignment quantifier for partitions on this device. Should be at least the optimal physical write size of the device, but going larger at worst wastes this many times the number of partitions disk sectors."; type = lib.types.int; default = 16384; };
|
alignment = lib.mkOption { description = "Default alignment quantifier for partitions on this device. Should be at least the optimal physical write size of the device, but going larger at worst wastes this many times the number of partitions disk sectors."; type = lib.types.int; default = 16384; };
|
||||||
|
gptOffset = lib.mkOption { description = "Offset of the partition tables, inwards from where (third / 2nd last) they usually are."; type = lib.types.ints.unsigned; default = 0; };
|
||||||
mbrParts = lib.mkOption { description = "Up to three colon-separated (GPT) partition numbers that will be made available in a hybrid MBR."; type = lib.types.nullOr lib.types.str; default = null; };
|
mbrParts = lib.mkOption { description = "Up to three colon-separated (GPT) partition numbers that will be made available in a hybrid MBR."; type = lib.types.nullOr lib.types.str; default = null; };
|
||||||
extraFDiskCommands = lib.mkOption { description = "»fdisk« menu commands to run against the hybrid MBR. ».mbrParts« 1[2[3]] exist as transfers from the GPT table, and part4 is the protective GPT part. Can do things like marking partitions as bootable or changing their type. Spaces and end-of-line »#«-prefixed comments are removed, new lines and »;« also mean return."; type = lib.types.lines; default = ""; example = ''
|
extraFDiskCommands = lib.mkOption { description = "»fdisk« menu commands to run against the hybrid MBR. ».mbrParts« 1[2[3]] exist as transfers from the GPT table, and part4 is the protective GPT part. Can do things like marking partitions as bootable or changing their type. Spaces and end-of-line »#«-prefixed comments are removed, new lines and »;« also mean return."; type = lib.types.lines; default = ""; example = ''
|
||||||
t;1;b # type ; part1 ; W95 FAT32
|
t;1;c # type ; part1 ; W95 FAT32 (LBA)
|
||||||
a;1 # active/boot ; part1
|
a;1 # active/boot ; part1
|
||||||
''; };
|
''; };
|
||||||
}; })));
|
}; })));
|
||||||
@ -35,6 +38,7 @@ in {
|
|||||||
description = "Set of disks disk partitions that the system will need/use. Partitions will be created on their respective ».disk«s in ».order« using »sgdisk -n X:+0+$size«.";
|
description = "Set of disks disk partitions that the system will need/use. Partitions will be created on their respective ».disk«s in ».order« using »sgdisk -n X:+0+$size«.";
|
||||||
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
||||||
name = lib.mkOption { description = "Name/partlabel that this partition can be referred to as once created."; type = lib.types.str; default = name; readOnly = true; };
|
name = lib.mkOption { description = "Name/partlabel that this partition can be referred to as once created."; type = lib.types.str; default = name; readOnly = true; };
|
||||||
|
guid = lib.mkOption { description = "GPT partition GUID of the partition."; type = types.guid; default = lib.wip.sha256guid ("gpt-part:${name}"+":${config.networking.hostName}"); };
|
||||||
disk = lib.mkOption { description = "Name of the disk that this partition resides on, which will automatically be declared with default options."; type = lib.types.str; default = "primary"; };
|
disk = lib.mkOption { description = "Name of the disk that this partition resides on, which will automatically be declared with default options."; type = lib.types.str; default = "primary"; };
|
||||||
type = lib.mkOption { description = "»gdisk« partition type of this partition."; type = lib.types.str; };
|
type = lib.mkOption { description = "»gdisk« partition type of this partition."; type = lib.types.str; };
|
||||||
size = lib.mkOption { description = "Partition size, either as integer suffixed with »K«, »M«, »G«, etc for sizes in XiB, or an integer suffixed with »%« for that portion of the size of the actual disk the partition gets created on. Or »null« to fill the remaining disk space."; type = lib.types.nullOr lib.types.str; default = null; };
|
size = lib.mkOption { description = "Partition size, either as integer suffixed with »K«, »M«, »G«, etc for sizes in XiB, or an integer suffixed with »%« for that portion of the size of the actual disk the partition gets created on. Or »null« to fill the remaining disk space."; type = lib.types.nullOr lib.types.str; default = null; };
|
||||||
@ -63,19 +67,19 @@ in {
|
|||||||
fs.disks.devices = lib.wip.mapMerge (name: { ${name} = { }; }) (lib.catAttrs "disk" config.${prefix}.fs.disks.partitionList);
|
fs.disks.devices = lib.wip.mapMerge (name: { ${name} = { }; }) (lib.catAttrs "disk" config.${prefix}.fs.disks.partitionList);
|
||||||
|
|
||||||
fs.disks.partitioning = let
|
fs.disks.partitioning = let
|
||||||
partition-disk = builtins.toFile "partition-disk" (lib.wip.extractBashFunction (builtins.readFile "${dirname}/../../lib/setup-scripts/disk.sh") "partition-disk");
|
partition-disk = { name = "partition-disk"; text = lib.wip.extractBashFunction (builtins.readFile lib.wip.setup-scripts.disk) "partition-disk"; };
|
||||||
esc = lib.escapeShellArg;
|
esc = lib.escapeShellArg;
|
||||||
in pkgs.runCommand "partitioning-${config.networking.hostName}" { } ''
|
in pkgs.runCommand "partitioning-${config.networking.hostName}" { } ''
|
||||||
${lib.wip.substituteImplicit { inherit pkgs; scripts = [ partition-disk ]; context = { inherit config; native = pkgs; }; }} # inherit (builtins) trace;
|
${lib.wip.substituteImplicit { inherit pkgs; scripts = [ partition-disk ]; context = { inherit config; native = pkgs; }; }} # inherit (builtins) trace;
|
||||||
mkdir $out ; beQuiet=/dev/stdout
|
mkdir $out ; beQuiet=/dev/stdout
|
||||||
${lib.concatStrings (lib.mapAttrsToList (name: disk: ''
|
${lib.concatStrings (lib.mapAttrsToList (name: disk: ''
|
||||||
name=${esc name} ; img=$name.img
|
name=${esc name} ; img=$name.img
|
||||||
${pkgs.coreutils}/bin/truncate -s ${esc disk.size} $img
|
${pkgs.coreutils}/bin/truncate -s ${esc disk.size} "$img"
|
||||||
partition-disk $name $img ${toString (lib.wip.parseSizeSuffix disk.size)}
|
partition-disk "$name" "$img" ${toString (lib.wip.parseSizeSuffix disk.size)}
|
||||||
${pkgs.gptfdisk}/bin/sgdisk --backup=$out/$name.backup $img
|
${pkgs.gptfdisk}/bin/sgdisk --backup=$out/"$name".backup "$img"
|
||||||
${pkgs.gptfdisk}/bin/sgdisk --print $img >$out/$name.gpt
|
${pkgs.gptfdisk}/bin/sgdisk --print "$img" >$out/"$name".gpt
|
||||||
${if disk.mbrParts != null then ''
|
${if disk.mbrParts != null then ''
|
||||||
${pkgs.util-linux}/bin/fdisk --type mbr --list $img >$out/$name.mbr
|
${pkgs.util-linux}/bin/fdisk --type mbr --list "$img" >$out/"$name".mbr
|
||||||
'' else ""}
|
'' else ""}
|
||||||
'') cfg.devices)}
|
'') cfg.devices)}
|
||||||
'';
|
'';
|
||||||
|
@ -15,7 +15,7 @@ What keys are used for is derived from the attribute name in the `.keys` specifi
|
|||||||
* Keys in `home/` are used as composites for home directory encryption, where the second and only other path label us the user name. TODO: this is not completely implemented yet.
|
* Keys in `home/` are used as composites for home directory encryption, where the second and only other path label us the user name. TODO: this is not completely implemented yet.
|
||||||
|
|
||||||
The attribute value in the `.keys` keys specification dictates how the key is acquired, primarily initially during installation, but (depending on the keys usage) also during boot unlocking, etc.
|
The attribute value in the `.keys` keys specification dictates how the key is acquired, primarily initially during installation, but (depending on the keys usage) also during boot unlocking, etc.
|
||||||
The format of the key specification is `method[=args]`, where `method` is the suffix of a bash function call `add-key-<method>` (the default functions are in [`add-key.sh`](../../lib/setup-scripts/add-key.sh), but others could be added to the installer), and `args` is the second argument to the respective function (often a `:` separated list of arguments, but some methods don't need any arguments at all).
|
The format of the key specification is `method[=args]`, where `method` is the suffix of a bash function call `gen-key-<method>` (the default functions are in [`add-key.sh`](../../lib/setup-scripts/add-key.sh), but others could be added to the installer), and `args` is the second argument to the respective function (often a `:` separated list of arguments, but some methods don't need any arguments at all).
|
||||||
Most key generation methods only make sense in some key usage contexts. A `random` key is impossible to provide to unlock the keystore (which it is stored in), but is well suited to unlock other devices (if the keystore has backups (TODO!)); conversely a USB-partition can be used to headlessly unlock the keystore, but would be redundant for any further devices, as it would also be copied in the keystore.
|
Most key generation methods only make sense in some key usage contexts. A `random` key is impossible to provide to unlock the keystore (which it is stored in), but is well suited to unlock other devices (if the keystore has backups (TODO!)); conversely a USB-partition can be used to headlessly unlock the keystore, but would be redundant for any further devices, as it would also be copied in the keystore.
|
||||||
|
|
||||||
If the module is `enable`d, a partition and LUKS device `keystore-...` gets configured and the contents of the installation time keystore is copied to it (in its entirety, including intermediate or derived keys and those unlocking the keystore itself (TODO: this could be optimized)).
|
If the module is `enable`d, a partition and LUKS device `keystore-...` gets configured and the contents of the installation time keystore is copied to it (in its entirety, including intermediate or derived keys and those unlocking the keystore itself (TODO: this could be optimized)).
|
||||||
|
@ -108,7 +108,7 @@ dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.sel
|
|||||||
mounts = lib.mkOption {
|
mounts = lib.mkOption {
|
||||||
description = "Locations (for »temp« in addition to »/«) where a ${desc} filesystem should be mounted. Some are declared by default but may be removed by setting them to »null«.";
|
description = "Locations (for »temp« in addition to »/«) where a ${desc} filesystem should be mounted. Some are declared by default but may be removed by setting them to »null«.";
|
||||||
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
type = lib.types.attrsOf (lib.types.nullOr (lib.types.submodule ({ name, ... }: { options = {
|
||||||
target = lib.mkOption { description = "Attribute name as the mount target path."; type = lib.types.addCheck lib.types.str (name: (builtins.match ''^/.*[^/]$'' name) != null); default = name; readOnly = true; };
|
target = lib.mkOption { description = "Attribute name as the mount target path."; type = lib.types.strMatching ''^/.*[^/]$''; default = name; readOnly = true; };
|
||||||
source = lib.mkOption { description = "Relative source path of the mount. (Irrelevant for »tmpfs«.)"; type = lib.types.str; default = builtins.substring 1 (builtins.stringLength name - 1) name; };
|
source = lib.mkOption { description = "Relative source path of the mount. (Irrelevant for »tmpfs«.)"; type = lib.types.str; default = builtins.substring 1 (builtins.stringLength name - 1) name; };
|
||||||
uid = lib.mkOption { description = "UID owning the mounted target."; type = lib.types.ints.unsigned; default = 0; };
|
uid = lib.mkOption { description = "UID owning the mounted target."; type = lib.types.ints.unsigned; default = 0; };
|
||||||
gid = lib.mkOption { description = "GID owning the mounted target."; type = lib.types.ints.unsigned; default = 0; };
|
gid = lib.mkOption { description = "GID owning the mounted target."; type = lib.types.ints.unsigned; default = 0; };
|
||||||
|
@ -120,7 +120,7 @@ in let module = {
|
|||||||
fi )'';
|
fi )'';
|
||||||
in {
|
in {
|
||||||
|
|
||||||
boot.initrd.postDeviceCommands = lib.mkIf (anyPool "autoApplyDuringBoot") (lib.mkAfter ''
|
boot.initrd.postDeviceCommands = lib.mkIf (anyPool "autoApplyDuringBoot") (lib.mkOrder 2000 ''
|
||||||
${ensure-datasets-for "autoApplyDuringBoot" "${extraUtils}/bin/zfs"}
|
${ensure-datasets-for "autoApplyDuringBoot" "${extraUtils}/bin/zfs"}
|
||||||
'');
|
'');
|
||||||
boot.initrd.supportedFilesystems = lib.mkIf (anyPool "autoApplyDuringBoot") [ "zfs" ];
|
boot.initrd.supportedFilesystems = lib.mkIf (anyPool "autoApplyDuringBoot") [ "zfs" ];
|
||||||
|
@ -17,28 +17,33 @@ in {
|
|||||||
options.${prefix} = { services.dropbear = {
|
options.${prefix} = { services.dropbear = {
|
||||||
enable = lib.mkEnableOption "dropbear SSH daemon";
|
enable = lib.mkEnableOption "dropbear SSH daemon";
|
||||||
socketActivation = lib.mkEnableOption "socket activation mode for dropbear";
|
socketActivation = lib.mkEnableOption "socket activation mode for dropbear";
|
||||||
rootKeys = lib.mkOption { default = [ ]; type = lib.types.listOf lib.types.str; description = "Literal lines to write to »/root/.ssh/authorized_keys«"; };
|
rootKeys = lib.mkOption { description = "Literal lines to write to »/root/.ssh/authorized_keys«"; default = [ ]; type = lib.types.listOf lib.types.str; };
|
||||||
|
hostKeys = lib.mkOption { description = "Location of the host key(s) to use. If empty, then a key(s) will be generated at »/etc/dropbear/dropbear_(ecdsa/rsa)_host_key« on first access to the server."; default = [ ]; type = lib.types.listOf lib.types.path; };
|
||||||
}; };
|
}; };
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable (lib.mkMerge [ ({
|
config = let
|
||||||
|
defaultArgs = lib.concatStringsSep "" [
|
||||||
|
"${pkgs.dropbear}/bin/dropbear"
|
||||||
|
" -p 22" # listen on TCP/22
|
||||||
|
(if cfg.hostKeys == [ ] then (
|
||||||
|
" -R" # generate host keys on connection
|
||||||
|
) else lib.concatMapStrings (path: (
|
||||||
|
" -r ${path}"
|
||||||
|
)) cfg.hostKeys)
|
||||||
|
];
|
||||||
|
|
||||||
|
in lib.mkIf cfg.enable (lib.mkMerge [ ({
|
||||||
environment.systemPackages = (with pkgs; [ dropbear ]);
|
environment.systemPackages = (with pkgs; [ dropbear ]);
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
#environment.etc."dropbear/.mkdir".text = "";
|
|
||||||
environment.etc.dropbear.source = "/run/user/0"; # allow for readonly /etc
|
|
||||||
|
|
||||||
}) (lib.mkIf (!cfg.socketActivation) {
|
}) (lib.mkIf (!cfg.socketActivation) {
|
||||||
|
|
||||||
systemd.services."dropbear" = {
|
systemd.services."dropbear" = {
|
||||||
description = "dropbear SSH server (listening)";
|
description = "dropbear SSH server (listening)";
|
||||||
wantedBy = [ "multi-user.target" ]; after = [ "network.target" ];
|
wantedBy = [ "multi-user.target" ]; after = [ "network.target" ];
|
||||||
serviceConfig.ExecStart = lib.concatStringsSep "" [
|
serviceConfig.PreExec = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/";
|
||||||
"${pkgs.dropbear}/bin/dropbear"
|
serviceConfig.ExecStart = defaultArgs + " -F -E"; # don't fork, use stderr
|
||||||
" -F -E" # don't fork, use stderr
|
|
||||||
" -p 22" # listen on TCP/22
|
|
||||||
" -R" # generate host keys on connection
|
|
||||||
#" -r .../dropbear_rsa_host_key"
|
|
||||||
];
|
|
||||||
#serviceConfig.PIDFile = "/var/run/dropbear.pid"; serviceConfig.Type = "forking"; after = [ "network.target" ]; # alternative to »-E -F« (?)
|
#serviceConfig.PIDFile = "/var/run/dropbear.pid"; serviceConfig.Type = "forking"; after = [ "network.target" ]; # alternative to »-E -F« (?)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,12 +60,11 @@ in {
|
|||||||
systemd.services."dropbear@" = {
|
systemd.services."dropbear@" = {
|
||||||
description = "dropbear SSH server (per-connection)";
|
description = "dropbear SSH server (per-connection)";
|
||||||
after = [ "syslog.target" ];
|
after = [ "syslog.target" ];
|
||||||
|
serviceConfig.PreExec = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/"; # or before socket?
|
||||||
serviceConfig.ExecStart = lib.concatStringsSep "" [
|
serviceConfig.ExecStart = lib.concatStringsSep "" [
|
||||||
"-" # for the most part ignore exit != 0
|
"-" # for the most part ignore exit != 0
|
||||||
"${pkgs.dropbear}/bin/dropbear"
|
defaultArgs
|
||||||
" -i" # handle a single connection on stdio
|
" -i" # handle a single connection on stdio
|
||||||
" -R" # generate host keys on connection
|
|
||||||
#" -r .../dropbear_rsa_host_key"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
27
overlays/gptfdisk.nix.md
Normal file
27
overlays/gptfdisk.nix.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
# sGdisk with Patches
|
||||||
|
|
||||||
|
GPT-FDisk patched to be able to move not only the primary, but also the backup partition table.
|
||||||
|
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
```nix
|
||||||
|
#*/# end of MarkDown, beginning of NixPkgs overlay:
|
||||||
|
dirname: inputs: final: prev: let
|
||||||
|
inherit (final) pkgs; inherit (inputs.self) lib;
|
||||||
|
in {
|
||||||
|
|
||||||
|
gptfdisk = prev.gptfdisk.overrideAttrs (old: rec {
|
||||||
|
pname = "gptfdisk";
|
||||||
|
version = "1.0.9";
|
||||||
|
src = builtins.fetchurl {
|
||||||
|
url = "https://downloads.sourceforge.net/gptfdisk/${pname}-${version}.tar.gz";
|
||||||
|
sha256 = "1hjh5m77fmfq5m44yy61kchv7mbfgx026aw3jy5qxszsjckavzns";
|
||||||
|
};
|
||||||
|
patches = [ # (don't include »old.patches«, as the only one was upstreamed)
|
||||||
|
../patches/gptfdisk-move-secondary-table.patch
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
191
patches/gptfdisk-move-secondary-table.patch
Normal file
191
patches/gptfdisk-move-secondary-table.patch
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
diff --git a/gpt.cc b/gpt.cc
|
||||||
|
index 76cd9ad..375c216 100644
|
||||||
|
--- a/gpt.cc
|
||||||
|
+++ b/gpt.cc
|
||||||
|
@@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) {
|
||||||
|
cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
|
||||||
|
allOK = 0;
|
||||||
|
} // if write failed
|
||||||
|
- } // if
|
||||||
|
+ } // if
|
||||||
|
if (!myDisk.Seek(secondHeader.partitionEntriesLBA))
|
||||||
|
allOK = 0;
|
||||||
|
if (allOK) {
|
||||||
|
@@ -1911,6 +1911,23 @@ int GPTData::MoveMainTable(uint64_t pteSector) {
|
||||||
|
return retval;
|
||||||
|
} // GPTData::MoveMainTable()
|
||||||
|
|
||||||
|
+// Change the start sector for the secondary partition table.
|
||||||
|
+// Returns 1 on success, 0 on failure
|
||||||
|
+int GPTData::MoveSecondaryTable(uint64_t pteSector) {
|
||||||
|
+ uint64_t pteSize = GetTableSizeInSectors();
|
||||||
|
+ int retval = 1;
|
||||||
|
+
|
||||||
|
+ if ((pteSector > FindLastUsedLBA()) && ((pteSector + pteSize) < diskSize)) {
|
||||||
|
+ secondHeader.partitionEntriesLBA = pteSector; // (RebuildSecondHeader actually replaces this with lastUsableLBA+1)
|
||||||
|
+ mainHeader.lastUsableLBA = secondHeader.partitionEntriesLBA - UINT64_C(1);
|
||||||
|
+ RebuildSecondHeader();
|
||||||
|
+ } else {
|
||||||
|
+ cerr << "Unable to set the secondary partition table's location to " << pteSector << "!\n";
|
||||||
|
+ retval = 0;
|
||||||
|
+ } // if/else
|
||||||
|
+ return retval;
|
||||||
|
+} // GPTData::MoveSecondaryTable()
|
||||||
|
+
|
||||||
|
// Blank the partition array
|
||||||
|
void GPTData::BlankPartitions(void) {
|
||||||
|
uint32_t i;
|
||||||
|
@@ -2285,7 +2302,7 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) {
|
||||||
|
} // GPTData::FindFirstAvailable()
|
||||||
|
|
||||||
|
// Returns the LBA of the start of the first partition on the disk (by
|
||||||
|
-// sector number), or 0 if there are no partitions defined.
|
||||||
|
+// sector number), or UINT64_MAX if there are no partitions defined.
|
||||||
|
uint64_t GPTData::FindFirstUsedLBA(void) {
|
||||||
|
uint32_t i;
|
||||||
|
uint64_t firstFound = UINT64_MAX;
|
||||||
|
@@ -2298,6 +2315,20 @@ uint64_t GPTData::FindFirstUsedLBA(void) {
|
||||||
|
return firstFound;
|
||||||
|
} // GPTData::FindFirstUsedLBA()
|
||||||
|
|
||||||
|
+// Returns the LBA of the end of the last partition on the disk (by
|
||||||
|
+// sector number), or 0 if there are no partitions defined.
|
||||||
|
+uint64_t GPTData::FindLastUsedLBA(void) {
|
||||||
|
+ uint32_t i;
|
||||||
|
+ uint64_t lastFound = 0;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < numParts; i++) {
|
||||||
|
+ if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() > lastFound)) {
|
||||||
|
+ lastFound = partitions[i].GetFirstLBA();
|
||||||
|
+ } // if
|
||||||
|
+ } // for
|
||||||
|
+ return lastFound;
|
||||||
|
+} // GPTData::FindLastUsedLBA()
|
||||||
|
+
|
||||||
|
// Finds the first available sector in the largest block of unallocated
|
||||||
|
// space on the disk. Returns 0 if there are no available blocks left
|
||||||
|
uint64_t GPTData::FindFirstInLargest(void) {
|
||||||
|
diff --git a/gpt.h b/gpt.h
|
||||||
|
index 5d19372..c272d65 100644
|
||||||
|
--- a/gpt.h
|
||||||
|
+++ b/gpt.h
|
||||||
|
@@ -142,6 +142,7 @@ public:
|
||||||
|
// Adjust GPT structures WITHOUT user interaction...
|
||||||
|
int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
|
||||||
|
int MoveMainTable(uint64_t pteSector);
|
||||||
|
+ int MoveSecondaryTable(uint64_t pteSector);
|
||||||
|
void BlankPartitions(void);
|
||||||
|
int DeletePartition(uint32_t partNum);
|
||||||
|
uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
|
||||||
|
@@ -158,7 +159,7 @@ public:
|
||||||
|
void RecomputeCHS(void);
|
||||||
|
int Align(uint64_t* sector);
|
||||||
|
void SetProtectiveMBR(BasicMBRData & newMBR) {protectiveMBR = newMBR;}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
// Return data about the GPT structures....
|
||||||
|
WhichToUse GetState(void) {return whichWasUsed;}
|
||||||
|
int GetPartRange(uint32_t* low, uint32_t* high);
|
||||||
|
@@ -181,6 +182,7 @@ public:
|
||||||
|
// Find information about free space
|
||||||
|
uint64_t FindFirstAvailable(uint64_t start = 0);
|
||||||
|
uint64_t FindFirstUsedLBA(void);
|
||||||
|
+ uint64_t FindLastUsedLBA(void);
|
||||||
|
uint64_t FindFirstInLargest(void);
|
||||||
|
uint64_t FindLastAvailable();
|
||||||
|
uint64_t FindLastInFree(uint64_t start, bool align = false);
|
||||||
|
diff --git a/gptcl.cc b/gptcl.cc
|
||||||
|
index 34c9421..f6517e4 100644
|
||||||
|
--- a/gptcl.cc
|
||||||
|
+++ b/gptcl.cc
|
||||||
|
@@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||||
|
int opt, numOptions = 0, saveData = 0, neverSaveData = 0;
|
||||||
|
int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0;
|
||||||
|
int byteSwapPartNum = 0;
|
||||||
|
- uint64_t low, high, startSector, endSector, sSize, mainTableLBA;
|
||||||
|
+ uint64_t low, high, startSector, endSector, sSize, mainTableLBA, secondTableLBA;
|
||||||
|
uint64_t temp; // temporary variable; free to use in any case
|
||||||
|
char *device;
|
||||||
|
string cmd, typeGUID, name;
|
||||||
|
@@ -94,7 +94,8 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||||
|
{"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"},
|
||||||
|
{"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
|
||||||
|
{"align-end", 'I', POPT_ARG_NONE, NULL, 'I', "align partition end points", ""},
|
||||||
|
- {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"},
|
||||||
|
+ {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "change the start sector of the main partition table", "sector"},
|
||||||
|
+ {"move-secondary-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the secondary partition table", "sector"},
|
||||||
|
{"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
|
||||||
|
{"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
|
||||||
|
{"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"},
|
||||||
|
@@ -117,6 +118,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||||
|
{"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""},
|
||||||
|
{"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""},
|
||||||
|
POPT_AUTOHELP { NULL, 0, 0, NULL, 0 }
|
||||||
|
+ // TODO: Incorrect(ly documented) (long) arguments are silently swallowed and seem to take the next argument with them!
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create popt context...
|
||||||
|
@@ -280,13 +282,21 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||||
|
alignEnd = true;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
- if (MoveMainTable(mainTableLBA)) {
|
||||||
|
- JustLooking(0);
|
||||||
|
- saveData = 1;
|
||||||
|
- } else {
|
||||||
|
- neverSaveData = 1;
|
||||||
|
- } // if/else
|
||||||
|
- break;
|
||||||
|
+ if (MoveMainTable(mainTableLBA)) {
|
||||||
|
+ JustLooking(0);
|
||||||
|
+ saveData = 1;
|
||||||
|
+ } else {
|
||||||
|
+ neverSaveData = 1;
|
||||||
|
+ } // if/else
|
||||||
|
+ break;
|
||||||
|
+ case 'k':
|
||||||
|
+ if (MoveSecondaryTable(secondTableLBA)) {
|
||||||
|
+ JustLooking(0);
|
||||||
|
+ saveData = 1;
|
||||||
|
+ } else {
|
||||||
|
+ neverSaveData = 1;
|
||||||
|
+ } // if/else
|
||||||
|
+ break;
|
||||||
|
case 'l':
|
||||||
|
LoadBackupFile(backupFile, saveData, neverSaveData);
|
||||||
|
free(backupFile);
|
||||||
|
diff --git a/sgdisk.8 b/sgdisk.8
|
||||||
|
index b966a13..dad877b 100644
|
||||||
|
--- a/sgdisk.8
|
||||||
|
+++ b/sgdisk.8
|
||||||
|
@@ -304,7 +304,7 @@ with the current final partition being aligned, and if \fBsgdisk\fR is asked
|
||||||
|
to create a partition in that space, then it will \fBnot\fR be end\-aligned.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
-.B \-j, \-\-adjust\-main\-table=sector
|
||||||
|
+.B \-j, \-\-move\-main\-table=sector
|
||||||
|
Adjust the location of the main partition table. This value is normally 2,
|
||||||
|
but it may need to be increased in some cases, such as when a
|
||||||
|
system\-on\-chip (SoC) is hard\-coded to read boot code from sector 2. I
|
||||||
|
diff --git a/sgdisk.html b/sgdisk.html
|
||||||
|
index 36a28bc..ec0f505 100644
|
||||||
|
--- a/sgdisk.html
|
||||||
|
+++ b/sgdisk.html
|
||||||
|
@@ -195,7 +195,7 @@ when using this option. The others require a partition number. The
|
||||||
|
<I>nand</I>, <I>xor</I>, <I>=</I>, <I>set</I>, <I>clear</I>, and
|
||||||
|
<I>toggle</I> options enable you to change the attribute bit value. The
|
||||||
|
<I>set</I>, <I>clear</I>, <I>toggle</I>, and <I>get</I> options work on a
|
||||||
|
-bit number; the others work on a hexadecimal bit mask. For example, type
|
||||||
|
+bit number; the others work on a hexadecimal bit mask. For example, type
|
||||||
|
<B>sgdisk -A 4:set:2 /dev/sdc</B> to set the bit 2 attribute (legacy BIOS
|
||||||
|
bootable) on partition 4 on <I>/dev/sdc</I>.
|
||||||
|
<P>
|
||||||
|
@@ -344,7 +344,7 @@ if the free space at the end of a disk is less than the alignment value,
|
||||||
|
with the current final partition being aligned, and if <B>sgdisk</B> is asked
|
||||||
|
to create a partition in that space, then it will <B>not</B> be end-aligned.
|
||||||
|
<P>
|
||||||
|
-<DT><B>-j, --adjust-main-table=sector</B>
|
||||||
|
+<DT><B>-j, --move-main-table=sector</B>
|
||||||
|
|
||||||
|
<DD>
|
||||||
|
Adjust the location of the main partition table. This value is normally 2,
|
Loading…
Reference in New Issue
Block a user