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

install-grub.pl: support bindmounts #374398

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 1 addition & 2 deletions nixos/modules/system/boot/loader/grub/grub.nix
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ let
timeoutStyle
;
path = with pkgs; makeBinPath (
[ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ]
[ coreutils gnused gnugrep findutils diffutils util-linux mdadm ]
++ optional cfg.efiSupport efibootmgr
++ optionals cfg.useOSProber [ busybox os-prober ]);
font = lib.optionalString (cfg.font != null) (
Expand Down Expand Up @@ -730,7 +730,6 @@ in
install-grub-pl = pkgs.substituteAll {
src = ./install-grub.pl;
utillinux = pkgs.util-linux;
btrfsprogs = pkgs.btrfs-progs;
inherit (config.system.nixos) distroName;
};
perl = pkgs.perl.withPackages (p: with p; [
Expand Down
80 changes: 7 additions & 73 deletions nixos/modules/system/boot/loader/grub/install-grub.pl
Original file line number Diff line number Diff line change
Expand Up @@ -110,59 +110,20 @@ sub runCommand {
# Discover information about the location of the bootPath
struct(Fs => {
device => '$',
root => '$',
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, @pathInfo) = runCommand("@utillinux@/bin/findmnt", "-n", "-u", "-v", "-o", "SOURCE,FSROOT,FSTYPE,TARGET", "-T", @{[$dir]});
if ($status != 0 || @pathInfo != 1) {
die "Failed to get file system (returned $status) for @{[$dir]}";
}
return $bestFs;
my @fields = split /\s+/, $pathInfo[0];
return Fs->new(device => $fields[0], root => $fields[1], type => $fields[2], mount => $fields[3]);
}
struct (Grub => {
path => '$',
Expand All @@ -172,10 +133,7 @@ sub GetFs {
sub GrubFs {
my ($dir) = @_;
my $fs = GetFs($dir);
my $path = substr($dir, length($fs->mount));
if (substr($path, 0, 1) ne "/") {
$path = "/$path";
}
my $path = File::Spec->catdir($fs->root, substr($dir, length($fs->mount)));
my $search = "";

# ZFS is completely separate logic as zpools are always identified by a label
Expand Down Expand Up @@ -217,30 +175,6 @@ sub GrubFs {
}
$search .= $matches[0];
}

# BTRFS is a special case in that we need to fix the referenced path based on subvolumes
if ($fs->type eq 'btrfs') {
my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "show", @{[$fs->mount]});
if ($status != 0) {
die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
}
my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
if ($#ids > 0) {
die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
} elsif ($#ids == 0) {
my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "list", @{[$fs->mount]});
if ($status != 0) {
die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
}
my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/;
if ($#paths > 0) {
die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n";
} elsif ($#paths != 0) {
die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n";
}
$path = "/$paths[0]$path";
}
}
Comment on lines -220 to -243
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btrfs no longer needs to be treated specially here since $fs->root will contain the subvolume path and will be resolved the same way bind mounts are resolved.

}
if (not $search eq "") {
$search = "search --set=drive$driveid " . $search;
Expand Down