nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 106 lines 3.4 kB view raw
1use std::{fs, os::unix::fs::PermissionsExt, path::Path, process::Command}; 2 3use anyhow::{Context, Result}; 4 5use crate::{activate::activate, config::Config, fs::atomic_symlink, proc_mounts::Mounts}; 6 7const NIX_STORE_PATH: &str = "/nix/store"; 8 9fn prefixed_store_path(prefix: &str) -> String { 10 format!("{prefix}{NIX_STORE_PATH}") 11} 12 13/// Initialize the system in a prefix. 14/// 15/// This is done only once during the boot of the system. 16/// 17/// It is not designed to be re-executed during the lifetime of a system boot cycle. 18pub fn init(prefix: &str, toplevel: impl AsRef<Path>, config: &Config) -> Result<()> { 19 log::info!("Setting up /nix/store permissions..."); 20 setup_nix_store_permissions(prefix); 21 22 log::info!("Remounting /nix/store with the correct options..."); 23 remount_nix_store(prefix, &config.nix_store_mount_opts)?; 24 25 log::info!("Setting up /run/booted-system..."); 26 atomic_symlink(&toplevel, format!("{prefix}/run/booted-system"))?; 27 28 log::info!("Activating the system..."); 29 activate(prefix, toplevel, config)?; 30 31 Ok(()) 32} 33 34/// Set up the correct permissions for the Nix Store. 35/// 36/// Gracefully fail if they cannot be changed to accommodate read-only filesystems. 37fn setup_nix_store_permissions(prefix: &str) { 38 const ROOT_UID: u32 = 0; 39 const NIXBUILD_GID: u32 = 0; 40 const NIX_STORE_MODE: u32 = 0o1775; 41 42 let nix_store_path = prefixed_store_path(prefix); 43 44 std::os::unix::fs::chown(&nix_store_path, Some(ROOT_UID), Some(NIXBUILD_GID)).ok(); 45 fs::metadata(&nix_store_path) 46 .map(|metadata| { 47 let mut permissions = metadata.permissions(); 48 permissions.set_mode(NIX_STORE_MODE); 49 }) 50 .ok(); 51} 52 53/// Remount the Nix Store in a prefix with the provided options. 54fn remount_nix_store(prefix: &str, nix_store_mount_opts: &[String]) -> Result<()> { 55 let nix_store_path = prefixed_store_path(prefix); 56 57 let mut missing_opts = Vec::new(); 58 let mounts = Mounts::parse_from_proc_mounts()?; 59 60 if let Some(last_nix_store_mount) = mounts.find_mountpoint(&nix_store_path) { 61 for opt in nix_store_mount_opts { 62 if !last_nix_store_mount.mntopts.contains(opt) { 63 missing_opts.push(opt.clone()); 64 } 65 } 66 if !missing_opts.is_empty() { 67 log::info!( 68 "/nix/store is missing mount options: {}.", 69 missing_opts.join(",") 70 ); 71 } 72 } else { 73 log::info!("/nix/store is not a mountpoint."); 74 missing_opts.extend_from_slice(nix_store_mount_opts); 75 } 76 77 if !missing_opts.is_empty() { 78 log::info!("Remounting /nix/store with {}...", missing_opts.join(",")); 79 80 mount(&["--bind", &nix_store_path, &nix_store_path])?; 81 mount(&[ 82 "-o", 83 &format!("remount,bind,{}", missing_opts.join(",")), 84 &nix_store_path, 85 ])?; 86 } 87 88 Ok(()) 89} 90 91/// Call `mount` with the provided `args`. 92fn mount(args: &[&str]) -> Result<()> { 93 let output = Command::new("mount") 94 .args(args) 95 .output() 96 .context("Failed to run mount. Most likely, the binary is not on PATH")?; 97 98 if !output.status.success() { 99 return Err(anyhow::anyhow!( 100 "mount executed unsuccessfully: {}", 101 String::from_utf8_lossy(&output.stdout) 102 )); 103 } 104 105 Ok(()) 106}