Skip to content

Commit

Permalink
install-grub.pl: use findmnt to get the boot filesystem
Browse files Browse the repository at this point in the history
The current implementation of GetFS uses heuristics to find the best
filesystem for the given boot path. Using findmnt is more robust.

Specifically, this fixes mounting tmpfs on / for [NixOS
impermanence](https://github.com/nix-community/impermanence).

Before this change, GetFs would incorrectly match /boot to the tmpfs
filesystem, resulting in a broken grub config.
  • Loading branch information
kira-bruneau committed Jan 16, 2025
1 parent ef56e77 commit 548802c
Showing 1 changed file with 5 additions and 45 deletions.
50 changes: 5 additions & 45 deletions nixos/modules/system/boot/loader/grub/install-grub.pl
Original file line number Diff line number Diff line change
Expand Up @@ -113,56 +113,16 @@ sub runCommand {
type => '$',
mount => '$',
});
sub PathInMount {
my ($path, $mount) = @_;
my @splitMount = split /\//, $mount;
my @splitPath = split /\//, $path;
if ($#splitPath < $#splitMount) {
return 0;
}
for (my $i = 0; $i <= $#splitMount; $i++) {
if ($splitMount[$i] ne $splitPath[$i]) {
return 0;
}
}
return 1;
}

# Figure out what filesystem is used for the directory with init/initrd/kernel files
sub GetFs {
my ($dir) = @_;
my $bestFs = Fs->new(device => "", type => "", mount => "");
foreach my $fs (read_file("/proc/self/mountinfo")) {
chomp $fs;
my @fields = split / /, $fs;
my $mountPoint = $fields[4];
my @mountOptions = split /,/, $fields[5];

# Skip the optional fields.
my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
my $fsType = $fields[$n];
my $device = $fields[$n + 1];
my @superOptions = split /,/, $fields[$n + 2];

# Skip the bind-mount on /nix/store.
next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions);
# Skip mount point generated by systemd-efi-boot-generator?
next if $fsType eq "autofs";

# Ensure this matches the intended directory
next unless PathInMount($dir, $mountPoint);

# Is it better than our current match?
if (length($mountPoint) > length($bestFs->mount)) {

# -d performs a stat, which can hang forever on network file systems,
# so we only make this call last, when it's likely that this is the mount point we need.
next unless -d $mountPoint;

$bestFs = Fs->new(device => $device, type => $fsType, mount => $mountPoint);
}
my ($status, @devInfo) = runCommand("@utillinux@/bin/findmnt", "-n", "-v", "-o", "SOURCE,FSTYPE,TARGET", "-T", @{[$dir]});
if ($status != 0 || $#devInfo != 1) {
die "Failed to get file system (returned $status) for @{[$dir]}";
}
return $bestFs;
my @fields = split /\s+/, $devInfo[0];
return Fs->new(device => $fields[0], type => $fields[1], mount => $fields[2]);
}
struct (Grub => {
path => '$',
Expand Down

0 comments on commit 548802c

Please sign in to comment.