mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-03 21:40:12 +00:00
nixos-init: use libpathrs instead of chroot to resolve path in sysroot
This commit is contained in:
@@ -573,7 +573,7 @@ in
|
||||
"${cfg.package.util-linux}/bin/sulogin"
|
||||
|
||||
# Resolving sysroot symlinks without code exec
|
||||
"${config.system.nixos-init.package}/bin/chroot-realpath"
|
||||
"${config.system.nixos-init.package}/bin/resolve-in-root"
|
||||
# Find the etc paths
|
||||
"${config.system.nixos-init.package}/bin/find-etc"
|
||||
]
|
||||
@@ -664,7 +664,7 @@ in
|
||||
|
||||
# Resolve symlinks in the init parameter. We need this for some boot loaders
|
||||
# (e.g. boot.loader.generationsDir).
|
||||
closure="$(chroot-realpath /sysroot "$closure")"
|
||||
closure="$(resolve-in-root /sysroot "$closure")"
|
||||
|
||||
# Assume the directory containing the init script is the closure.
|
||||
closure="$(dirname "$closure")"
|
||||
|
||||
82
pkgs/by-name/ni/nixos-init/Cargo.lock
generated
82
pkgs/by-name/ni/nixos-init/Cargo.lock
generated
@@ -22,7 +22,7 @@ checksum = "75726e2aa2b4c5a9d5c4cf3cb7f24658b6ec861616088f3ef3fb72edc0599286"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -31,6 +31,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
@@ -84,6 +90,15 @@ version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.16"
|
||||
@@ -92,15 +107,15 @@ checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -123,6 +138,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"indoc",
|
||||
"log",
|
||||
"pathrs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
@@ -134,6 +150,24 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "pathrs"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7e1a93ab007fbfbd784b3015b60cae7fc564ed3dc44d3c9f9f4a5043040ad83"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"itertools",
|
||||
"libc",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustversion",
|
||||
"static_assertions",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
@@ -160,9 +194,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -171,6 +205,12 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.21"
|
||||
@@ -220,6 +260,12 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
@@ -250,7 +296,16 @@ version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -264,6 +319,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
anyhow = "1.0.98"
|
||||
log = "0.4.27"
|
||||
env_logger = { version = "0.11.8", default-features = false }
|
||||
pathrs = "0.2.2"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
bootspec = "2.0.0"
|
||||
|
||||
@@ -52,7 +52,7 @@ closure. Currently nixos-init comes in at ~500 KiB.
|
||||
- `find-etc`: Finds the `/etc` paths in `/sysroot` so that the initrd doesn't
|
||||
directly depend on the toplevel, reducing the need to rebuild the initrd on
|
||||
every generation.
|
||||
- `chroot-realpath`: Figures out the canonical path inside a chroot.
|
||||
- `resolve-in-root`: Figures out the canonical path inside a chroot.
|
||||
|
||||
## Future
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
|
||||
binaries = [
|
||||
"initrd-init"
|
||||
"find-etc"
|
||||
"chroot-realpath"
|
||||
"resolve-in-root"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
use std::{
|
||||
env,
|
||||
io::{Write, stdout},
|
||||
os::unix::ffi::OsStrExt,
|
||||
os::unix::fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
||||
/// Canonicalize `path` in a chroot at the specified `root`.
|
||||
pub fn canonicalize_in_chroot(root: &str, path: &Path) -> Result<PathBuf> {
|
||||
let output = Command::new("chroot-realpath")
|
||||
.arg(root)
|
||||
.arg(path.as_os_str())
|
||||
.output()
|
||||
.context("Failed to run chroot-realpath. Most likely, the binary is not on PATH")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"chroot-realpath exited unsuccessfully: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
let output =
|
||||
String::from_utf8(output.stdout).context("Failed to decode stdout of chroot-realpath")?;
|
||||
|
||||
Ok(PathBuf::from(&output))
|
||||
}
|
||||
|
||||
/// Entrypoint for the `chroot-realpath` binary.
|
||||
pub fn chroot_realpath() -> Result<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
bail!("Usage: {} <chroot> <path>", args[0]);
|
||||
}
|
||||
|
||||
fs::chroot(&args[1]).context("Failed to chroot")?;
|
||||
std::env::set_current_dir("/").context("Failed to change directory")?;
|
||||
|
||||
let path = std::fs::canonicalize(&args[2])
|
||||
.with_context(|| format!("Failed to canonicalize {}", args[2]))?;
|
||||
|
||||
stdout()
|
||||
.write_all(path.into_os_string().as_bytes())
|
||||
.context("Failed to write output")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use std::{os::unix, path::Path};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::{SYSROOT_PATH, canonicalize_in_chroot, find_toplevel_in_prefix};
|
||||
use crate::{SYSROOT_PATH, find_toplevel_in_prefix, resolve_in_prefix};
|
||||
|
||||
/// Entrypoint for the `find-etc` binary.
|
||||
///
|
||||
@@ -18,14 +18,14 @@ pub fn find_etc() -> Result<()> {
|
||||
let basedir = config
|
||||
.etc_basedir
|
||||
.context("Failed to read etc_basedir from bootspec")?;
|
||||
let etc_basedir = Path::new(SYSROOT_PATH)
|
||||
.join(canonicalize_in_chroot(SYSROOT_PATH, Path::new(&basedir))?.strip_prefix("/")?);
|
||||
let etc_basedir =
|
||||
Path::new(SYSROOT_PATH).join(resolve_in_prefix(SYSROOT_PATH, &basedir)?.strip_prefix("/")?);
|
||||
|
||||
let metadata_image = config
|
||||
.etc_metadata_image
|
||||
.context("Failed to read etc_metadata_image from bootspec")?;
|
||||
let etc_metadata_image = Path::new(SYSROOT_PATH)
|
||||
.join(canonicalize_in_chroot(SYSROOT_PATH, Path::new(&metadata_image))?.strip_prefix("/")?);
|
||||
.join(resolve_in_prefix(SYSROOT_PATH, &metadata_image)?.strip_prefix("/")?);
|
||||
|
||||
unix::fs::symlink(etc_basedir, "/etc-basedir").context("Failed to link /etc-basedir")?;
|
||||
unix::fs::symlink(etc_metadata_image, "/etc-metadata-image")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
mod activate;
|
||||
mod chroot_realpath;
|
||||
mod config;
|
||||
mod find_etc;
|
||||
mod fs;
|
||||
mod init;
|
||||
mod initrd_init;
|
||||
mod path;
|
||||
mod proc_mounts;
|
||||
mod switch_root;
|
||||
|
||||
@@ -14,10 +14,10 @@ use anyhow::{Context, Result, bail};
|
||||
|
||||
pub use crate::{
|
||||
activate::activate,
|
||||
chroot_realpath::{canonicalize_in_chroot, chroot_realpath},
|
||||
find_etc::find_etc,
|
||||
init::init,
|
||||
initrd_init::initrd_init,
|
||||
path::{resolve_in_prefix, resolve_in_root},
|
||||
switch_root::switch_root,
|
||||
};
|
||||
|
||||
@@ -77,7 +77,7 @@ pub fn verify_init_is_nixos(prefix: &str, path: impl AsRef<Path>) -> Result<Path
|
||||
pub fn find_init_in_prefix(prefix: &str) -> Result<PathBuf> {
|
||||
let cmdline = std::fs::read_to_string("/proc/cmdline")?;
|
||||
let init = extract_init(&cmdline)?;
|
||||
let canonicalized_init = canonicalize_in_chroot(prefix, &init)?;
|
||||
let canonicalized_init = resolve_in_prefix(prefix, &init)?;
|
||||
log::info!("Found init: {}.", canonicalized_init.display());
|
||||
Ok(canonicalized_init)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{env, io::Write, process::ExitCode};
|
||||
|
||||
use log::Level;
|
||||
|
||||
use nixos_init::{chroot_realpath, find_etc, initrd_init};
|
||||
use nixos_init::{find_etc, initrd_init, resolve_in_root};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let arg0 = env::args()
|
||||
@@ -13,7 +13,7 @@ fn main() -> ExitCode {
|
||||
setup_logger();
|
||||
let entrypoint = match arg0.as_str() {
|
||||
"find-etc" => find_etc,
|
||||
"chroot-realpath" => chroot_realpath,
|
||||
"resolve-in-root" => resolve_in_root,
|
||||
"initrd-init" => initrd_init,
|
||||
_ => {
|
||||
log::error!("Command {arg0} unknown");
|
||||
|
||||
64
pkgs/by-name/ni/nixos-init/src/path.rs
Normal file
64
pkgs/by-name/ni/nixos-init/src/path.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use std::{
|
||||
env,
|
||||
io::{Write, stdout},
|
||||
os::{
|
||||
fd::{AsFd, AsRawFd},
|
||||
unix::ffi::OsStrExt,
|
||||
},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use pathrs::{
|
||||
Root,
|
||||
procfs::{ProcfsBase, ProcfsHandle},
|
||||
};
|
||||
|
||||
/// Resolve a path inside a prefix.
|
||||
///
|
||||
/// This resolves the path by following all symlinks until the end.
|
||||
///
|
||||
/// Uses `openat(2)` with the `RESOLVE_IN_ROOT` flag.
|
||||
pub fn resolve_in_prefix(prefix: &str, path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||
let root = Root::open(prefix).with_context(|| format!("Failed to open prefix {prefix}"))?;
|
||||
let handle = root.resolve(&path).with_context(|| {
|
||||
format!(
|
||||
"Failed to resolve path {} in prefix {prefix}",
|
||||
path.as_ref().display()
|
||||
)
|
||||
})?;
|
||||
|
||||
let fd = handle.as_fd().as_raw_fd();
|
||||
if fd.is_negative() {
|
||||
bail!("File descriptor of resolved path is negative")
|
||||
}
|
||||
let proc = ProcfsHandle::new().context("Failed to open /proc")?;
|
||||
let resolved_path = proc
|
||||
.readlink(ProcfsBase::ProcSelf, format!("fd/{fd}"))
|
||||
.context("Failed to read path from procfs fd")?;
|
||||
|
||||
// Reading the path of the resolved FD will add the prefix to the path. Nonetheless this path
|
||||
// has been correctly resolved and we can simply strip the prefix again.
|
||||
Ok(Path::new("/").join(
|
||||
resolved_path
|
||||
.strip_prefix(prefix)
|
||||
.context("Failed to strip prefix from path")?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Entrypoint for the `resolve-in-root` binary.
|
||||
pub fn resolve_in_root() -> Result<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
bail!("Usage: {} <root> <path>", args[0]);
|
||||
}
|
||||
|
||||
let path = resolve_in_prefix(&args[1], &args[2])?;
|
||||
|
||||
stdout()
|
||||
.write_all(path.into_os_string().as_bytes())
|
||||
.context("Failed to write output")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user