Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Systemd stage 1 fsck #208269

Merged
merged 4 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions nixos/modules/system/boot/systemd/initrd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ in {
'';
};

managerEnvironment = mkOption {
type = with types; attrsOf (nullOr (oneOf [ str path package ]));
default = {};
example = { SYSTEMD_LOG_LEVEL = "debug"; };
description = lib.mdDoc ''
Environment variables of PID 1. These variables are
*not* passed to started units.
'';
};

contents = mkOption {
description = lib.mdDoc "Set of files that have to be linked into the initrd";
example = literalExpression ''
Expand Down Expand Up @@ -355,8 +365,11 @@ in {
less = "${pkgs.less}/bin/less";
mount = "${cfg.package.util-linux}/bin/mount";
umount = "${cfg.package.util-linux}/bin/umount";
fsck = "${cfg.package.util-linux}/bin/fsck";
};

managerEnvironment.PATH = "/bin:/sbin";

contents = {
"/init".source = "${cfg.package}/lib/systemd/systemd";
"/etc/systemd/system".source = stage1Units;
Expand All @@ -365,6 +378,7 @@ in {
[Manager]
DefaultEnvironment=PATH=/bin:/sbin ${optionalString (isBool cfg.emergencyAccess && cfg.emergencyAccess) "SYSTEMD_SULOGIN_FORCE=1"}
${cfg.extraConfig}
ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)}
'';

"/lib/modules".source = "${modulesClosure}/lib/modules";
Expand Down Expand Up @@ -447,21 +461,6 @@ in {
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);

# The unit in /run/systemd/generator shadows the unit in
# /etc/systemd/system, but will still apply drop-ins from
# /etc/systemd/system/foo.service.d/
#
# We need IgnoreOnIsolate, otherwise the Requires dependency of
# a mount unit on its makefs unit causes it to be unmounted when
# we isolate for switch-root. Use a dummy package so that
# generateUnits will generate drop-ins instead of unit files.
packages = [(pkgs.runCommand "dummy" {} ''
mkdir -p $out/etc/systemd/system
touch $out/etc/systemd/system/systemd-{makefs,growfs}@.service
'')];
services."systemd-makefs@" = lib.mkIf needMakefs { unitConfig.IgnoreOnIsolate = true; };
services."systemd-growfs@" = lib.mkIf needGrowfs { unitConfig.IgnoreOnIsolate = true; };

# make sure all the /dev nodes are set up
services.systemd-tmpfiles-setup-dev.wantedBy = ["sysinit.target"];

Expand Down
48 changes: 40 additions & 8 deletions nixos/modules/tasks/filesystems.nix
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ let
else if config.fsType == "reiserfs" then "-q"
else null;
in {
options = mkIf config.autoResize [ "x-nixos.autoresize" ];
options = mkMerge [
(mkIf config.autoResize [ "x-nixos.autoresize" ])
(mkIf (utils.fsNeededForBoot config) [ "x-initrd.mount" ])
];
formatOptions = mkIf (defaultFormatOptions != null) (mkDefault defaultFormatOptions);
};

Expand All @@ -155,27 +158,54 @@ let

makeFstabEntries =
let
fsToSkipCheck = [ "none" "bindfs" "btrfs" "zfs" "tmpfs" "nfs" "nfs4" "vboxsf" "glusterfs" "apfs" "9p" "cifs" "prl_fs" "vmhgfs" ];
fsToSkipCheck = [
"none"
"auto"
"overlay"
"iso9660"
"bindfs"
"udf"
"btrfs"
"zfs"
"tmpfs"
"bcachefs"
"nfs"
"nfs4"
"nilfs2"
"vboxsf"
"squashfs"
"glusterfs"
"apfs"
"9p"
"cifs"
"prl_fs"
"vmhgfs"
] ++ lib.optionals (!config.boot.initrd.checkJournalingFS) [
"ext3"
"ext4"
"reiserfs"
"xfs"
"jfs"
"f2fs"
];
isBindMount = fs: builtins.elem "bind" fs.options;
skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
# https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
in fstabFileSystems: { rootPrefix ? "", excludeChecks ? false, extraOpts ? (fs: []) }: concatMapStrings (fs:
in fstabFileSystems: { rootPrefix ? "", extraOpts ? (fs: []) }: concatMapStrings (fs:
(optionalString (isBindMount fs) (escape rootPrefix))
+ (if fs.device != null then escape fs.device
else if fs.label != null then "/dev/disk/by-label/${escape fs.label}"
else throw "No device specified for mount point ‘${fs.mountPoint}’.")
+ " " + escape (rootPrefix + fs.mountPoint)
+ " " + escape fs.mountPoint
+ " " + fs.fsType
+ " " + escape (builtins.concatStringsSep "," (fs.options ++ (extraOpts fs)))
+ " " + (optionalString (!excludeChecks)
("0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2")))
+ " 0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2")
+ "\n"
) fstabFileSystems;

initrdFstab = pkgs.writeText "initrd-fstab" (makeFstabEntries (filter utils.fsNeededForBoot fileSystems) {
rootPrefix = "/sysroot";
excludeChecks = true;
extraOpts = fs:
(optional fs.autoResize "x-systemd.growfs")
++ (optional fs.autoFormat "x-systemd.makefs");
Expand Down Expand Up @@ -328,7 +358,9 @@ in
)}
'';

boot.initrd.systemd.contents."/etc/fstab".source = initrdFstab;
boot.initrd.systemd.storePaths = [initrdFstab];
boot.initrd.systemd.managerEnvironment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
boot.initrd.systemd.services.initrd-parse-etc.environment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
Comment on lines +361 to +363
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ElvishJerricco Could you share the reason for removing /etc/fstab from the initrd filesystem? I found that this broke one of my custom initrd units, which had a mount <mountpoint> line. I have a workaround, but I'm curious to understand the reason for this change so that I can choose the best workaround in my config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Majiir /etc/fstab was never the right choice; the systemd tooling wants you to use root= and mount.usr= on the cmdline. The service initrd-parse-etc.service reads /sysroot/etc/fstab, and so does the systemd-fstab-generator, which is what we're overriding with this environment variable.


# Provide a target that pulls in all filesystems.
systemd.targets.fs =
Expand Down
16 changes: 9 additions & 7 deletions nixos/modules/virtualisation/qemu-vm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1084,15 +1084,17 @@ in
what = "overlay";
type = "overlay";
options = "lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/store,workdir=/sysroot/nix/.rw-store/work";
wantedBy = ["local-fs.target"];
before = ["local-fs.target"];
requires = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
after = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
unitConfig.IgnoreOnIsolate = true;
wantedBy = ["initrd-fs.target"];
before = ["initrd-fs.target"];
requires = ["rw-store.service"];
after = ["rw-store.service"];
unitConfig.RequiresMountsFor = "/sysroot/nix/.ro-store";
}];
services.rw-store = {
after = ["sysroot-nix-.rw\\x2dstore.mount"];
unitConfig.DefaultDependencies = false;
unitConfig = {
DefaultDependencies = false;
RequiresMountsFor = "/sysroot/nix/.rw-store";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "/bin/mkdir -p 0755 /sysroot/nix/.rw-store/store /sysroot/nix/.rw-store/work /sysroot/nix/store";
Expand Down
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ in {
freshrss-pgsql = handleTest ./freshrss-pgsql.nix {};
frr = handleTest ./frr.nix {};
fsck = handleTest ./fsck.nix {};
fsck-systemd-stage-1 = handleTest ./fsck.nix { systemdStage1 = true; };
ft2-clone = handleTest ./ft2-clone.nix {};
mimir = handleTest ./mimir.nix {};
garage = handleTest ./garage {};
Expand Down
12 changes: 11 additions & 1 deletion nixos/tests/fsck.nix
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{ system ? builtins.currentSystem
, config ? {}
, pkgs ? import ../.. { inherit system config; }
, systemdStage1 ? false
}:

import ./make-test-python.nix {
name = "fsck";

Expand All @@ -11,13 +17,17 @@ import ./make-test-python.nix {
autoFormat = true;
};
};

boot.initrd.systemd.enable = systemdStage1;
};

testScript = ''
machine.wait_for_unit("default.target")

with subtest("root fs is fsckd"):
machine.succeed("journalctl -b | grep 'fsck.ext4.*/dev/vda'")
machine.succeed("journalctl -b | grep '${if systemdStage1
then "fsck.*vda.*clean"
else "fsck.ext4.*/dev/vda"}'")

with subtest("mnt fs is fsckd"):
machine.succeed("journalctl -b | grep 'fsck.*/dev/vdb.*clean'")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,30 @@ Date: Thu, 1 May 2014 14:10:10 +0200
Subject: [PATCH] Look for fsck in the right place

---
src/fsck/fsck.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
src/fsck/fsck.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 595434ab57..374a4e6c76 100644
index 73c76fceea..d00cea7158 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -373,7 +373,7 @@ static int run(int argc, char *argv[]) {
@@ -352,6 +352,7 @@ static int run(int argc, char *argv[]) {
if (r == 0) {
char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1];
int progress_socket = -1;
+ _cleanup_free_ char *fsck_name = NULL;
const char *cmdline[9];
int i = 0;

@@ -372,7 +373,10 @@ static int run(int argc, char *argv[]) {
} else
dash_c[0] = 0;

- cmdline[i++] = "/sbin/fsck";
+ cmdline[i++] = "/run/current-system/sw/bin/fsck";
+ r = find_executable("fsck", &fsck_name);
+ if (r < 0)
+ return r;
+ cmdline[i++] = fsck_name;
cmdline[i++] = arg_repair;
cmdline[i++] = "-T";