Loading nixos/lib/systemd-types.nix +26 −0 Original line number Diff line number Diff line Loading @@ -46,12 +46,14 @@ let inherit (lib.types) attrsOf coercedTo enum lines listOf nullOr oneOf package path singleLineStr submodule ; Loading @@ -71,6 +73,30 @@ let type = path; description = "Path of the source file."; }; dlopen = { usePriority = mkOption { type = enum [ "required" "recommended" "suggested" ]; default = "recommended"; description = '' Priority of dlopen ELF notes to include. "required" is minimal, "recommended" includes "required", and "suggested" includes "recommended". See: https://systemd.io/ELF_DLOPEN_METADATA/ ''; }; features = mkOption { type = listOf singleLineStr; default = [ ]; description = '' Features to enable via dlopen ELF notes. These will be in addition to anything included via 'usePriority', regardless of their priority. ''; }; }; }; }; Loading nixos/modules/system/boot/systemd/initrd.nix +0 −2 Original line number Diff line number Diff line Loading @@ -488,8 +488,6 @@ in { # fido2 support "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so" "${pkgs.libfido2}/lib/libfido2.so.1" ] ++ optionals cfg.package.withKmod [ "${pkgs.kmod.lib}/lib/libkmod.so.2" ] ++ jobScripts ++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents); Loading pkgs/build-support/kernel/make-initrd-ng/src/main.rs +111 −22 Original line number Diff line number Diff line use std::collections::{HashSet, VecDeque}; use std::collections::{BTreeSet, HashSet, VecDeque}; use std::env; use std::ffi::{OsStr, OsString}; use std::fs; Loading @@ -12,10 +12,41 @@ use eyre::Context; use goblin::{elf::Elf, Object}; use serde::Deserialize; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Deserialize, Hash)] #[serde(rename_all = "lowercase")] enum DLOpenPriority { Required, Recommended, Suggested, } #[derive(PartialEq, Eq, Debug, Deserialize, Clone, Hash)] #[serde(rename_all = "camelCase")] struct DLOpenConfig { use_priority: DLOpenPriority, features: BTreeSet<String>, } #[derive(Deserialize, Debug)] struct DLOpenNote { soname: Vec<String>, feature: Option<String>, // description is in the spec, but we don't need it here. // description: Option<String>, priority: Option<DLOpenPriority>, } #[derive(Deserialize, Debug, PartialEq, Eq, Hash, Clone)] struct StoreInput { source: String, target: Option<String>, dlopen: Option<DLOpenConfig>, } #[derive(PartialEq, Eq, Hash, Clone)] struct StorePath { path: Box<Path>, dlopen: Option<DLOpenConfig>, } struct NonRepeatingQueue<T> { Loading Loading @@ -48,13 +79,47 @@ impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> { } } fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>( source: P, elf: Elf, queue: &mut NonRepeatingQueue<Box<Path>>, ) { contents: &[u8], dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { if let Some(interp) = elf.interpreter { queue.push_back(Box::from(Path::new(interp))); queue.push_back(StorePath { path: Box::from(Path::new(interp)), dlopen: dlopen.clone(), }); } let mut dlopen_libraries = vec![]; if let Some(dlopen) = dlopen { for n in elf .iter_note_sections(&contents, Some(".note.dlopen")) .into_iter() .flatten() { let note = n.wrap_err_with(|| format!("bad note in {:?}", source))?; // Payload is padded and zero terminated let payload = ¬e.desc[..note .desc .iter() .position(|x| *x == 0) .unwrap_or(note.desc.len())]; let parsed = serde_json::from_slice::<Vec<DLOpenNote>>(payload)?; for mut parsed_note in parsed { if dlopen.use_priority >= parsed_note.priority.unwrap_or(DLOpenPriority::Recommended) || parsed_note .feature .map(|f| dlopen.features.contains(&f)) .unwrap_or(false) { dlopen_libraries.append(&mut parsed_note.soname); } } } } let rpaths = if elf.runpaths.len() > 0 { Loading @@ -71,13 +136,21 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( .map(|p| Box::<Path>::from(Path::new(p))) .collect::<Vec<_>>(); for line in elf.libraries { for line in elf .libraries .into_iter() .map(|s| s.to_string()) .chain(dlopen_libraries) { let mut found = false; for path in &rpaths_as_path { let lib = path.join(line); let lib = path.join(&line); if lib.exists() { // No need to recurse. The queue will bring it back round. queue.push_back(Box::from(lib.as_path())); queue.push_back(StorePath { path: Box::from(lib.as_path()), dlopen: dlopen.clone(), }); found = true; break; } Loading @@ -92,6 +165,8 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( ); } } Ok(()) } fn copy_file< Loading @@ -100,7 +175,8 @@ fn copy_file< >( source: P, target: S, queue: &mut NonRepeatingQueue<Box<Path>>, dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { fs::copy(&source, &target) .wrap_err_with(|| format!("failed to copy {:?} to {:?}", source, target))?; Loading @@ -109,7 +185,7 @@ fn copy_file< fs::read(&source).wrap_err_with(|| format!("failed to read from {:?}", source))?; if let Ok(Object::Elf(e)) = Object::parse(&contents) { add_dependencies(source, e, queue); add_dependencies(source, e, &contents, &dlopen, queue)?; // Make file writable to strip it let mut permissions = fs::metadata(&target) Loading Loading @@ -138,14 +214,18 @@ fn copy_file< fn queue_dir<P: AsRef<Path> + std::fmt::Debug>( source: P, queue: &mut NonRepeatingQueue<Box<Path>>, dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { for entry in fs::read_dir(&source).wrap_err_with(|| format!("failed to read dir {:?}", source))? { let entry = entry?; // No need to recurse. The queue will bring us back round here on its own. queue.push_back(Box::from(entry.path().as_path())); queue.push_back(StorePath { path: Box::from(entry.path().as_path()), dlopen: dlopen.clone(), }); } Ok(()) Loading @@ -153,12 +233,12 @@ fn queue_dir<P: AsRef<Path> + std::fmt::Debug>( fn handle_path( root: &Path, p: &Path, queue: &mut NonRepeatingQueue<Box<Path>>, p: StorePath, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { let mut source = PathBuf::new(); let mut target = Path::new(root).to_path_buf(); let mut iter = p.components().peekable(); let mut iter = p.path.components().peekable(); while let Some(comp) = iter.next() { match comp { Component::Prefix(_) => panic!("This tool is not meant for Windows"), Loading @@ -182,7 +262,7 @@ fn handle_path( .wrap_err_with(|| format!("failed to get symlink metadata for {:?}", source))? .file_type(); if typ.is_file() && !target.exists() { copy_file(&source, &target, queue)?; copy_file(&source, &target, &p.dlopen, queue)?; if let Some(filename) = source.file_name() { source.set_file_name(OsString::from_iter([ Loading @@ -193,7 +273,10 @@ fn handle_path( let wrapped_path = source.as_path(); if wrapped_path.exists() { queue.push_back(Box::from(wrapped_path)); queue.push_back(StorePath { path: Box::from(wrapped_path), dlopen: p.dlopen.clone(), }); } } } else if typ.is_symlink() { Loading @@ -213,7 +296,10 @@ fn handle_path( } let link_target_path = source.as_path(); if link_target_path.exists() { queue.push_back(Box::from(link_target_path)); queue.push_back(StorePath { path: Box::from(link_target_path), dlopen: p.dlopen.clone(), }); } break; } else if typ.is_dir() { Loading @@ -224,7 +310,7 @@ fn handle_path( // Only recursively copy if the directory is the target object if iter.peek().is_none() { queue_dir(&source, queue) queue_dir(&source, &p.dlopen, queue) .wrap_err_with(|| format!("failed to queue dir {:?}", source))?; } } Loading @@ -244,11 +330,14 @@ fn main() -> eyre::Result<()> { let output = &args[2]; let out_path = Path::new(output); let mut queue = NonRepeatingQueue::<Box<Path>>::new(); let mut queue = NonRepeatingQueue::<StorePath>::new(); for sp in input { let obj_path = Path::new(&sp.source); queue.push_back(Box::from(obj_path)); queue.push_back(StorePath { path: Box::from(obj_path), dlopen: sp.dlopen, }); if let Some(target) = sp.target { println!("{} -> {}", &target, &sp.source); // We don't care about preserving symlink structure here Loading @@ -264,7 +353,7 @@ fn main() -> eyre::Result<()> { } } while let Some(obj) = queue.pop_front() { handle_path(out_path, &*obj, &mut queue)?; handle_path(out_path, obj, &mut queue)?; } Ok(()) Loading Loading
nixos/lib/systemd-types.nix +26 −0 Original line number Diff line number Diff line Loading @@ -46,12 +46,14 @@ let inherit (lib.types) attrsOf coercedTo enum lines listOf nullOr oneOf package path singleLineStr submodule ; Loading @@ -71,6 +73,30 @@ let type = path; description = "Path of the source file."; }; dlopen = { usePriority = mkOption { type = enum [ "required" "recommended" "suggested" ]; default = "recommended"; description = '' Priority of dlopen ELF notes to include. "required" is minimal, "recommended" includes "required", and "suggested" includes "recommended". See: https://systemd.io/ELF_DLOPEN_METADATA/ ''; }; features = mkOption { type = listOf singleLineStr; default = [ ]; description = '' Features to enable via dlopen ELF notes. These will be in addition to anything included via 'usePriority', regardless of their priority. ''; }; }; }; }; Loading
nixos/modules/system/boot/systemd/initrd.nix +0 −2 Original line number Diff line number Diff line Loading @@ -488,8 +488,6 @@ in { # fido2 support "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so" "${pkgs.libfido2}/lib/libfido2.so.1" ] ++ optionals cfg.package.withKmod [ "${pkgs.kmod.lib}/lib/libkmod.so.2" ] ++ jobScripts ++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents); Loading
pkgs/build-support/kernel/make-initrd-ng/src/main.rs +111 −22 Original line number Diff line number Diff line use std::collections::{HashSet, VecDeque}; use std::collections::{BTreeSet, HashSet, VecDeque}; use std::env; use std::ffi::{OsStr, OsString}; use std::fs; Loading @@ -12,10 +12,41 @@ use eyre::Context; use goblin::{elf::Elf, Object}; use serde::Deserialize; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Deserialize, Hash)] #[serde(rename_all = "lowercase")] enum DLOpenPriority { Required, Recommended, Suggested, } #[derive(PartialEq, Eq, Debug, Deserialize, Clone, Hash)] #[serde(rename_all = "camelCase")] struct DLOpenConfig { use_priority: DLOpenPriority, features: BTreeSet<String>, } #[derive(Deserialize, Debug)] struct DLOpenNote { soname: Vec<String>, feature: Option<String>, // description is in the spec, but we don't need it here. // description: Option<String>, priority: Option<DLOpenPriority>, } #[derive(Deserialize, Debug, PartialEq, Eq, Hash, Clone)] struct StoreInput { source: String, target: Option<String>, dlopen: Option<DLOpenConfig>, } #[derive(PartialEq, Eq, Hash, Clone)] struct StorePath { path: Box<Path>, dlopen: Option<DLOpenConfig>, } struct NonRepeatingQueue<T> { Loading Loading @@ -48,13 +79,47 @@ impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> { } } fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>( source: P, elf: Elf, queue: &mut NonRepeatingQueue<Box<Path>>, ) { contents: &[u8], dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { if let Some(interp) = elf.interpreter { queue.push_back(Box::from(Path::new(interp))); queue.push_back(StorePath { path: Box::from(Path::new(interp)), dlopen: dlopen.clone(), }); } let mut dlopen_libraries = vec![]; if let Some(dlopen) = dlopen { for n in elf .iter_note_sections(&contents, Some(".note.dlopen")) .into_iter() .flatten() { let note = n.wrap_err_with(|| format!("bad note in {:?}", source))?; // Payload is padded and zero terminated let payload = ¬e.desc[..note .desc .iter() .position(|x| *x == 0) .unwrap_or(note.desc.len())]; let parsed = serde_json::from_slice::<Vec<DLOpenNote>>(payload)?; for mut parsed_note in parsed { if dlopen.use_priority >= parsed_note.priority.unwrap_or(DLOpenPriority::Recommended) || parsed_note .feature .map(|f| dlopen.features.contains(&f)) .unwrap_or(false) { dlopen_libraries.append(&mut parsed_note.soname); } } } } let rpaths = if elf.runpaths.len() > 0 { Loading @@ -71,13 +136,21 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( .map(|p| Box::<Path>::from(Path::new(p))) .collect::<Vec<_>>(); for line in elf.libraries { for line in elf .libraries .into_iter() .map(|s| s.to_string()) .chain(dlopen_libraries) { let mut found = false; for path in &rpaths_as_path { let lib = path.join(line); let lib = path.join(&line); if lib.exists() { // No need to recurse. The queue will bring it back round. queue.push_back(Box::from(lib.as_path())); queue.push_back(StorePath { path: Box::from(lib.as_path()), dlopen: dlopen.clone(), }); found = true; break; } Loading @@ -92,6 +165,8 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>( ); } } Ok(()) } fn copy_file< Loading @@ -100,7 +175,8 @@ fn copy_file< >( source: P, target: S, queue: &mut NonRepeatingQueue<Box<Path>>, dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { fs::copy(&source, &target) .wrap_err_with(|| format!("failed to copy {:?} to {:?}", source, target))?; Loading @@ -109,7 +185,7 @@ fn copy_file< fs::read(&source).wrap_err_with(|| format!("failed to read from {:?}", source))?; if let Ok(Object::Elf(e)) = Object::parse(&contents) { add_dependencies(source, e, queue); add_dependencies(source, e, &contents, &dlopen, queue)?; // Make file writable to strip it let mut permissions = fs::metadata(&target) Loading Loading @@ -138,14 +214,18 @@ fn copy_file< fn queue_dir<P: AsRef<Path> + std::fmt::Debug>( source: P, queue: &mut NonRepeatingQueue<Box<Path>>, dlopen: &Option<DLOpenConfig>, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { for entry in fs::read_dir(&source).wrap_err_with(|| format!("failed to read dir {:?}", source))? { let entry = entry?; // No need to recurse. The queue will bring us back round here on its own. queue.push_back(Box::from(entry.path().as_path())); queue.push_back(StorePath { path: Box::from(entry.path().as_path()), dlopen: dlopen.clone(), }); } Ok(()) Loading @@ -153,12 +233,12 @@ fn queue_dir<P: AsRef<Path> + std::fmt::Debug>( fn handle_path( root: &Path, p: &Path, queue: &mut NonRepeatingQueue<Box<Path>>, p: StorePath, queue: &mut NonRepeatingQueue<StorePath>, ) -> eyre::Result<()> { let mut source = PathBuf::new(); let mut target = Path::new(root).to_path_buf(); let mut iter = p.components().peekable(); let mut iter = p.path.components().peekable(); while let Some(comp) = iter.next() { match comp { Component::Prefix(_) => panic!("This tool is not meant for Windows"), Loading @@ -182,7 +262,7 @@ fn handle_path( .wrap_err_with(|| format!("failed to get symlink metadata for {:?}", source))? .file_type(); if typ.is_file() && !target.exists() { copy_file(&source, &target, queue)?; copy_file(&source, &target, &p.dlopen, queue)?; if let Some(filename) = source.file_name() { source.set_file_name(OsString::from_iter([ Loading @@ -193,7 +273,10 @@ fn handle_path( let wrapped_path = source.as_path(); if wrapped_path.exists() { queue.push_back(Box::from(wrapped_path)); queue.push_back(StorePath { path: Box::from(wrapped_path), dlopen: p.dlopen.clone(), }); } } } else if typ.is_symlink() { Loading @@ -213,7 +296,10 @@ fn handle_path( } let link_target_path = source.as_path(); if link_target_path.exists() { queue.push_back(Box::from(link_target_path)); queue.push_back(StorePath { path: Box::from(link_target_path), dlopen: p.dlopen.clone(), }); } break; } else if typ.is_dir() { Loading @@ -224,7 +310,7 @@ fn handle_path( // Only recursively copy if the directory is the target object if iter.peek().is_none() { queue_dir(&source, queue) queue_dir(&source, &p.dlopen, queue) .wrap_err_with(|| format!("failed to queue dir {:?}", source))?; } } Loading @@ -244,11 +330,14 @@ fn main() -> eyre::Result<()> { let output = &args[2]; let out_path = Path::new(output); let mut queue = NonRepeatingQueue::<Box<Path>>::new(); let mut queue = NonRepeatingQueue::<StorePath>::new(); for sp in input { let obj_path = Path::new(&sp.source); queue.push_back(Box::from(obj_path)); queue.push_back(StorePath { path: Box::from(obj_path), dlopen: sp.dlopen, }); if let Some(target) = sp.target { println!("{} -> {}", &target, &sp.source); // We don't care about preserving symlink structure here Loading @@ -264,7 +353,7 @@ fn main() -> eyre::Result<()> { } } while let Some(obj) = queue.pop_front() { handle_path(out_path, &*obj, &mut queue)?; handle_path(out_path, obj, &mut queue)?; } Ok(()) Loading