Adding the OCaml interface
Dependencies
- [2]
UWQB743KFirst working shell (with ocaml code) - [3]
FRNJITN6Elfedit: Error instead of assert when there is no section table - [4]
VR4QJBTNElfedit: file sizes not aligned with page boundaries - [5]
E6W7X7U3Elfedit: fixing an alignment issue on GCC - [6]
AR2X3TLAInitial commit: introducing elfedit - [7]
VZTI2E23Elfedit: handling the case where we move the same section we're adding - [8]
5PJAXSZ7Elfedit: correct reallocation of dynstr/dynamic - [9]
ZV57M4VNDebugging - [10]
Z43EQIWNElfedit: edit to file - [11]
WGAKJ25EAdding a debug_svg command
Change contents
- replacement in src/mount.rs at line 1
use libc::{MS_BIND, mount, umount};use libc::{MS_BIND, MS_PRIVATE, mount, umount}; - edit in src/mount.rs at line 8
}pub fn make_root_private() -> Result<(), std::io::Error> {unsafe {let err = mount(std::ptr::null(),b"/\0".as_ptr() as *const i8,std::ptr::null(),MS_PRIVATE,std::ptr::null(),);if err == 0 {Ok(())} else {Err(std::io::Error::last_os_error())}} - replacement in src/mount.rs at line 28
pub fn bind(source: &str, target: &str) -> Result<Mount, std::io::Error> {let target = std::ffi::CString::new(target).unwrap();pub fn bind<S: AsRef<std::path::Path>, T: AsRef<std::path::Path>>(source: S,target: T,) -> Result<Mount, std::io::Error> {let target = target.as_ref().to_str().unwrap();let target = CString::new(target).unwrap();let source = source.as_ref().to_str().unwrap();let source = CString::new(source).unwrap(); - replacement in src/mount.rs at line 38
std::ffi::CString::new(source).unwrap().as_ptr(),source.as_ptr(), - replacement in src/mount.rs at line 44
error!("err {:?}", err);if err == 0 {return Ok(Mount {target: Some(target),});}Err(std::io::Error::last_os_error())}}pub fn ramfs<T: AsRef<std::path::Path>>(target: T) -> Result<Mount, std::io::Error> {let target = target.as_ref().to_str().unwrap();let target = CString::new(target).unwrap();unsafe {let err = mount(b"ramfs\0".as_ptr() as *const i8,target.as_ptr(),b"ramfs\0".as_ptr() as *const i8,0,std::ptr::null(),); - replacement in src/main.rs at line 1
use libc::*;use std::ffi::CString;use std::path::Path;use elpe::extract::*;use elpe::*;use std::path::{Path, PathBuf}; - edit in src/main.rs at line 5
use tokio::io::AsyncWriteExt;use tonic::codegen::tokio_stream::StreamExt; - edit in src/main.rs at line 9
use elpe::extract::*;use elpe::mount::*;use elpe::*; - replacement in src/main.rs at line 15
deb_client: Arc<elpe::Client>,deb_client: elpe::Client,sender: tokio::sync::mpsc::UnboundedSender<(crate::container::BuildRequest,tokio::sync::oneshot::Sender<Result<PathBuf, String>>,)>, - edit in src/main.rs at line 21
use proto::*; - replacement in src/main.rs at line 24
async fn derivation(async fn add_path( - replacement in src/main.rs at line 26
request: tonic::Request<DerivationRequest>,) -> Result<tonic::Response<DerivationReply>, tonic::Status> {debug!("Got a request from {:?}", request.remote_addr());request: tonic::Request<tonic::Streaming<proto::AddPathRequest>>,) -> Result<tonic::Response<proto::DerivationReply>, tonic::Status> {let mut r = request.into_inner();let mut current_file = None;let ref store = self.deb_client.store_path;let tmp_dir = tempfile::tempdir_in(store)?;let mut hasher = blake3::Hasher::new(); - replacement in src/main.rs at line 35
// let reply = hello_world::HelloReply {// message: format!("Hello {}!", request.into_inner().name),// };// Ok(Response::new(reply))unimplemented!()loop {trace!("waiting for next in stream");let Some(r) = r.next().await else { break };let r = r.unwrap();match r.request {Some(proto::add_path_request::Request::File(f)) => {hasher.update(b"\0f");hasher.update(f.name.as_bytes());hasher.update(b"\0");let p = tmp_dir.path().join(&f.name);tokio::fs::create_dir_all(p.parent().unwrap()).await?;current_file = Some(tokio::fs::File::create(&p).await?)}Some(proto::add_path_request::Request::Directory(d)) => {hasher.update(b"\0d");hasher.update(d.name.as_bytes());hasher.update(b"\0");let p = tmp_dir.path().join(&d.name);tokio::fs::create_dir_all(&p).await.unwrap();}Some(proto::add_path_request::Request::Contents(c)) => {hasher.update(&c.content);current_file.as_mut().unwrap().write_all(&c.content).await?;}None => break,}}let path = store.join(data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes()));use tokio::io::ErrorKind;let new = tmp_dir.into_path();match tokio::fs::rename(&new, &path).await {Ok(()) => (),Err(e) if e.kind() == ErrorKind::DirectoryNotEmpty => (),Err(e) => {tokio::fs::remove_dir_all(&new).await?;return Err(e.into());}}info!("add_path extracted to {:?}", path);Ok(tonic::Response::new(proto::DerivationReply {result: Some(proto::derivation_reply::Result::Ok(proto::DerivationResult {destdir: vec![path.to_str().unwrap().to_string()],paths: Vec::new(),path_patterns: Vec::new(),},)),}))}async fn derivation(&self,request: tonic::Request<proto::DerivationRequest>,) -> Result<tonic::Response<proto::DerivationReply>, tonic::Status> {debug!("derivation request");let r = request.into_inner();let (tx, rx) = tokio::sync::oneshot::channel();self.sender.send((crate::container::BuildRequest {name: r.name,paths: r.paths,script: r.builder,target: r.target,},tx,)).unwrap();Ok(tonic::Response::new(match rx.await.unwrap() {Ok(out) => proto::DerivationReply {result: Some(proto::derivation_reply::Result::Ok(proto::DerivationResult {destdir: vec![out.to_str().unwrap().to_string()],paths: Vec::new(),path_patterns: Vec::new(),},)),},Err(e) => proto::DerivationReply {result: Some(proto::derivation_reply::Result::Error(e)),},})) - replacement in src/main.rs at line 121
request: tonic::Request<UbuntuReleaseRequest>,) -> Result<tonic::Response<DerivationReply>, tonic::Status> {request: tonic::Request<proto::UbuntuReleaseRequest>,) -> Result<tonic::Response<proto::DerivationReply>, tonic::Status> { - replacement in src/main.rs at line 137
path: vec![p.to_str().unwrap().to_string()],error: None,result: Some(proto::derivation_reply::Result::Ok(proto::DerivationResult {destdir: vec![p.to_str().unwrap().to_string()],paths: Vec::new(),path_patterns: Vec::new(),},)), - replacement in src/main.rs at line 149
request: tonic::Request<UbuntuPackageRequest>,) -> Result<tonic::Response<DerivationReply>, tonic::Status> {request: tonic::Request<proto::UbuntuPackageRequest>,) -> Result<tonic::Response<proto::DerivationReply>, tonic::Status> { - replacement in src/main.rs at line 162
info!("path {:?}", p);info!("path {:?} {:#?}", r.name, p); - replacement in src/main.rs at line 164
path: p.iter().rev().map(|x| x.to_str().unwrap().to_string()).collect(),error: None,result: Some(proto::derivation_reply::Result::Ok(proto::DerivationResult {destdir: p.result.iter().rev().map(|x| x.to_str().unwrap().to_string()).collect(),paths: p.paths.into_iter().filter_map(Arc::into_inner).collect(),path_patterns: Vec::new(),},)), - edit in src/main.rs at line 177
}}pub fn child<P: AsRef<Path>>(path: &[P]) {let store = Path::new("/home/pe/Projets/frix/store/ailpe/store");std::fs::create_dir_all(store).unwrap();let vm_store = Path::new("/ailpe/store");let mut path_env = "PATH=".to_string();let mut ld_env = "LD_LIBRARY_PATH=".to_string();let mut mounts = Vec::new();for p in path {use std::fmt::Write;let host = store.join(p.as_ref().file_name().unwrap());let guest = vm_store.join(p.as_ref().file_name().unwrap());std::fs::create_dir_all(&host).unwrap();write!(&mut path_env,"{}/usr/bin:{}/bin:",guest.to_str().unwrap(),guest.to_str().unwrap()).unwrap();write!(&mut ld_env,"{}/lib64/x86_64-linux-gnu:{}/lib/x86_64-linux-gnu:",guest.to_str().unwrap(),guest.to_str().unwrap()).unwrap();mounts.push(Mount::bind(p.as_ref().to_str().unwrap(), host.to_str().unwrap()).unwrap()); - edit in src/main.rs at line 178
println!("{:?}", mounts);// Fork in order to be able to unmount despite the chroot.let f = unsafe { fork() };if f == 0 {let c = std::ffi::CString::new("/home/pe/Projets/frix/store").unwrap();if unsafe { chroot(c.as_ptr()) } < 0 {println!("child {:?}", std::io::Error::last_os_error());}println!("{:?}\n{:?}", path_env, ld_env);let penv = CString::new(path_env).unwrap();let lenv = CString::new(ld_env).unwrap();unsafe {let c = std::ffi::CString::new("/ailpe/store/tmp3/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",).unwrap();execve(c.as_ptr(),[c.as_ptr(), std::ptr::null()].as_ptr(),[penv.as_ptr(), lenv.as_ptr(), std::ptr::null()].as_ptr(),);println!("execve {:?}", std::io::Error::last_os_error());}} else {let mut status = 0;unsafe { waitpid(f, &mut status, 0) };std::mem::drop(mounts);} - edit in src/main.rs at line 179
/*let lua = mlua::Lua::new();let map_table = lua.create_table().unwrap();map_table.set(1, "one").unwrap();map_table.set("two", 2).unwrap();lua.globals().set("map_table", map_table).unwrap();let derivation = lua.create_function(|st, t: mlua::Table| {t.for_each(|k: String, v: mlua::Value| {println!("{:?} {:?}", k, v);Ok(())}).unwrap();Ok(1)}).unwrap();lua.globals().set("__derivation", derivation).unwrap();let ubuntu = lua.create_function(|st, (t, name): (mlua::Table, String)| {t.for_each(|k: String, v: mlua::Value| {println!("{:?} {:?}", k, v);Ok(())}).unwrap();let mut t = st.create_table().unwrap();t.set(1, "one").unwrap();Ok(t)}).unwrap();lua.globals().set("__ubuntu", ubuntu).unwrap(); - replacement in src/main.rs at line 180
let ubuntu_ctx = lua.create_function(|st: &mlua::Lua, t: mlua::Table| -> mlua::Result<mlua::Table> {t.for_each(|k: String, v: mlua::Value| {println!("{:?} {:?}", k, v);Ok(())}).unwrap();let mut t = st.create_table().unwrap();t.set(1, "one").unwrap();Ok(t)}).unwrap();lua.globals().set("__ubuntu_ctx", ubuntu_ctx).unwrap();lua.load(std::fs::read_to_string(&std::env::args().nth(1).unwrap()).unwrap()).exec().unwrap();return;*/#[tokio::main]async fn main() {fn main() { - edit in src/main.rs at line 188
let elpe = Elpe {deb_client: Arc::new(Client {c: lazy_init::Lazy::new(),mirror: "http://fr.archive.ubuntu.com/ubuntu".to_string(),store_path: Path::new("/home/pe/Projets/frix/store").to_path_buf(),in_release: None.into(),}),};let addr = "0.0.0.0:50051".parse().unwrap();tonic::transport::Server::builder().add_service(elpe_server::ElpeServer::new(elpe)).serve(addr).await.unwrap(); - replacement in src/main.rs at line 189
/*let h_secu = c.in_release("noble-security").await.unwrap();let h = c.in_release("noble").await.unwrap();let store_path = Path::new("/home/pe/Projets/frix/store");let user = "pe";let home = "/home/pe";let container_channel = crate::container::serve(user, store_path); - replacement in src/main.rs at line 195
let p_secu = c.packages(&h_secu, "main", "amd64").await.unwrap();let p_main = c.packages(&h, "main", "amd64").await.unwrap();let p_universe = c.packages(&h, "universe", "amd64").await.unwrap();let rt = tokio::runtime::Runtime::new().unwrap();rt.block_on(async move {privdrop::PrivDrop::default().user(user).apply().unwrap();unsafe {std::env::set_var("HOME", home);}let (sender, receiver) = tokio::sync::mpsc::unbounded_channel::<(crate::container::BuildRequest,tokio::sync::oneshot::Sender<Result<PathBuf, String>>,)>(); - replacement in src/main.rs at line 206
let index_secu = deb::Index::open(&p_secu).unwrap();let index_main = deb::Index::open(&p_main).unwrap();let index_universe = deb::Index::open(&p_universe).unwrap();let t = tokio::spawn(crate::container::forward(receiver, container_channel)); - replacement in src/main.rs at line 208
let h = Arc::new(Mutex::new(HashMap::new()));let index = &[index_main, index_universe, index_secu];download_extract_deps(index, &c, h.clone(), "hello-traditional").await.unwrap();let elpe = Elpe {deb_client: Client::new(store_path, "http://fr.archive.ubuntu.com/ubuntu"),sender,}; - replacement in src/main.rs at line 213
download_extract_deps(index, &c, h.clone(), "bash-static")/*let x = elpe.deb_client.command("noble", "qemu-utils", "usr/bin/qemu-nbd").await.output() - replacement in src/main.rs at line 222
trace!("{:#?}", h);return;panic!("x = {:?}", x);*/ - replacement in src/main.rs at line 225
let pid = unsafe {syscall(SYS_clone3,&libc::clone_args {flags: (CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS) as u64,pidfd: 0,parent_tid: 0,child_tid: 0,stack: 0,stack_size: 0,tls: 0,set_tid: 0,set_tid_size: 0,cgroup: 0,exit_signal: SIGCHLD as u64,},std::mem::size_of::<libc::clone_args>(),)};println!("pid {:?}", pid);if pid == 0 {child(&["/home/pe/Projets/frix/tmp","/home/pe/Projets/frix/tmp2","/home/pe/Projets/frix/tmp3",]);} else {println!("{:?}", std::io::Error::last_os_error());let x = unsafe { waitpid(pid as i32, std::ptr::null_mut(), 0) };println!("x {:?}", x);}*/let addr = "0.0.0.0:50051".parse().unwrap();tokio::select! {_ = tonic::transport::Server::builder().add_service(proto::elpe_server::ElpeServer::new(elpe)).serve(addr)=> {}_ = t => {}}}) - edit in src/lib.rs at line 1
//! Run bash scripts in containers on Linux, with only deterministic//! Debian or Ubuntu packages in scope.#![deny(missing_docs,trivial_casts,trivial_numeric_casts,unused_import_braces,unused_qualifications)] - edit in src/lib.rs at line 13
use std::sync::Arc; - edit in src/lib.rs at line 16
use tokio::sync::{Mutex, Semaphore}; - edit in src/lib.rs at line 18
use std::sync::Arc; - edit in src/lib.rs at line 19
/// Handle Debian packages. - edit in src/lib.rs at line 22
/// Extract a Debian package and all dependencies into the store. - edit in src/lib.rs at line 25
/// Recursive iterator on the files/directories in a path.pub mod find_files;/// Mount and unmount paths. - edit in src/lib.rs at line 30
/// Run scripts in a chroot inside a new Linux namespace, with the/// right directories mounted and symbolically linked to simulate a/// Debian/Ubuntu filesystem hierarchy.pub mod container; - edit in src/lib.rs at line 36
/// Errors - edit in src/lib.rs at line 39
/// An error from manipulating a Debian package. - edit in src/lib.rs at line 42
/// IO error - edit in src/lib.rs at line 45
/// An error from persisting a temporary file. - edit in src/lib.rs at line 48
/// An error from creating a temporary file. - edit in src/lib.rs at line 50
Tempfile(#[from] async_tempfile::Error),#[error(transparent)]/// An error from `elfedit`, this crate's companion ELF patcher. - edit in src/lib.rs at line 54
/// HTTP errors - edit in src/lib.rs at line 57
/// Wrong signature (on an Ubuntu index) - edit in src/lib.rs at line 60
/// Wrong package hash - replacement in src/lib.rs at line 62
WrongHash { expected: String, got: String },WrongHash {/// Expected hash.expected: String,/// Obtained hash.got: String},/// Wrong package or file size - edit in src/lib.rs at line 71
/// Wrong result symlink: Elpe packages have one "input name"/// given by the hash of their inputs, and that directory is/// symlinked from the "output name", which is the hash of their/// output. This makes the outputs verifiable all the way down:/// failures to verify result in this error. - replacement in src/lib.rs at line 77
WrongResultSymlink { expected: PathBuf, got: PathBuf },WrongResultSymlink {/// Expected output.expected: PathBuf,/// Obtained output.got: PathBuf},/// The contained process failed to produce the file or directory/// called `$DESTDIR`.#[error("Failed to produce $DESTDIR")]NoDestDir,/// The build process returned a status other than 0.#[error("Build process returned {status}")]BuildReturn {/// Return status.status: i32}, - edit in src/lib.rs at line 95
/// A Debian index client.#[derive(Clone)] - replacement in src/lib.rs at line 98
// dest: PathBuf,pub c: lazy_init::Lazy<reqwest::Client>,pub mirror: String,pub store_path: PathBuf,pub in_release: tokio::sync::Mutex<Option<Arc<InRelease>>>,c: lazy_init::Lazy<reqwest::Client>,mirror: String,store_path: PathBuf,in_release: Arc<Mutex<Option<Arc<InRelease>>>>,download_sem: Arc<Semaphore>,store_locks: Arc<std::sync::Mutex<HashMap<PathBuf, Arc<Mutex<()>>>>>,timeout: std::time::Duration,}/// A locked store path, to prevent concurrent writes to the/// store. For performance reasons, these are memory locks rather than/// filesystem locks.pub struct StoreLock {locks: Arc<std::sync::Mutex<HashMap<PathBuf, Arc<Mutex<()>>>>>,p: PathBuf,_lock: tokio::sync::OwnedMutexGuard<()>,}impl Drop for StoreLock {fn drop(&mut self) {debug!("store lock: dropping {:?}", self.p);self.locks.lock().unwrap().remove(&self.p);} - edit in src/lib.rs at line 133
/// - edit in src/lib.rs at line 155
const MAX_PARALLEL_DOWNLOADS: usize = 20; - edit in src/lib.rs at line 159
pub fn new<P: AsRef<std::path::Path>>(store_path: P, mirror: &str) -> Self {Client {c: lazy_init::Lazy::new(),mirror: mirror.to_string(),store_path: store_path.as_ref().to_path_buf(),in_release: Arc::new(None.into()),download_sem: Arc::new(Semaphore::new(MAX_PARALLEL_DOWNLOADS)),store_locks: Arc::new(std::sync::Mutex::new(HashMap::new())),timeout: std::time::Duration::from_secs(30),}} - replacement in src/lib.rs at line 172
self.c.get_or_create(|| reqwest::Client::new())self.c.get_or_create(|| reqwest::ClientBuilder::new().read_timeout(self.timeout).build().unwrap())}pub async fn command<R: Into<std::borrow::Cow<'static, str>>, P: AsRef<std::path::Path>>(&self,release: R,pkg: &str,cmd: P,) -> Command {let h = self.in_release(release.into()).await.unwrap();let arch = if cfg!(target_arch = "x86_64") {"amd64"} else if cfg!(target_arch = "aarch64") {"aarch64"} else {unreachable!()};let (main, universe) = tokio::join!(self.packages(&h, "main", arch),self.packages(&h, "universe", arch));let main = main.unwrap();let universe = universe.unwrap();let index = vec![deb::Index::open(&main).unwrap(),deb::Index::open(&universe).unwrap(),];let p = extract::download_extract_deps(&index, self, pkg).await.unwrap();debug!("running {:?}", p.result.last().unwrap().join(&cmd));Command::new(p.result.last().unwrap().join(&cmd))}pub async fn lock_store_path<P: AsRef<std::path::Path>>(&self, path: P) -> StoreLock {debug!("locking store path {:?}", path.as_ref());let p = path.as_ref().to_path_buf();let mutex = {let mut locks = self.store_locks.lock().unwrap();locks.entry(p.clone()).or_insert_with(|| Arc::new(Mutex::new(()))).clone()};let lock = mutex.lock_owned().await;debug!("lock acquired: {:?}", p);StoreLock {locks: self.store_locks.clone(),p,_lock: lock,} - replacement in src/lib.rs at line 328
return Ok(l)return Ok(l); - edit in src/lib.rs at line 337
let store_lock = self.lock_store_path(&ubuntu).await; - edit in src/lib.rs at line 344
let tmp = async_tempfile::TempFile::new_in(ubuntu.parent().unwrap()).await?; - replacement in src/lib.rs at line 349
.arg(&format!("--output={}", ubuntu.to_str().unwrap())).arg("--overwrite").arg(&format!("--output={}", tmp.file_path().to_str().unwrap())) - replacement in src/lib.rs at line 361
std::mem::drop(i);drop(i); - replacement in src/lib.rs at line 364
self.read_in_release(release, tokio::fs::File::open(&ubuntu).await?)self.read_in_release(release, tokio::fs::File::open(&tmp.file_path()).await?) - edit in src/lib.rs at line 371
drop(store_lock);tokio::fs::rename(&tmp.file_path(), &ubuntu).await?;std::mem::forget(tmp); - replacement in src/lib.rs at line 410
fn url(&self, package: &deb::Stanza) -> String {fn url(&self, file_name: Option<&str>) -> String { - replacement in src/lib.rs at line 412
let filename = package.file_name.unwrap();let filename = file_name.unwrap(); - edit in src/lib.rs at line 416
/// Download the given URL and check the hash, moving the/// resulting dir atomically. - edit in src/lib.rs at line 422
debug!("is_ok {:?}", path); - edit in src/lib.rs at line 425
let lock = self.lock_store_path(path.clone()).await; - replacement in src/lib.rs at line 429
let r = self.client().get(url).send().await?;const MAX_ATTEMPTS: usize = 10;for i in 1..=MAX_ATTEMPTS {match self.try_download(url, sha256, &path).await {Ok(()) => break,Err(e) if i == MAX_ATTEMPTS => return Err(e),Err(e) => {error!("attempt {:?} at downloading {:?}: {:?}", i, url, e);tokio::time::sleep(std::time::Duration::from_secs(1)).await;}}}drop(lock); // Ensure lock lives until here.Ok((Downloaded { path }, true))}async fn try_download(&self,url: &str,sha256: &str,path: &std::path::Path,) -> Result<(), Error> {let r = self.client().get(url).send().await?; - replacement in src/lib.rs at line 459
let mut f = tokio::fs::File::create(&path).await?;let mut f = async_tempfile::TempFile::new_in(self.store_path.as_path()).await?; - replacement in src/lib.rs at line 474
Ok((Downloaded { path }, true))tokio::fs::rename(&f.file_path(), &path).await?;std::mem::forget(f);Ok(()) - file addition: find_files.rs[2.15]
use std::path::PathBuf;use tracing::*;pub fn find_files(path: PathBuf) -> Result<FindFiles, std::io::Error> {debug!("find_files {:?}", path);let meta = std::fs::metadata(&path)?;Ok(FindFiles {stack: vec![(path, meta)],})}pub struct FindFiles {stack: Vec<(PathBuf, std::fs::Metadata)>,}impl Iterator for FindFiles {type Item = (PathBuf, std::fs::Metadata);fn next(&mut self) -> Option<Self::Item> {while let Some((p, meta)) = self.stack.pop() {debug!("find_files {:?}", p);if meta.is_symlink() || meta.is_file() {return Some((p, meta));} else if let Ok(dir) = std::fs::read_dir(&p) {for e in dir {if let Ok(e) = e {self.stack.push((e.path(), e.metadata().unwrap()));}}return Some((p, meta));} else {error!("could not read {:?}", p);continue;}}None}}pub fn find_dirs(path: PathBuf) -> Result<FindDirs, std::io::Error> {debug!("find_files {:?}", path);let meta = std::fs::metadata(&path)?;Ok(FindDirs {stack: vec![(path, meta)],})}pub struct FindDirs {stack: Vec<(PathBuf, std::fs::Metadata)>,}impl Iterator for FindDirs {type Item = (PathBuf, std::fs::Metadata);fn next(&mut self) -> Option<Self::Item> {while let Some((p, meta)) = self.stack.pop() {if meta.is_symlink() {continue;}if let Ok(dir) = std::fs::read_dir(&p) {for e in dir {if let Ok(e) = e {self.stack.push((e.path(), e.metadata().unwrap()));}}return Some((p, meta));}}None}} - replacement in src/extract.rs at line 1
use crate::{Client, Downloaded, Error};use crate::{Client, Downloaded, Error, find_files::*}; - replacement in src/extract.rs at line 7
use tokio::sync::Semaphore;use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; - edit in src/extract.rs at line 12
/// The result of downloading/extracting a package.#[derive(Debug, Clone)]pub struct Packages {/// Paths of each of the extracted packages, in reverse order from/// the root (i.e. the root is the last).pub result: Vec<Arc<PathBuf>>,/// Paths to directories (i.e. */usr/bin, */usr/lib etc), useful/// to create $PATH or $CFLAGS from the client.pub paths: Vec<Arc<String>>,} - replacement in src/extract.rs at line 25
client: &Arc<Client>,client: &Client, - replacement in src/extract.rs at line 27
) -> Result<Vec<PathBuf>, Error> {) -> Result<Packages, Error> { - replacement in src/extract.rs at line 30
let pkg = multi_lookup(index, &package).unwrap();let pkg = multi_lookup(index, &package).await.unwrap(); - replacement in src/extract.rs at line 35
deps_h: BTreeSet::new(),transitive_deps: Vec::new(),transitive_deps_h: BTreeSet::new(), - edit in src/extract.rs at line 39
files: BTreeSet::new(), - edit in src/extract.rs at line 41
let tasks = Arc::new(Semaphore::new(MAX_PARALLEL_DOWNLOADS)); - edit in src/extract.rs at line 42
let mut paths = BTreeSet::new(); - replacement in src/extract.rs at line 47
let downloaded = rx.await.unwrap();let downloaded = rx.await.unwrap()?; - replacement in src/extract.rs at line 49
elt.finalize(client, &files, downloaded, &mut stack, &mut result)let final_path = elt.finalize(client, &files, downloaded, &mut stack, &mut result) - replacement in src/extract.rs at line 52
seen.insert(elt.package.package, (elt.ld_path, elt.deps));seen.insert(elt.package.package,(elt.ld_path, elt.transitive_deps, final_path),);paths = elt.files; - replacement in src/extract.rs at line 58
if let Some((ld_path, deps)) = seen.get(&elt.package.package) {if let Some((ld_path, transitive_deps, final_path)) = seen.get(&elt.package.package) {debug!("already seen {:?}", elt.package.package); - replacement in src/extract.rs at line 66
for dep in deps {if last.deps_h.insert(dep.clone()) {last.deps.push(dep.clone());for dep in transitive_deps {if last.transitive_deps_h.insert(dep.clone()) {debug!("adding transitive dep: {:?} -> {:?}", last.package.package, dep);last.transitive_deps.push(dep.clone()); - edit in src/extract.rs at line 72
last.deps.push(final_path.clone()); - edit in src/extract.rs at line 75
}use std::collections::hash_map::Entry;if let Entry::Vacant(e) = seen.entry(elt.package.package) {e.insert((Vec::new(), Vec::new())); - replacement in src/extract.rs at line 76
unreachable!()seen.insert(elt.package.package,(Vec::new(), Vec::new(), Arc::new(PathBuf::new())),); - replacement in src/extract.rs at line 82
let client = client.clone();let url = client.url(&elt.package);let sha256 = elt.package.sha256.unwrap().to_string();let (tx, rx) = tokio::sync::oneshot::channel();let files = files.clone();let tasks = tasks.clone();tokio::spawn(async move {let permit = tasks.acquire_owned().await.unwrap();info!("downloading {:?}", url);let (mut task, downloaded) = client.download_url(&url, &sha256).await.unwrap();info!("finished downloading {:?}", url);if downloaded {tokio::task::spawn_blocking(move || {extract_task(&mut task, &mut *files.lock().unwrap()).unwrap();info!("finished extracting {:?}", url);tx.send(task).unwrap_or(());info!("sent {:?}", url);}).await.unwrap();} else {task.path.set_extension("");let mut files = files.lock().unwrap();for f in find_files(task.path.clone()) {if let Ok(p) = f.strip_prefix(&task.path) {use std::collections::hash_map::Entry;if let Entry::Vacant(e) = files.entry(p.to_path_buf()) {debug!("cached {:?} -> {:?}", p, f);e.insert(task.path.clone());}}}tx.send(task).unwrap_or(());elt.spawn_extract(client.clone()).await;elt.push_deps(index, &mut stack).await;}}Ok(Packages {result,paths: paths.into_iter().collect(),})}async fn add_subst(downloaded: PathBuf, target: &Path, files: Files) -> Result<(), std::io::Error> {let mut files = files.lock().unwrap();for (f, m) in find_files(downloaded.clone())? {if !m.is_dir() {if let Ok(p) = f.strip_prefix(&downloaded) {use std::collections::hash_map::Entry;if let Entry::Vacant(e) = files.entry(p.to_path_buf()) {debug!("add_subst {:?} -> {:?}", p, target);e.insert(target.to_path_buf()); - replacement in src/extract.rs at line 103
std::mem::drop(permit);Ok::<_, Error>(())});elt.rx = Some(rx);push_deps(index, elt, &mut stack);} - replacement in src/extract.rs at line 106
Ok(result)Ok(()) - replacement in src/extract.rs at line 109
async fn hash_reader(mut reader: impl tokio::io::AsyncReadExt + std::marker::Unpin,pub async fn hash_reader(mut reader: impl tokio::io::AsyncReadExt + Unpin, - replacement in src/extract.rs at line 128
fn extract_task(download: &mut Downloaded,files: &mut HashMap<PathBuf, PathBuf>,) -> Result<(), Error> {async fn extract_task(client: &Client, download: &mut Downloaded) -> Result<(), Error> { - edit in src/extract.rs at line 130
let path = download.path.with_extension("");let lock = client.lock_store_path(&path).await; - replacement in src/extract.rs at line 133
let d = deb::Deb::read(&mut f)?;download.path.set_extension("");std::fs::create_dir_all(&download.path)?;d.decompress(&mut f, &download.path, |path| {use std::collections::hash_map::Entry;let path = if let Ok(p) = path.strip_prefix(".") {p.to_path_buf()} else {path.to_path_buf()};debug!("extract {:?} -> {:?}", path, download.path);if let Entry::Vacant(e) = files.entry(path) {e.insert(download.path.clone());let d = match deb::Deb::read(&mut f) {Ok(d) => d,Err(e) => {std::fs::remove_file(&download.path).unwrap_or(());return Err(e.into());}};std::fs::create_dir_all(&path).unwrap_or(());match d.decompress(&mut f, &path) {Ok(()) => {download.path = path;drop(lock);Ok(()) - replacement in src/extract.rs at line 147
})?;Ok(())Err(e) => {std::fs::remove_dir_all(&path).unwrap_or(());std::fs::remove_file(&download.path).unwrap_or(());drop(lock);Err(e.into())}} - replacement in src/extract.rs at line 156
const MAX_PARALLEL_DOWNLOADS: usize = 20;fn multi_lookup<'a>(index: &'a [deb::Index], package: &str) -> Option<deb::Stanza<'a>> {async fn multi_lookup<'a>(index: &'a [deb::Index], package: &str) -> Option<deb::Stanza<'a>> {for ind in index {if let Some(i) = ind.lookup_async(package).await {return Some(i);}} - replacement in src/extract.rs at line 163
if let Some(i) = ind.lookup(&package) {if let Some(i) = ind.lookup_virtual_async(package).await.into_iter().next() { - replacement in src/extract.rs at line 170
struct FindFiles {stack: Vec<(PathBuf, std::fs::Metadata, bool)>,}type Files = Arc<Mutex<HashMap<PathBuf, PathBuf>>>; - replacement in src/extract.rs at line 172
fn find_files(path: PathBuf) -> FindFiles {let meta = std::fs::metadata(&path).unwrap();FindFiles {stack: vec![(path, meta, false)],}}impl<'a> StackElt<'a> {/// Some libexec files (e.g. gcc) are searched relative to their/// original package /bin path. Propagating the /usr/libexec/// folder avoids overrides and ad-hoc code in the client.async fn link_extra(&mut self, final_path: &Path, extra: &[&str]) -> Result<(), Error> {debug!("linking extra path in {:?}: {:?}", final_path, extra);for path in extra {let final_extra = final_path.join(path); - replacement in src/extract.rs at line 181
impl Iterator for FindFiles {type Item = PathBuf;fn next(&mut self) -> Option<Self::Item> {while let Some((p, meta, visited)) = self.stack.pop() {if visited {continue;}if let Ok(dir) = std::fs::read_dir(&p) {self.stack.push((p, meta, true));for e in dir {if let Ok(e) = e {self.stack.push((e.path(), e.metadata().unwrap(), false));for d in self.deps.iter() {let d_extra = d.join(path);let Ok(meta) = tokio::fs::metadata(&d_extra).await else {continue;};let mut stack = vec![(d_extra.clone(), meta)];while let Some((elt, meta)) = stack.pop() {if let Ok(mut dir) = tokio::fs::read_dir(&elt).await {let p = elt.strip_prefix(&d_extra).unwrap();tokio::fs::create_dir_all(&final_extra.join(&p)).await?;while let Some(e) = dir.next_entry().await? {stack.push((e.path(), e.metadata().await?));}} else if meta.is_file() {let p = elt.strip_prefix(&d_extra).unwrap();debug!("libexec hard link {:?} to {:?}", elt, final_extra.join(&p));tokio::fs::hard_link(&elt, &final_extra.join(&p)).await.unwrap_or(()); - edit in src/extract.rs at line 202
} else if meta.is_file() || meta.is_symlink() {return Some(p);} else {error!("could not read {:?}", p);continue; - replacement in src/extract.rs at line 204
NoneOk(()) - edit in src/extract.rs at line 206
}type Files = Arc<Mutex<HashMap<PathBuf, PathBuf>>>; - edit in src/extract.rs at line 207
impl<'a> StackElt<'a> { - edit in src/extract.rs at line 229
/// Let `path` be the path to the raw extracted deb package, and/// `dest` be the final destination of this package in the store/// (when taking the package's context into account).////// Then, `find_libs` finds all paths in `path` that match the/// patterns in self.ld_path (`self.ld_path` paths are of the form/// `/usr/lib/x86_64-linux`), and add their equivalent in `dest` to/// `self.transitive_deps`.////// This makes sure that packages depending on `self` will be able/// to find the correct paths to these libs. - replacement in src/extract.rs at line 246
if self.deps_h.insert(path.clone()) {self.deps.push(path);if self.transitive_deps_h.insert(path.clone()) {self.transitive_deps.push(path); - replacement in src/extract.rs at line 253
fn patch_elf(&mut self, f: &Path, dest_path: &Path, files: &Files) -> Result<bool, Error> {async fn patch_elf(&mut self,f: &Path,dest_path: &Path,files: &Files,) -> Result<bool, Error> { - replacement in src/extract.rs at line 266
let Ok(mut elf) = Elf::open(&file) else {return Ok(false);let mut elf = match Elf::open(&file) {Ok(elf) => elf,Err(e) => {info!("error opening {:?}: {:?}", file, e);return Ok(false);} - replacement in src/extract.rs at line 278
let has_needed = parsed.needed().next().is_some();let needed: Vec<_> = parsed.needed().map(|x| x.unwrap().to_str().unwrap().to_string()).collect(); - replacement in src/extract.rs at line 318
info!("{:?}", subst);info!("set interpreter {:?}", subst); - replacement in src/extract.rs at line 320
} else if !has_needed {} else if needed.is_empty() { - replacement in src/extract.rs at line 327
for dep in self.deps.iter().rev() {debug!("dep = {:?}", dep);for dep in self.transitive_deps.iter().rev() {debug!("dep of {:?}: {:?}", f, dep); - edit in src/extract.rs at line 330
continue;}let mut dep = dep.to_path_buf();let mut is_needed = false;for n in needed.iter() {dep.push(&n);is_needed |= tokio::fs::metadata(&dep).await.is_ok();dep.pop();}if !is_needed { - replacement in src/extract.rs at line 349
if has_needed {if path.len() > 1 { - replacement in src/extract.rs at line 363
result: &mut Vec<PathBuf>,) -> Result<(), Error> {result: &mut Vec<Arc<PathBuf>>,) -> Result<Arc<PathBuf>, Error> { - edit in src/extract.rs at line 367
self.deps.sort(); - edit in src/extract.rs at line 371
debug!("finalize {:?} {:#?}",self.package.package, self.transitive_deps); - edit in src/extract.rs at line 378
let lock = client.lock_store_path(&dest).await; - replacement in src/extract.rs at line 384
let initial_deps_len = self.deps.len();let initial_deps_len = self.transitive_deps.len(); - replacement in src/extract.rs at line 393
info!("patching");// Patch the ELFs, now that we have all the deps.let mut hashing = Vec::new();for f in find_files(downloaded.path.clone()) {let dest_path = dest.join(&f.strip_prefix(&downloaded.path).unwrap());std::fs::create_dir_all(dest_path.parent().unwrap()).unwrap();let patched = self.patch_elf(&f, &dest_path, &files).unwrap_or(false);if !patched {// Hard linkdebug!("hard link {:?} {:?}", f, dest_path);std::fs::hard_link(&f, &dest_path).unwrap_or(());}hashing.push(tokio::spawn(async move {// hash + writeinfo!("hashing {:?}", f);if let Ok(link) = std::fs::read_link(&dest_path) {Ok(Some((dest_path, link.to_str().unwrap().to_string())))} else if let Ok(file) = tokio::fs::File::open(&dest_path).await {let mut hasher = blake3::Hasher::new();hash_reader(file, &mut hasher).await?;let hex = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());Ok::<_, Error>(Some((f, hex)))} else {Ok(None)}}));}info!("patched all");let mut hashes = Vec::with_capacity(hashing.len());for h in hashing.into_iter() {if let Some(h) = h.await.unwrap().unwrap() {hashes.push(h)info!("create final path for {dest:?}");match self.create_final_path(client, &files, &downloaded.path, &dest, &base_package_name).await{Ok(x) => x,Err(e) => {tokio::fs::remove_dir_all(&dest).await.unwrap_or(());return Err(e); - edit in src/extract.rs at line 404
hashes.sort_by(|a, b| a.0.cmp(&b.0));info!("hashed all");let mut output_hasher = blake3::Hasher::new();let blakesums = dest.join("blake3sums");let mut file = std::fs::File::create(&blakesums).unwrap();for (path, hash) in hashes {debug!("strip {:?} {:?}", path, downloaded.path);let path = path.to_str().unwrap();writeln!(file, "{} {}", hash, path)?;writeln!(output_hasher, "{} {}", hash, path)?;}client.store_path.join(&format!("{}-{}",data_encoding::HEXLOWER.encode(output_hasher.finalize().as_bytes()),base_package_name,)) - replacement in src/extract.rs at line 405
info!("found, no patching");info!("found, no patching: {:?}", dest); - replacement in src/extract.rs at line 408
let file = tokio::fs::File::open(&blakesums).await.unwrap();let file = match tokio::fs::File::open(&blakesums).await {Ok(file) => file,Err(e) => {error!("Error {:?} {:?}: {:?}", blakesums, downloaded.path, e);return Err(e.into());}}; - edit in src/extract.rs at line 416
let r = tokio::io::BufReader::new(tokio::fs::File::open(&dest.join("paths")).await.unwrap(),);let mut l = r.lines();while let Some(l) = l.next_line().await? {self.files.insert(Arc::new(l));} - edit in src/extract.rs at line 431
let final_path = Arc::new(final_path); - edit in src/extract.rs at line 433
add_subst(downloaded.path.clone(), &dest, files.clone()).await?; - replacement in src/extract.rs at line 437
for dep in &mut self.deps[initial_deps_len..] {for dep in &mut self.transitive_deps[initial_deps_len..] { - replacement in src/extract.rs at line 442
match std::os::unix::fs::symlink(&dest, &final_path) {match std::os::unix::fs::symlink(&dest, &*final_path) { - replacement in src/extract.rs at line 445
let got = std::fs::read_link(&final_path)?;let got = std::fs::read_link(&*final_path)?; - replacement in src/extract.rs at line 455
result.push(final_path);result.push(final_path.clone()); - replacement in src/extract.rs at line 464
for dep in self.deps.iter() {if last.deps_h.insert(dep.clone()) {last.deps.push(dep.clone());for dep in self.transitive_deps.iter() {if last.transitive_deps_h.insert(dep.clone()) {debug!("adding transitive dep: {:?} -> {:?}", last.package.package, dep);last.transitive_deps.push(dep.clone()); - edit in src/extract.rs at line 469
}for f in self.files.iter() {last.files.insert(f.clone()); - edit in src/extract.rs at line 473
last.deps.push(final_path.clone()); - edit in src/extract.rs at line 475
drop(lock); - replacement in src/extract.rs at line 477
Ok(())Ok(final_path) - edit in src/extract.rs at line 479
} - replacement in src/extract.rs at line 480
fn push_deps<'a>(index: &'a [deb::Index], elt: StackElt<'a>, stack: &mut Vec<StackElt<'a>>) {let depends = elt.package.depends.clone();stack.push(elt);for dep in depends.iter() {match dep {deb::Dep::Simple(s) => {debug!("dep {:?}", s);let dep = multi_lookup(index, &s.name).unwrap();stack.push(StackElt {package: dep,rx: None,deps: Vec::new(),deps_h: BTreeSet::new(),ld_path: Vec::new(),ld_path_h: BTreeSet::new(),})async fn spawn_extract(&mut self,client: Client,) {let client = client.clone();let url = client.url(self.package.file_name.as_deref());let sha256 = self.package.sha256.unwrap().to_string();let (tx, rx) = tokio::sync::oneshot::channel();tokio::spawn(async move {let permit = client.download_sem.clone().acquire_owned().await.unwrap();info!("downloading {:?}", url);let (mut task, _) = match client.download_url(&url, &sha256).await {Ok(x) => x,Err(e) => {tx.send(Err(e)).unwrap_or(());return Ok(());}};let is_extracted = std::fs::metadata(&task.path.with_extension("")).is_ok();info!("finished downloading {:?}", url);if !is_extracted {// Sets extensionif let Err(e) = extract_task(&client, &mut task).await {info!("finished extracting {:?} {:?}", url, e);tx.send(Err(e)).unwrap_or(());} else {info!("finished extracting {:?}, Ok", url);tx.send(Ok(task)).unwrap_or(());}info!("sent {:?}", url);} else {task.path.set_extension("");tx.send(Ok(task)).unwrap_or(()); - replacement in src/extract.rs at line 514
deb::Dep::Alternatives { alt } => {debug!("alt {:?}", alt);for dep in alt {if let Some(dep_) = multi_lookup(index, &dep.name) {stack.push(StackElt {package: dep_,rx: None,deps: Vec::new(),deps_h: BTreeSet::new(),ld_path: Vec::new(),ld_path_h: BTreeSet::new(),});return;drop(permit);Ok::<_, Error>(())});self.rx = Some(rx);}async fn push_deps(self, index: &'a [deb::Index], stack: &mut Vec<StackElt<'a>>) {let depends = self.package.depends.clone();stack.push(self);for dep in depends.iter() {match dep {deb::Dep::Simple(s) => {debug!("dep {:?}", s);let Some(dep) = multi_lookup(index, &s.name).await else {panic!("could not find {:?}", s.name)};stack.push(StackElt {package: dep,rx: None,deps: Vec::new(),transitive_deps: Vec::new(),transitive_deps_h: BTreeSet::new(),ld_path: Vec::new(),ld_path_h: BTreeSet::new(),files: BTreeSet::new(),})}deb::Dep::Alternatives { alt } => {debug!("alt {:?}", alt);for dep in alt {if let Some(dep_) = multi_lookup(index, &dep.name).await {stack.push(StackElt {package: dep_,rx: None,deps: Vec::new(),transitive_deps: Vec::new(),transitive_deps_h: BTreeSet::new(),ld_path: Vec::new(),ld_path_h: BTreeSet::new(),files: BTreeSet::new(),});return;} - edit in src/extract.rs at line 558
panic!("Not found: {:?}", alt); - replacement in src/extract.rs at line 560
panic!("Not found: {:?}", alt);}}}async fn create_final_path(&mut self,client: &Client,files: &Files,downloaded_path: &Path,dest: &Path,base_package_name: &str,) -> Result<PathBuf, Error> {// Link the required libexec before hashing.let tmp = async_tempfile::TempDir::new_in(dest.parent().unwrap()).await?;self.link_extra(&tmp.dir_path(), &["usr/libexec", "usr/lib/gcc"]).await?;// Patch the ELFs, now that we have all the deps.let mut hashing = Vec::new();let mut hashes = Vec::with_capacity(hashing.len());debug!("create_final_path {:?}", downloaded_path);for (f, meta) in find_files(downloaded_path.to_path_buf())? {debug!("f = {:?}", f);let rel = f.strip_prefix(&downloaded_path).unwrap();let dest_path = tmp.dir_path().join(&rel);if meta.is_dir() {tokio::fs::create_dir_all(dest_path).await.unwrap_or(());continue;}if meta.is_symlink() {// Relink the file to the subst.let target = tokio::fs::read_link(&f).await?;debug!("relink {:?} -> {:?} {:?}", f, target, target.is_relative());let subst = {let l = files.lock().unwrap();let target_rel = rel.parent().unwrap().join(&target);if let Some(subst) = l.get(&target_rel) {Some(subst.join(&target_rel))} else {None}};if let Some(subst) = subst {debug!("relink to {:?}", dest_path);tokio::fs::symlink(&subst, &dest_path).await?;hashes.push((f, subst.to_str().unwrap().to_string()))} else {// Leave the symlink untouched// tokio::fs::create_dir_all(dest_path.parent().unwrap()).await.unwrap_or(());debug!("symlink {:?} {:?} {:?}", target, dest_path, f);tokio::fs::symlink(&target, &dest_path).await?;hashes.push((f, target.to_str().unwrap().to_string()));}continue;}if !self.patch_elf(&f, &dest_path, &files).await.unwrap_or(false){// Hard linkdebug!("hard link {:?} {:?}", f, dest_path);tokio::fs::hard_link(&f, &dest_path).await.unwrap_or(());}hashing.push(tokio::spawn(async move {// hash + writeinfo!("hashing {:?}", f);if let Ok(file) = tokio::fs::File::open(&dest_path).await {let mut hasher = blake3::Hasher::new();hash_reader(file, &mut hasher).await.unwrap();let hex = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());Ok::<_, Error>(Some((f, hex)))} else {Ok(None)}}));}info!("patched all");for h in hashing.into_iter() {if let Some(h) = h.await.unwrap().unwrap() {hashes.push(h)}}hashes.sort_by(|a, b| a.0.cmp(&b.0));info!("hashed all");let mut output_hasher = blake3::Hasher::new();{let blakesums = tmp.dir_path().join("blake3sums");let mut file = tokio::fs::File::create(&blakesums).await?;for (path, hash) in hashes {let path = path.strip_prefix(&downloaded_path).unwrap().to_str().unwrap();file.write_all(hash.as_bytes()).await?;file.write_all(b" ").await?;file.write_all(path.as_bytes()).await?;file.write_all(b"\n").await?;writeln!(output_hasher, "{} {}", hash, path)?; - edit in src/extract.rs at line 668
let final_path = client.store_path.join(&format!("{}-{}",data_encoding::HEXLOWER.encode(output_hasher.finalize().as_bytes()),base_package_name,));{let transitive = tmp.dir_path().join("paths");let mut file = tokio::fs::File::create(&transitive).await?;for path in self.files.iter() {file.write_all(path.as_bytes()).await?;file.write_all(b"\n").await?;}}for (f, _) in find_dirs(downloaded_path.to_path_buf())? {let rel = f.strip_prefix(&downloaded_path).unwrap();self.files.insert(Arc::new(final_path.join(rel).to_str().unwrap().to_string()));}info!("rename {:?} to {:?}", tmp.dir_path(), dest);tokio::fs::rename(tmp.dir_path(), dest).await?;std::mem::forget(tmp);Ok(final_path) - replacement in src/extract.rs at line 700
rx: Option<tokio::sync::oneshot::Receiver<Downloaded>>,rx: Option<tokio::sync::oneshot::Receiver<Result<Downloaded, Error>>>, - replacement in src/extract.rs at line 702
deps_h: BTreeSet<Arc<PathBuf>>,transitive_deps: Vec<Arc<PathBuf>>,transitive_deps_h: BTreeSet<Arc<PathBuf>>, - edit in src/extract.rs at line 706
files: BTreeSet<Arc<String>>, - replacement in src/deb.rs at line 15
global: Header,control: Header,_global: Header,_control: Header, - edit in src/deb.rs at line 47
}/*impl<'a> From<Dep<'a>> for OwnedDep {fn from(e: Dep<'a>) -> Self {match e {Dep::Simple(s) => OwnedDep::Simple(s.to_owned()),Dep::Alternatives { alt } => OwnedDep::Alternatives {alt: alt.iter().map(From::from).collect()},}}}/// A dependency, which is either a "simple" dependency, or a list of/// alternatives.#[derive(Debug, Clone)]pub enum OwnedDep {Simple(OwnedSimpleDep),Alternatives { alt: Vec<OwnedSimpleDep> },}/// A single dependency.#[derive(Debug, Clone)]pub struct OwnedSimpleDep {pub name: String,pub any: bool,pub constraints: Vec<(String, OwnedVersion)>, - edit in src/deb.rs at line 76
impl<'a> From<SimpleDep<'a>> for OwnedSimpleDep {fn from(e: SimpleDep<'a>) -> Self {OwnedSimpleDep {name: e.name.to_string(),any: e.any,constraints: e.constraints.iter().map(|(a, b)| (a.into(), b.into())).collect(),}}}*/ - replacement in src/deb.rs at line 113
return nom::IResult::Err(nom::Err::Error(nom::error::make_error(return IResult::Err(nom::Err::Error(nom::error::make_error( - edit in src/deb.rs at line 126
#[test]fn test_grub_common() {use tracing_subscriber::prelude::*;use tracing_subscriber::util::SubscriberInitExt;tracing_subscriber::registry().with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| String::new().into()),).with(tracing_subscriber::fmt::layer()).try_init(); - edit in src/deb.rs at line 139
let (rest, p) = parse_control("Package: grub-common\nArchitecture: amd64\nVersion: 2.12-1ubuntu7\nBuilt-Using: lzo2 (= 2.10-2build3)\nMulti-Arch: foreign\nPriority: optional\nSection: admin\nSource: grub2\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>\nOriginal-Maintainer: GRUB Maintainers <pkg-grub-devel@alioth-lists.debian.net>\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 12476\nDepends: libc6 (>= 2.38), libdevmapper1.02.1 (>= 2:1.02.36), libefiboot1t64 (>=38), libefivar1t64 (>= 38), libfreetype6 (>= 2.2.1), libfuse3-3 (>= 3.2.3), liblzma5 (>= 5.1.1alpha+20120614), debconf (>= 0.5) | debconf-2.0, gettext-base, lsb-base (>= 3.0-6), python3, python3-apt\nRecommends: os-prober (>= 1.33)\nSuggests: multiboot-doc, grub-emu, mtools, xorriso (>= 0.5.6.pl00), desktop-base (>= 4.0.6), console-setup\nConflicts: init-select\nBreaks: apport (<< 2.1.1), friendly-recovery (<< 0.2.13), lupin-support (<< 0.55), mdadm (<< 2.6.7-2)\nReplaces: grub-coreboot (<< 2.00-4), grub-efi (<< 1.99-1), grub-efi-amd64 (<< 2.00-4), grub-efi-ia32 (<< 2.00-4), grub-efi-ia64 (<< 2.00-4), grub-ieee1275 (<< 2.00-4), grub-linuxbios (<< 1.96+20080831-1), grub-pc (<< 2.00-4), grub-yeeloong (<< 2.00-4), init-select\nFilename: pool/main/g/grub2/grub-common_2.12-1ubuntu7_amd64.deb\nSize: 2119862\nMD5sum: 86e63081ae4ee34d6a4f408ac6dbf0e4\nSHA1: b98f9f88e5480423e041fd647b438ba2f6e6f5ea\nSHA256: d8110b97b6fb6da40449c74b9cb527f02965dffaae8344399a711617f5a69249\nSHA512: 85c63b8d3e6a870df48533ab525df13abe9ec4861ff4b5577def94f65a2ebf6ac44a54a6cd0eca1c825e1c7aa2bd14e4d4ed53f83d381963ac1dc51daa16482a\nHomepage: https://www.gnu.org/software/grub/\nDescription: GRand Unified Bootloader (common files)\nTask: ubuntu-desktop-minimal, ubuntu-desktop, ubuntu-desktop-raspi, kubuntu-desktop, xubuntu-minimal, xubuntu-desktop, lubuntu-desktop, ubuntustudio-desktop-core, ubuntustudio-desktop, ubuntukylin-desktop,ubuntukylin-desktop-minimal, ubuntu-mate-core, ubuntu-mate-desktop, ubuntu-budgie-desktop-minimal, ubuntu-budgie-desktop, ubuntu-budgie-desktop-raspi, ubuntu-unity-desktop, edubuntu-desktop-gnome-minimal, edubuntu-desktop-gnome-raspi, ubuntucinnamon-desktop-minimal, ubuntucinnamon-desktop-raspi\nDescription-md5: 9c75036dc0a0792fedbc58df208ed227").unwrap();assert!(p.depends.iter().any(|x| match x {Dep::Simple(s) => s.name == "liblzma5",_ => false}));assert!(rest.is_empty())} - edit in src/deb.rs at line 161
#[derive(Debug, Clone)]pub struct OwnedVersion {pub epoch: u32,pub upstream_version: OwnedUpstream,pub debian: Option<String>,} - replacement in src/deb.rs at line 208
return nom::IResult::Err(nom::Err::Error(nom::error::make_error(return IResult::Err(nom::Err::Error(nom::error::make_error( - replacement in src/deb.rs at line 258
return nom::IResult::Err(nom::Err::Error(nom::error::make_error(return IResult::Err(nom::Err::Error(nom::error::make_error( - replacement in src/deb.rs at line 279
_ => {p => {if let Some(p) = p {d.push(p)} - edit in src/deb.rs at line 285
} else {panic!("Alternatives {:?}", s) - edit in src/deb.rs at line 308
#[derive(Debug, Clone)]pub struct OwnedUpstream(String); - replacement in src/deb.rs at line 329
regex::Regex::new(r"^[a-z0-9.~+-]+-([a-z0-9.~+]+)").unwrap();static ref RE: regex::Regex = regex::Regex::new(r"^[a-z0-9.~+]+").unwrap();regex::Regex::new(r"^[a-z0-9\.~+-]+-([a-z0-9\.~+]+)").unwrap();static ref RE: regex::Regex = regex::Regex::new(r"^[a-z0-9\.~+]+").unwrap(); - edit in src/deb.rs at line 334
debug!("parse_version RE_DASH {:?}", s); - edit in src/deb.rs at line 338
debug!("parse_version RE {:?}", s); - replacement in src/deb.rs at line 342
return nom::IResult::Err(nom::Err::Error(nom::error::make_error(debug!("parse_version failed {:?}", s);return IResult::Err(nom::Err::Error(nom::error::make_error( - replacement in src/deb.rs at line 410
unsafe { std::str::from_utf8_unchecked(&self.file_id).trim() }std::str::from_utf8(&self.file_id).unwrap().trim() - replacement in src/deb.rs at line 430
assert_eq!(std::mem::size_of::<H>(), HEADER_SIZE as usize);assert_eq!(size_of::<H>(), HEADER_SIZE as usize); - edit in src/deb.rs at line 432
let pb: *mut H = &mut b; - replacement in src/deb.rs at line 434
&mut b as *mut H as *mut u8,std::mem::size_of::<H>(),pb as *mut u8,size_of::<H>(), - edit in src/deb.rs at line 443
const SIGNATURE_SIZE: u64 = 8; - replacement in src/deb.rs at line 457
pub fn decompress<R: BufRead + Seek, P: AsRef<std::path::Path>, F: FnMut(&std::path::Path)>(pub fn decompress<R: BufRead + Seek, P: AsRef<std::path::Path>>( - edit in src/deb.rs at line 461
mut f: F, - replacement in src/deb.rs at line 470
for e in ar.entries()? {let mut e = e?;f(&e.path()?);e.unpack_in(&path)?;for e in ar.entries().unwrap() {let mut e = e.unwrap();debug!("zstd decompress {:?}", e.path());// f(&e.path()?);e.unpack_in(&path).unwrap();}} else if self.data.file_id().ends_with(".tar.xz") {let mut ar = tar::Archive::new(xz::read::XzDecoder::new(r.take(self.data.file_size)));for e in ar.entries().unwrap() {let mut e = e.unwrap();debug!("xz decompress {:?}", e.path());// f(&e.path()?);e.unpack_in(&path).unwrap();}} else if self.data.file_id().ends_with(".tar") {let mut ar = tar::Archive::new(r.take(self.data.file_size));for e in ar.entries().unwrap() {let mut e = e.unwrap();debug!("tar extract {:?}", e.path());// f(&e.path()?);e.unpack_in(&path).unwrap(); - edit in src/deb.rs at line 507
assert_eq!(global.file_size, 4); - replacement in src/deb.rs at line 510
r.skip_until(b'c')?; // 'control.…'r.seek(SeekFrom::Current(-1))?;// r.skip_until(b'c')?; // 'control.…'// r.seek(SeekFrom::Current(-1))?; - replacement in src/deb.rs at line 525
unimplemented!()let mut decompressor = xz::read::XzDecoder::new(&ctrl[..]);let mut result = Vec::new();decompressor.read_to_end(&mut result)?;result - replacement in src/deb.rs at line 557
r.seek(SeekFrom::Start(SIGNATURE_SIZE + 2 * HEADER_SIZE + control.file_size,))?;r.skip_until(b'd')?; // 'data.…'r.seek(SeekFrom::Current(-1))?;if control.file_size % 2 == 1 {r.seek(SeekFrom::Current(1))?;} - edit in src/deb.rs at line 561
debug!("data = {:?}", data); - replacement in src/deb.rs at line 565
global,control,_global: global,_control: control, - edit in src/deb/index.rs at line 2
use tracing::*; - edit in src/deb/index.rs at line 4
use tracing::*; - replacement in src/deb/index.rs at line 11
pub struct Index {struct Index_ { - replacement in src/deb/index.rs at line 13
lru: Arc<Mutex<lru::LruCache<String, Option<(usize, usize)>>>>,lru: Mutex<lru::LruCache<String, Option<(usize, usize)>>>, - edit in src/deb/index.rs at line 16
#[derive(Clone)]pub struct Index(Arc<Index_>); - replacement in src/deb/index.rs at line 23
Ok(Index {Ok(Index(Arc::new(Index_ { - replacement in src/deb/index.rs at line 25
lru: Arc::new(Mutex::new(lru::LruCache::new(NonZeroUsize::new(256).unwrap()))),lru: Mutex::new(lru::LruCache::new(NonZeroUsize::new(256).unwrap())),})))}pub async fn lookup_async<'a>(&'a self, package: &str) -> Option<Stanza<'a>> {let s = self.clone();let p = package.to_string();tokio::task::spawn_blocking(move || s.lookup_range(&p)).await.unwrap().map(|(m1, m2)| {let s = &*self.0.map;let line = std::str::from_utf8(&s[m1..m2]).unwrap().trim();parse_control(line).unwrap().1})}pub async fn lookup_virtual_async<'a>(&'a self, package: &str) -> Vec<Stanza<'a>> {let s = self.clone();let r =regex::bytes::Regex::new(&format!(r#"\nProvides:[^\n]*[, ]{package}[,\s\n]"#)).unwrap();tokio::task::spawn_blocking(move || {r.find_iter(&*s.0.map).map(|m| {let map = &*s.0.map;s.frame(0, map.len(), m.start())}).collect::<Vec<_>>() - edit in src/deb/index.rs at line 54
.await.unwrap().into_iter().map(|(m1, m2)| {let line = std::str::from_utf8(&self.0.map[m1..m2]).unwrap().trim();parse_control(line).unwrap().1}).collect()}pub fn lookup<'a>(&'a self, package: &str) -> Option<Stanza<'a>> {self.lookup_range(package).map(|(m1, m2)| {let s = &*self.0.map;let line = std::str::from_utf8(&s[m1..m2]).unwrap().trim();parse_control(line).unwrap().1}) - replacement in src/deb/index.rs at line 72
pub fn lookup(&self, package: &str) -> Option<Stanza> {fn frame(&self, a: usize, b: usize, mut m1: usize) -> (usize, usize) {let s = &*self.0.map;while m1 > a && (s[m1] != b'\n' || s[m1 + 1] != b'\n') {m1 -= 1;}let mut m2 = m1 + 2;while m2 + 1 < b && (s[m2] != b'\n' || s[m2 + 1] != b'\n') {m2 += 1;}if m2 < b {m2 += 1}(m1, m2)}fn lookup_range(&self, package: &str) -> Option<(usize, usize)> { - replacement in src/deb/index.rs at line 91
let s = &*self.map;let s = &*self.0.map; - replacement in src/deb/index.rs at line 93
match self.lru.lock().unwrap().get(package) {match self.0.lru.lock().unwrap().get(package) { - replacement in src/deb/index.rs at line 95
let line = std::str::from_utf8(&s[*m1..*m2]).unwrap().trim();let (_, st) = parse_control(line).unwrap();return Some(st)return Some((*m1, *m2)); - replacement in src/deb/index.rs at line 97
Some(None) => {return None},None => {},Some(None) => return None,None => {} - edit in src/deb/index.rs at line 115
let mut m1 = (a + b) / 2;while m1 > a && (s[m1] != b'\n' || s[m1 + 1] != b'\n') {m1 -= 1;}let mut m2 = m1 + 2;while m2 + 1 < b && (s[m2] != b'\n' || s[m2 + 1] != b'\n') {m2 += 1;}if m2 < b {m2 += 1} - edit in src/deb/index.rs at line 116
let (m1, m2) = self.frame(a, b, (a + b) / 2); - replacement in src/deb/index.rs at line 122
self.lru.lock().unwrap().put(package.to_string(), None);self.0.lru.lock().unwrap().put(package.to_string(), None); - replacement in src/deb/index.rs at line 130
self.lru.lock().unwrap().put(package.to_string(), Some((m1, m2)));return Some(st);self.0.lru.lock().unwrap().put(package.to_string(), Some((m1, m2)));return Some((m1, m2)); - replacement in src/deb/index.rs at line 159
self.lru.lock().unwrap().put(package.to_string(), None);self.0.lru.lock().unwrap().put(package.to_string(), None); - file addition: container.rs[2.15]
use crate::{Error, find_files::*, mount};use futures::{SinkExt, StreamExt};use std::collections::{HashMap, HashSet};use std::ffi::CString;use std::os::unix::fs::PermissionsExt;use std::path::{Path, PathBuf};use std::sync::Arc;use tracing::*;/// A request to build a package.#[derive(bincode::Encode, bincode::Decode, Debug)]pub struct BuildRequest {pub name: String,pub paths: Vec<String>,pub script: String,pub target: String,}/// The channel used to communicate with the container process.pub struct ContainerChannel {r: std::os::unix::net::UnixStream,w: std::os::unix::net::UnixStream,}/// A function that forwards messages from the receiver to the/// container process and back.pub async fn forward(mut receiver: tokio::sync::mpsc::UnboundedReceiver<(BuildRequest,tokio::sync::oneshot::Sender<Result<PathBuf, String>>,)>,c: ContainerChannel,) -> Result<(), Error> {let r = tokio::net::UnixStream::from_std(c.r).unwrap();let w = tokio::net::UnixStream::from_std(c.w).unwrap();let encoder = tokio_util::codec::LengthDelimitedCodec::new();let mut writer = tokio_util::codec::FramedWrite::new(w, encoder);let decoder = tokio_util::codec::LengthDelimitedCodec::new();let mut reader = tokio_util::codec::FramedRead::new(r, decoder);let mut pending = HashMap::new();let mut id = 1u64;loop {tokio::select! {x = receiver.recv() => {if let Some((msg, resp)) = x {pending.insert(id, resp);let mut bytes = bytes::BytesMut::new();bytes.extend_from_slice(&bincode::encode_to_vec(&(id, msg), bincode::config::standard()).unwrap());debug!("sending to process {:?}", bytes.len());writer.send(bytes.into()).await?;id += 1;}}x = reader.next() => {debug!("received process response {:?}", x);if let Some(Ok(msg)) = x {let ((id, resp), _) = bincode::decode_from_slice::<(u64, ProcessResult), _>(&msg, bincode::config::standard()).unwrap();let chan = pending.remove(&id).unwrap();chan.send(resp).unwrap_or(());} else {panic!("received none");}}}}}type ProcessResult = Result<PathBuf, String>;/// Start the container process. This function forks: the child blocks/// indefinitely, while the parent returns. This needs to be a/// separate process in order to run as root and create namespaces.pub fn serve(user: &str, store_path: &Path) -> ContainerChannel {let (r0, w0) = std::os::unix::net::UnixStream::pair().unwrap();let (r1, w1) = std::os::unix::net::UnixStream::pair().unwrap();r0.set_nonblocking(true).unwrap();r1.set_nonblocking(true).unwrap();w0.set_nonblocking(true).unwrap();w1.set_nonblocking(true).unwrap();let pid = unsafe { libc::fork() };if pid == 0 {let rt = tokio::runtime::Runtime::new().unwrap();rt.block_on(async move {let r1 = tokio::net::UnixStream::from_std(r1).unwrap();let w0 = tokio::net::UnixStream::from_std(w0).unwrap();async_serve(user, store_path, r1, w0).await});unreachable!()} else {ContainerChannel { r: r0, w: w1 }}}/// Inner server callable from a Tokio runtime, used since Tokio/// runtimes don't survive forks.async fn async_serve(user: &str,store: &Path,r: tokio::net::UnixStream,w: tokio::net::UnixStream,) {let encoder = tokio_util::codec::LengthDelimitedCodec::new();let writer = Arc::new(tokio::sync::Mutex::new(tokio_util::codec::FramedWrite::new(w, encoder),));let decoder = tokio_util::codec::LengthDelimitedCodec::new();let mut reader = tokio_util::codec::FramedRead::new(r, decoder);debug!("drv process waiting");while let Some(received) = reader.next().await {if let Ok(received) = received {debug!("drv_process received {:?}", received.len());let ((id, rec_msg), _) = bincode::decode_from_slice::<(u64, BuildRequest), _>(&received,bincode::config::standard(),).unwrap();let writer = writer.clone();let store = store.to_path_buf();let user = user.to_string();tokio::spawn(async move {let result: ProcessResult =run_in_container(&user, &store, rec_msg).map_err(|e| format!("{:?}", e));debug!("result {:?}", result);let v = bincode::encode_to_vec(&(id, result), bincode::config::standard()).unwrap();let mut bytes = bytes::BytesMut::new();debug!("drv_process replying {:?}", v.len());bytes.extend_from_slice(&v);writer.lock().await.send(bytes.into()).await.unwrap()});}}info!("drv_process exited");}fn run_in_container(user: &str, store: &Path, r: BuildRequest) -> Result<PathBuf, Error> {let mut hasher = blake3::Hasher::new();hasher.update(r.name.as_bytes());hasher.update(b"\n");hasher.update(r.target.as_bytes());hasher.update(b"\n");debug!("run in container, path = {:#?}", r.paths);for p in r.paths.iter() {hasher.update(p.as_bytes());hasher.update(b"\n");}hasher.update(r.script.as_bytes());hasher.update(b"\n");let name = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());// Guest dest.let dest = store.join(&name);if std::fs::metadata(&dest).is_ok() {let mut output_hasher = blake3::Hasher::new();let blakesums = dest.join("blake3sums");let file = match std::fs::File::open(&blakesums) {Ok(file) => file,Err(e) => {error!("Error {:?} {:?}: {:?}", blakesums, dest, e);return Err(e.into());}};output_hasher.update_reader(file)?;return Ok(store.join(&format!("{}-{}",data_encoding::HEXLOWER.encode(output_hasher.finalize().as_bytes()),r.name,)));}// Tmp host path where things will be mounted.let tmp_dir = store.join(format!("{}.drv", name));// Full path of the store in the host.let tmp_store = Path::new(&tmp_dir).join(store.strip_prefix("/").unwrap());std::fs::create_dir_all(&tmp_store)?;// Host dest.let tmp_dest = tmp_store.join(&name);let pid = unsafe {libc::syscall(libc::SYS_clone3,&libc::clone_args {flags: (libc::CLONE_NEWPID | libc::CLONE_NEWNET | libc::CLONE_NEWNS) as u64,pidfd: 0,parent_tid: 0,child_tid: 0,stack: 0,stack_size: 0,tls: 0,set_tid: 0,set_tid_size: 0,cgroup: 0,exit_signal: libc::SIGCHLD as u64,},size_of::<libc::clone_args>(),)};if pid == 0 {match std::panic::catch_unwind(|| {inner_process(user, &r, &tmp_dir, &dest, &store, &tmp_store, &name,)}) {Ok(Ok(())) => std::process::exit(0),Ok(Err(Error::BuildReturn { status })) => std::process::exit(status),_ => std::process::exit(1),}} else {debug!("waitpid");let mut status = 0;unsafe { libc::waitpid(pid as i32, &mut status, 0) };info!("return status {:?}", status);if status != 0 {debug!("returning error");return Err(Error::BuildReturn { status });}// Now that the paths have been unmounted, delete.if let Ok(dir) = std::fs::read_dir(&tmp_store) {for entry in dir {std::fs::remove_dir(&entry?.path()).unwrap_or(());}std::fs::remove_dir(&tmp_store).unwrap_or(());}}// And hash the output.debug!("tmp_dest {:?}", tmp_dest);let Ok(hashed) = hash_all(&tmp_dest) else {return Err(Error::NoDestDir);};let out = store.join(&format!("{}-{}",data_encoding::HEXLOWER.encode(hashed.as_bytes()),r.name,));std::fs::remove_dir_all(&dest).unwrap_or(());// Should be a symlinkstd::fs::rename(&tmp_dest, &dest).unwrap();match std::os::unix::fs::symlink(&dest, &out) {Ok(()) => (),Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {let got = std::fs::read_link(&out).unwrap();if dest != got {Err::<(), _>(Error::WrongResultSymlink {expected: dest,got,}).unwrap();}}Err(e) => return Err(e.into()),}Ok(out)}fn patch_result_elf(root: &Path, f: &Path, target: &str) -> Result<bool, Error> {use elfedit::*;info!("patch_elf {:?}", f);let file = std::fs::OpenOptions::new().read(true).write(true).open(&f)?;let mut elf = match Elf::open(&file) {Ok(elf) => elf,Err(e) => {info!("error opening {:?}: {:?}", file, e);return Ok(false);}};info!("patching {:?}", f);let Some(parsed) = elf.parse().unwrap() else {info!("No dynamic section");return Ok(false);};let needed: Vec<_> = parsed.needed().map(|x| x.unwrap().to_str().unwrap().to_string()).collect();let interp = parsed.interpreter();if let Some(interp) = interp.unwrap() {let mut interp_ = interp.to_str().unwrap();let mut interp = Path::new(interp_).to_path_buf();debug!("existing interp: {interp:?}");if interp_.starts_with("/usr") || interp_.starts_with("/lib") {while interp_.starts_with("/usr") || interp_.starts_with("/lib") {if let Ok(target) = std::fs::read_link(&interp) {let target = if target.is_relative() {interp.parent().unwrap().join(target)} else {target};interp = target;interp_ = interp.to_str().unwrap();} else {break;}}debug!("target: {target:?}");let subst = CString::new(interp_).unwrap();info!("set interpreter {:?}", subst);elf.set_interpreter(subst.to_bytes_with_nul());} else {// TODO: not sure what else to do here, we// might want to set the interpreter to a// different value (equivalent to "recompiling// everything" on Nix).info!("Interpreter is {interp_}. Already patched?");return Ok(false);}} else if needed.is_empty() {return Ok(false);}let mut deps_h = HashSet::new();let mut path = String::new();for n in needed.iter() {for p in &["/usr/lib", "/usr/lib64", &format!("/usr/lib/{target}")] {let Ok(dep) = std::fs::read_link(Path::new(p).join(n)) else {continue;};if !deps_h.insert(dep.clone()) {continue;}debug!("patch_elf needed: {:?}", dep);debug!("root: {:?}", root);if !path.is_empty() {path.push(':')}path.push_str(dep.parent().unwrap().to_str().unwrap());}}path.push('\0');info!("Setting path {:?}", path);if path.len() > 1 {elf.set_runpath(&path.as_bytes());}Ok(elf.update(None).unwrap()) // map_err(From::from)}/// Can't be async since this needs to fork and Tokio doesn't work/// across forks.////// The fork is used to manage the mounts.fn inner_process(user: &str,r: &BuildRequest,tmp_dir: &Path,dest: &Path,store: &Path,tmp_store: &Path,name: &str,) -> Result<(), Error> {let tmp_usr = tmp_dir.join("usr");std::fs::create_dir_all(&tmp_usr)?;mount::make_root_private().unwrap();std::mem::forget(mount::Mount::ramfs(&tmp_usr).unwrap());std::os::unix::fs::symlink("/usr/lib", &tmp_dir.join("lib")).unwrap_or(());std::os::unix::fs::symlink("/usr/lib64", &tmp_dir.join("lib64")).unwrap_or(());for host in r.paths.iter() {let guest = tmp_dir.join(host.strip_prefix("/").unwrap());debug!("mounting {:?} to {:?}", host, guest);std::fs::create_dir_all(&guest).unwrap();debug!("created {:?} to {:?}", host, guest);match mount::Mount::bind(host, guest.to_str().unwrap()) {Ok(m) => std::mem::forget(m),Err(e) => error!("{e:?}"),}debug!("mounted {:?}", guest);if let Ok(target) = std::fs::read_link(&host) {info!("mounting link {:?}", target);if target.is_absolute() {let guest = tmp_dir.join(target.strip_prefix("/").unwrap());std::fs::create_dir_all(&guest).unwrap();match mount::Mount::bind(&target, guest.to_str().unwrap()) {Ok(m) => std::mem::forget(m),Err(e) => error!("{e:?}"),}}}let guest_usr = guest.join("usr");if let Ok(find) = find_files(guest_usr.clone()) {std::fs::create_dir(&guest_usr).unwrap_or(());for (f, m) in find {// Stripped: guest path of the link.let stripped = f.strip_prefix(&guest).unwrap();// Target: host path of the link.let target = tmp_dir.join(stripped);debug!("mount link {:?} {:?} {:?}", f, target, m);if m.is_dir() {std::fs::create_dir(&target).unwrap_or(());} else if let Ok(out) = std::fs::read_link(&f) {if std::fs::remove_file(&target).is_err() {std::fs::remove_dir_all(&target).unwrap_or(());}std::os::unix::fs::symlink(&out, &target).unwrap();} else {if std::fs::remove_file(&target).is_err() {std::fs::remove_dir_all(&target).unwrap_or(());}std::os::unix::fs::symlink(Path::new(&host).join(&stripped), &target).unwrap();}}}}let builder_base = format!("{name}-builder.sh");let tmp_builder = tmp_store.join(&builder_base);let builder = store.join(&builder_base);std::fs::write(&tmp_builder, &r.script).unwrap();let mut perm = std::fs::metadata(&tmp_builder).unwrap().permissions();perm.set_mode(0o555);std::fs::set_permissions(&tmp_builder, perm).unwrap();let (uid, gid) = {let user_ffi = CString::new(user).unwrap();let pw = unsafe { libc::getpwnam(user_ffi.as_ptr()) };assert!(!pw.is_null());let pw = unsafe { &*pw };(pw.pw_uid, pw.pw_gid)};std::os::unix::fs::chown(tmp_dir, Some(uid), Some(gid)).unwrap();std::os::unix::fs::chown(tmp_store, Some(uid), Some(gid)).unwrap();let out_env = format!("DESTDIR={}", dest.to_str().unwrap()).to_string();let out_env = CString::new(out_env.as_str()).unwrap();let c = CString::new(builder.to_str().unwrap()).unwrap();let pid = unsafe { libc::fork() };if pid == 0 {info!("chrooting to {:?}", tmp_dir);privdrop::PrivDrop::default().chroot(tmp_dir).user(user).apply().unwrap();std::env::set_current_dir("/").unwrap();debug!("execve {:?}", c);unsafe {libc::execve(c.as_ptr(),[c.as_ptr(), std::ptr::null()].as_ptr(),[out_env.as_ptr(), std::ptr::null()].as_ptr(),);}panic!("execve failed: {:?}", std::io::Error::last_os_error())} else {let mut status = 0;unsafe { libc::waitpid(pid, &mut status, 0) };debug!("fork returned {status}");if status != 0 {debug!("returning error");return Err(Error::BuildReturn { status });}}info!("chrooting to {:?}", tmp_dir);unsafe {let c = CString::new(tmp_dir.to_str().unwrap()).unwrap();libc::chroot(c.as_ptr());}if let Ok(f) = find_files(dest.to_path_buf()) {for (f, _meta) in f {// Potentially patch the ELF.debug!("patching {f:?}");if let Err(e) = patch_result_elf(&tmp_dir, &f, &r.target) {error!("{:?}", e);}}}Ok(())}/// Create the `blake3sums` file of `p` and hash that file.fn hash_all(p: &Path) -> Result<blake3::Hash, Error> {let mut hashes = Vec::new();for (f, _meta) in find_files(p.to_path_buf())? {// hash + writeinfo!("hashing {:?}", f);if let Ok(link) = std::fs::read_link(&f) {hashes.push((f, link.to_str().unwrap().to_string()))} else if f.is_file() {let file = std::fs::File::open(&f)?;let mut hasher = blake3::Hasher::new();hasher.update_reader(file).unwrap();let hex = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());hashes.push((f, hex))}}hashes.sort_by(|a, b| a.0.cmp(&b.0));info!("hashed all");let mut output_hasher = blake3::Hasher::new();let blakesums = p.join("blake3sums");let mut file = std::fs::File::create(&blakesums).unwrap();use std::io::Write;for (path, hash) in hashes {let path = path.to_str().unwrap();writeln!(file, "{} {}", hash, path)?;writeln!(output_hasher, "{} {}", hash, path)?;}Ok(output_hasher.finalize())} - edit in elpe/lib/elpegrpc.proto at line 3
message AddPathRequest {message File {string name = 1;int64 length = 2;int32 permissions = 3;}message FileContents {int64 start = 1;bytes content = 2;}message Directory {string name = 1;int32 permissions = 3;}oneof request {File file = 1;Directory directory = 2;FileContents contents = 3;}} - edit in elpe/lib/elpegrpc.proto at line 30
repeated string paths = 3;string target = 4;}message PathPattern {string pattern = 1;repeated string matches = 2;}message DerivationResult {repeated string destdir = 1;repeated string paths = 2;repeated PathPattern path_patterns = 3; - replacement in elpe/lib/elpegrpc.proto at line 46
repeated string path = 1;optional string error = 2;oneof result {DerivationResult ok = 1;string error = 2;} - edit in elpe/lib/elpegrpc.proto at line 66
repeated string path_patterns = 3; - edit in elpe/lib/elpegrpc.proto at line 71
rpc AddPath (stream AddPathRequest) returns (DerivationReply); - replacement in elpe/lib/elpe.ml at line 6
let connection address port =(* Setup Http/2 connection *)let* addresses =Lwt_unix.getaddrinfo address (string_of_int port)[ Unix.(AI_FAMILY PF_INET) ]inlet socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 inlet* () = Lwt_unix.connect socket (List.hd addresses).Unix.ai_addr inlet error_handler e =raise (Error e)inH2_lwt_unix.Client.create_connection ~error_handler sockettype store_path = { path : string } - edit in elpe/lib/elpe.ml at line 41
~do_request:(H2_lwt_unix.Client.request connection ~error_handler:(fun _ ->failwith "Error"))~handler:(Client.Rpc.unary enc ~f:(fun decoder ->let+ decoder = decoder inmatch decoder with| Some decoder -> (Reader.create decoder |> decode |> function| Ok v -> v| Error e ->failwith(Printf.sprintf "Could not decode request: %s"(Result.show_error e)))| None -> Elpe.Derivation.Response.make ()))()let derivation connection ~name ~builder ~paths ~target =let req = Elpegrpc.Elpe.DerivationRequest.make ~name ~builder ~paths ~target () inlet open Ocaml_protoc_plugin inlet open Elpegrpc.Elpe inlet encode, decode = Service.make_client_functions Elpe.derivation inlet enc = encode req |> Writer.contents inClient.call ~service:"elpe.Elpe" ~rpc:"Derivation" - edit in elpe/lib/elpe.ml at line 82
let rec walk_dir_rec encode f buf path path_name =let open Ocaml_protoc_plugin inlet* dir = Lwt_unix.opendir path inLwt.finalize(fun () ->let open Elpegrpc.Elpe inlet rec walk () =Lwt.catch(fun () ->let* entry = Lwt_unix.readdir dir inif entry = ".." || entry = "." then walk ()elselet path = Filename.concat path entry inlet path_name = Filename.concat path_name entry inlet* stat = Lwt_unix.lstat path inlet* () =match stat.st_kind with| Unix.S_DIR ->let req =AddPathRequest.make~request:(`Directory(AddPathRequest.Directory.make ~name:path_name~permissions:0o644 ()))()inlet enc = encode req |> Writer.contents inf (Some enc);walk_dir_rec encode f buf path path_name| Unix.S_REG ->let* file = Lwt_unix.openfile path [ O_RDONLY ] 0 inlet ff =AddPathRequest.File.make ~name:path_name~length:stat.st_size ~permissions:0o644 ()inlet req = AddPathRequest.make ~request:(`File ff) () inlet enc = encode req |> Writer.contents inlet () = f (Some enc) inlet rec read_all n =let* r = Lwt_unix.read file buf 0 4096 inif r != 0 thenlet req =AddPathRequest.make~request:(`Contents(AddPathRequest.FileContents.make ~start:n~content:(Bytes.sub buf 0 r) ()))()inlet enc = encode req |> Writer.contents inlet () = f (Some enc) inread_all (n + r)else Lwt.return ()inread_all 0| _ -> Lwt.return ()inwalk ())(function End_of_file -> Lwt.return () | e -> Lwt.fail e)inwalk ())(fun () -> Lwt_unix.closedir dir)let add_path connection path0 =let open Ocaml_protoc_plugin inlet open Elpegrpc.Elpe inlet encode, decode = Service.make_client_functions Elpe.addPath inClient.call ~service:"elpe.Elpe" ~rpc:"AddPath"~do_request:(H2_lwt_unix.Client.request connection ~error_handler:(fun _ ->failwith "Error"))~handler:(Client.Rpc.client_streaming ~f:(fun f response ->let buf = Bytes.create 4096 inlet* _ = walk_dir_rec encode f buf path0 "" inf None;let+ decoder = response inmatch decoder with| Some decoder -> (Reader.create decoder |> decode |> function| Ok v -> v| Error e ->failwith(Printf.sprintf "Could not decode request: %s"(Result.show_error e)))| None -> Elpe.Derivation.Response.make ()))()type build_result = { destdir : string list; paths : string list } - replacement in elpe/lib/elpe.ml at line 175
object (self)object - replacement in elpe/lib/elpe.ml at line 177
method setup : string list Lwt.t = Lwt.return []method build : string list Lwt.t = self#setup(* Returns the script to setup the build process, also used in shells. *)method setup : string Lwt.t = Lwt.return ""(* List of paths resulting from building the package. *)method build : build_result Lwt.t = failwith "Not implemented" - replacement in elpe/lib/elpe.ml at line 203
Lwt.return res.path)match res with| `Ok r -> Lwt.return r.destdir| `Error e -> failwith e| _ -> assert false) - edit in elpe/lib/elpe.ml at line 210
- replacement in elpe/lib/elpe.ml at line 212
Lwt.return res.pathmatch res with| `Ok r -> Lwt.return { destdir = r.destdir; paths = r.paths }| `Error e -> failwith e| _ -> assert false - edit in elpe/lib/elpe.ml at line 223
method target : string = "x86_64-linux-gnu"val extra_paths : string list ref = ref []method derivation (drv : derivation) =let* path = drv#build inextra_paths := path.destdir @ !extra_paths;Lwt.return pathmethod local_src p =let path_drv =objectinherit derivationmethod name = Filename.basename p - edit in elpe/lib/elpe.ml at line 237
method! build =let c =match !backend_conn with| None -> failwith "no conn"| Some c -> cinlet* res = add_path c p inlet res, _ = Result.get_ok res inmatch res with| `Ok r -> Lwt.return { destdir = r.destdir; paths = r.paths }| `Error e -> failwith e| _ -> assert falseendinlet* built = path_drv#build inextra_paths := built.destdir @ !extra_paths;Lwt.return (List.hd built.destdir)method src : string Lwt.t = failwith ("No src defined for " ^ self#name) - replacement in elpe/lib/elpe.ml at line 258
let* bash = (ubuntu "bash-static")#build inlet bash = List.hd bash inlet* b = self#build_inputs inlet* b =Lwt_list.map_plet* bash = self#derivation (ubuntu "bash-static") inlet bash = List.hd bash.destdir inlet* build_inputs = self#build_inputs inlet* () =Lwt_list.iter_p - replacement in elpe/lib/elpe.ml at line 265
Lwt.return (List.map (fun p -> p ^ "/usr/bin") p))bextra_paths := p.destdir @ !extra_paths;Lwt.return ())build_inputs - replacement in elpe/lib/elpe.ml at line 269
let path =List.fold_left(List.fold_left (fun acc x -> if acc == "" then x else acc ^ ":" ^ x))"" binLwt.return [ "#!" ^ bash ^ "\n" ^ "export PATH=" ^ path ^ "\n" ]endLwt.return("#!" ^ bash^ "/usr/bin/bash-static\n\set -xe\n\export PATH=/usr/bin\n\export LIBRARY_PATH=/lib/" ^ self#target ^ ":/lib64/" ^ self#target^ ":/usr/lib:/usr/lib64:/usr/lib/x86_64-linux-gnu\n\n") - replacement in elpe/lib/elpe.ml at line 277
let last_built_module : std_derivation option ref = ref Nonelet build (spec : std_derivation) = last_built_module := Some specmethod pre_unpack = Lwt.return ""method post_unpack = Lwt.return "" - replacement in elpe/lib/elpe.ml at line 280
let run_shell (spec : std_derivation) cmd =Lwt_main.run(let open Lwt.Syntax inlet port = 50051 inlet address = "127.0.0.1" inlet* c = connection address port inbackend_conn := Some c;let* b = spec#setup inmethod unpack_phase =let* src = self#src inlet* all = Lwt.all [ self#pre_unpack; self#post_unpack ] inmatch all with| pre :: post :: _ -> Lwt.return (pre ^ "\ncp -R " ^ src ^ "/* .\n" ^ post)| _ -> assert false - replacement in elpe/lib/elpe.ml at line 287
let* bash = (ubuntu "bash-static")#build inlet bash = List.hd bash inlet f = Filename.temp_dir "elpe-" "" ^ "/setup" inUnix.mkfifo f 0o666;let pid =Unix.create_process(bash ^ "/usr/bin/bash-static")(match cmd with| None -> [| "bash"; "--init-file"; f; "-i" |]| Some cmd -> [| "bash"; "--init-file"; f; "-i"; "-c"; cmd |])Unix.stdin Unix.stdout Unix.stderrinlet p = Unix.openfile f [ Unix.O_WRONLY ] 0o644 inList.fold_left(fun () b ->let _ = Unix.write_substring p b 0 (String.length b) in())() b;Unix.close p;method pre_configure = Lwt.return ""method post_configure = Lwt.return "" - replacement in elpe/lib/elpe.ml at line 290
match Unix.waitpid [] pid with| _, Unix.WEXITED e -> Lwt.return e| _ -> failwith "Unknown")method configure_phase =let* all = Lwt.all [ self#pre_configure; self#post_configure ] inmatch all with| pre :: post :: _ ->Lwt.return(pre ^ "\nif [[ -e configure ]]; then ./configure; fi\n" ^ post)| _ -> assert false - replacement in elpe/lib/elpe.ml at line 298
let run_build (spec : derivation) =Lwt_main.run(let open Lwt.Syntax inlet port = 50051 inlet address = "127.0.0.1" inlet* c = connection address port inbackend_conn := Some c;let* b = spec#build inmethod pre_build = Lwt.return ""method post_build = Lwt.return "" - replacement in elpe/lib/elpe.ml at line 301
let* bash = (ubuntu "bash-static")#build inlet bash = List.hd bash inmethod build_phase =let* all = Lwt.all [ self#pre_build; self#post_build ] inmatch all with| pre :: post :: _ -> Lwt.return (pre ^ "\nif [[ -e Makefile ]]; then make; fi\n" ^ post)| _ -> assert false - replacement in elpe/lib/elpe.ml at line 307
let f = Filename.temp_dir "elpe-" "" ^ "/setup" inUnix.mkfifo f 0o666;method pre_install = Lwt.return ""method post_install = Lwt.return "" - replacement in elpe/lib/elpe.ml at line 310
let pid =Unix.create_process(bash ^ "/usr/bin/bash-static")[| "bash"; "--init-file"; f; "-i" |]Unix.stdin Unix.stdout Unix.stderrinmethod install_phase =let* all = Lwt.all [ self#pre_install; self#post_install ] inmatch all with| pre :: post :: _ -> Lwt.return (pre ^ "\nif [[ -e Makefile ]]; then make install; fi\n" ^ post)| _ -> assert false - replacement in elpe/lib/elpe.ml at line 316
let p = Unix.openfile f [ Unix.O_WRONLY ] 0o644 inList.fold_left(fun () b ->let _ = Unix.write_substring p b 0 (String.length b) in())() b;Unix.close p;method! build =let* phases =Lwt.all[self#setup;self#unpack_phase;self#configure_phase;self#build_phase;self#install_phase;]inlet builder = String.concat "\n" phases inlet c =match !backend_conn with None -> failwith "no conn" | Some c -> cinlet* res = derivation c ~name:self#name ~builder ~paths:!extra_paths ~target:self#target inlet res, _ = Result.get_ok res inmatch res with| `Ok r -> Lwt.return { destdir = r.destdir; paths = r.paths }| `Error e -> failwith e| _ -> assert falseend - replacement in elpe/lib/elpe.ml at line 339
let _ =match Unix.waitpid [] pid with| _, Unix.WEXITED e -> print_endline ("ended waitpid " ^ string_of_int e)| _ -> print_endline "other"inLwt.return ())[2.62151]let last_built_module : std_derivation option ref = ref Nonelet build (spec : std_derivation) = last_built_module := Some spec - replacement in elpe/lib/dune at line 5
(libraries grpc-lwt lwt lwt.unix h2 h2-lwt-unix pbrt ocaml-protoc-plugin))(librariesgrpc-lwtlwtlwt.unixh2h2-lwt-unixpbrtocaml-protoc-plugintartar.gztar-unix)) - replacement in elpe/lib/dune at line 26
"--ocaml_out=annot=[@@deriving show { with_path = false }]:.""--ocaml_out=annot=[@@deriving show { with_path = false }, eq]:." - replacement in elpe/dune-project at line 17
(depends ocaml elpe lwt)(depends ocaml elpe lwt core core_unix) - replacement in elpe/dune-project at line 22
(depends ocaml lwt grpc-lwt h2-lwt-unix pbrt ocaml-protoc-plugin protoc)(depends ocaml lwt grpc-lwt h2-lwt-unix pbrt ocaml-protoc-plugin protoc tar tar-unix) - replacement in elpe/bin/elpe_bin.ml at line 1
open Elpelet connection address port =let open Lwt.Syntax in(* Setup Http/2 connection *)let* addresses =Lwt_unix.getaddrinfo address (string_of_int port)[ Unix.(AI_FAMILY PF_INET) ]inlet socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 inlet* () = Lwt_unix.connect socket (List.hd addresses).Unix.ai_addr inlet error_handler e = raise (Elpe.Error e) inH2_lwt_unix.Client.create_connection ~error_handler socketlet run_shell (spec : Elpe.std_derivation) cmd =Lwt_main.run(let open Lwt.Syntax inlet port = 50051 inlet address = "127.0.0.1" inlet* c = connection address port inElpe.backend_conn := Some c;let* b = spec#setup in - replacement in elpe/bin/elpe_bin.ml at line 22
let usage_msg = "elpe [-verbose] [-c <command>] <file1> [<file2>]"let verbose = ref falselet cmd = ref ""let input_files = ref []let anon_fun filename = input_files := filename :: !input_fileslet* bash = (Elpe.ubuntu "bash-static")#build inlet bash = List.hd bash.destdir inlet f = Filename.temp_dir "elpe-" "" ^ "/setup" inUnix.mkfifo f 0o666; - replacement in elpe/bin/elpe_bin.ml at line 28
let speclist =[("-verbose", Arg.Set verbose, "Output debug information");("-c", Arg.Set_string cmd, "Run the specified command");]let pid =Unix.create_process(bash ^ "/usr/bin/bash-static")(match cmd with| None -> [| "bash"; "--init-file"; f; "-i" |]| Some cmd -> [| "bash"; "--init-file"; f; "-i"; "-c"; cmd |])Unix.stdin Unix.stdout Unix.stderrinlet p = Unix.openfile f [ Unix.O_WRONLY ] 0o644 inlet _ = Unix.write_substring p b 0 (String.length b) inUnix.close p;match Unix.waitpid [] pid with| _, Unix.WEXITED e -> Lwt.return e| _ -> failwith "Unknown")let run_build (spec : Elpe.derivation) =Lwt_main.run(let open Lwt.Syntax inlet port = 50051 inlet address = "127.0.0.1" inlet* c = connection address port inElpe.backend_conn := Some c;let* b = spec#build inprint_endline (List.fold_left (fun _ x -> x) "" b.destdir);Lwt.return ()) - replacement in elpe/bin/elpe_bin.ml at line 56
let compile_and_run () =let compile input_files = - replacement in elpe/bin/elpe_bin.ml at line 67
@ !input_files)@ input_files) - edit in elpe/bin/elpe_bin.ml at line 83
Dynlink.set_allowed_units [ "Elpe" ]; - replacement in elpe/bin/elpe_bin.ml at line 89
Dynlink.loadfile obj;match !last_built_module with| None -> 0| Some last ->run_shell last (if String.length !cmd == 0 then None else Some !cmd)Dynlink.loadfile objlet shell =Core.Command.basic ~summary:""(let%map_open.Command cmd =flag "-c" (optional string) ~doc:"Command to run in this shell"and files = anon (non_empty_sequence_as_list ("file" %: string)) infun () ->Elpe.last_built_module := None;compile files;match !Elpe.last_built_module with| None -> Unix._exit 0| Some last -> Unix._exit (run_shell last cmd))let build =Core.Command.basic ~summary:""(let%map_open.Command files =anon (non_empty_sequence_as_list ("file" %: string))infun () ->Elpe.last_built_module := None;compile files;let _ =match !Elpe.last_built_module with| None -> ()| Some last -> run_build (last :> Elpe.derivation)in()) - replacement in elpe/bin/elpe_bin.ml at line 118
let () =Arg.parse speclist anon_fun usage_msg;if !input_files != [] thenlet exit = compile_and_run () inUnix._exit exitelse Printf.eprintf "\n"[2.64513]let command =Core.Command.group ~summary:"Elpe" [ ("shell", shell); ("build", build) ]let () = Command_unix.run command(* if !input_files != [] then *)(* let exit = compile_and_run () in *)(* Unix._exit exit *)(* else Printf.eprintf "\n" *) - replacement in elpe/bin/dune at line 6
(libraries elpe grpc-lwt dynlink))[2.64790](libraries elpe grpc-lwt dynlink core core_unix.command_unix)(preprocess(pps ppx_jane))) - replacement in elfedit/src/lib.rs at line 585
if id.class == Bits::B32 {if id.class == Bits::B32 { - replacement in elfedit/src/lib.rs at line 591
})}); - replacement in elfedit/src/lib.rs at line 598
})}); - replacement in elfedit/src/lib.rs at line 621
let mut aux_ptr = current.add((&*(current as *const Verneed)).aux as usize) as *const u8;let mut aux_ptr =current.add((&*(current as *const Verneed)).aux as usize) as *const u8; - replacement in elfedit/src/lib.rs at line 628
breakbreak; - replacement in elfedit/src/lib.rs at line 635
breakbreak; - replacement in elfedit/src/lib.rs at line 775
return Err(Error::NoSectionTable)return Err(Error::NoSectionTable); - replacement in elfedit/src/lib.rs at line 784
return Err(Error::NoSectionTable)return Err(Error::NoSectionTable); - replacement in elfedit/src/lib.rs at line 966
Err(Error::NoSectionTable) => {return Ok(None)},Err(e) => return Err(e)Err(Error::NoSectionTable) => return Ok(None),Err(e) => return Err(e), - replacement in elfedit/src/lib.rs at line 1356
self.add_load_segment(id, new_offset, new.len(), rounded_last_vaddr as usize, 6, page_size);self.add_load_segment(id,new_offset,new.len(),rounded_last_vaddr as usize,6,page_size,); - edit in elfedit/src/lib.rs at line 1367
/// Beware: the last segment of the file must be RW. - replacement in elfedit/src/lib.rs at line 1387
p.set_flags(id, flags | 6); // must be RW since it's at the end.p.set_flags(id, flags); - replacement in elfedit/src/lib.rs at line 1489
pub fn update<P: AsRef<std::path::Path>>(mut self, into: Option<P>) -> Result<bool, Error> {pub fn update(mut self, into: Option<&std::path::Path>) -> Result<bool, Error> { - replacement in elfedit/src/lib.rs at line 1526
std::fs::set_permissions(into.as_ref(), self.f.metadata()?.permissions())?;std::fs::set_permissions(into, self.f.metadata()?.permissions())?; - edit in elfedit/src/lib.rs at line 2090
- edit in elfedit/src/lib.rs at line 2110
- edit in default.nix at line 130
};# grpc-src = pkgs.fetchFromGitHub {# owner = "dialohq";# repo = "ocaml-grpc";# rev = "b71fba7067bad2cad62df9abd8b4e190e3c4fc94";# sha256 = "sha256-NtNU0ANMLRFFLoGtHMB0ynHbyh8YTfRF+PttJi9pYHU=";# };grpc-src = pkgs.fetchFromGitHub {owner = "dialohq";repo = "ocaml-grpc";rev = "0.2.0";sha256 = "sha256-YARAm3EVTbptAoKA0lysJRYeKPaCaeWU6X9Lzkn+30E="; - replacement in default.nix at line 149
src = pkgs.fetchurl {url = "https://github.com/dialohq/ocaml-grpc/archive/refs/tags/0.2.0.tar.gz";sha256 = "sha256-5myWWT3qziJ9m84aRXodGRZ5sGlUNBcY/6nkkzJ4in4=";};src = grpc-src; - edit in default.nix at line 161
src = grpc-src;propagatedBuildInputs = with pkgs; [ocamlPackages.stringextocamlPackages.lwtgrpc];};tar = ocamlPackages.buildDunePackage rec {pname = "tar";version = "3.3.0"; - replacement in default.nix at line 173
url = "https://github.com/dialohq/ocaml-grpc/archive/refs/tags/0.2.0.tar.gz";sha256 = "sha256-5myWWT3qziJ9m84aRXodGRZ5sGlUNBcY/6nkkzJ4in4=";url = "https://github.com/mirage/ocaml-tar/archive/refs/tags/v3.3.0.tar.gz";sha256 = "sha256-xxwUxq8G9G8LH8EedLaPAWNvxtpRFiWZyEv8YPgad+g="; - replacement in default.nix at line 177
ocamlPackages.stringextocamlPackages.camlp-streamsocamlPackages.cstructocamlPackages.decompress];};tar-unix = ocamlPackages.buildDunePackage rec {pname = "tar-unix";version = "3.3.0";src = pkgs.fetchurl {url = "https://github.com/mirage/ocaml-tar/archive/refs/tags/v3.3.0.tar.gz";sha256 = "sha256-xxwUxq8G9G8LH8EedLaPAWNvxtpRFiWZyEv8YPgad+g=";};propagatedBuildInputs = with pkgs; [ - replacement in default.nix at line 192
grpctar - edit in default.nix at line 195
tar-lwt-unix = tar-unix.overrideAttrs (_: {name = "tar-lwt-unix"; }); - edit in default.nix at line 207
tartar-lwt-unix - edit in default.nix at line 231
ocamlPackages.coreocamlPackages.core_unixtartar-lwt-unix - replacement in default.nix at line 238
elpe# elpe - replacement in Cargo.toml at line 10
members = ["elfedit"]members = ["elfedit", "elfedit-cli"] - edit in Cargo.toml at line 39
privdrop = "0.5.5"serde_derive = "1.0.219"serde = "1.0.219"tokio-bincode = "0.1.0"tokio-util = "0.7.15"bincode = "2.0.1"futures-util = "0.3.31"bytes = "1.10.1"async-tempfile = "0.7.0"xz = "0.1.0" - edit in Cargo.lock at line 34
[[package]]name = "anstream"version = "0.6.18"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"dependencies = ["anstyle","anstyle-parse","anstyle-query","anstyle-wincon","colorchoice","is_terminal_polyfill","utf8parse",][[package]]name = "anstyle"version = "1.0.10"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - edit in Cargo.lock at line 57
name = "anstyle-parse"version = "0.2.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"dependencies = ["utf8parse",][[package]]name = "anstyle-query"version = "1.1.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"dependencies = ["windows-sys 0.59.0",][[package]]name = "anstyle-wincon"version = "3.0.7"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"dependencies = ["anstyle","once_cell","windows-sys 0.59.0",][[package]] - edit in Cargo.lock at line 102
[[package]]name = "async-tempfile"version = "0.7.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c8a57b75c36e16f4d015e60e6a177552976a99b6947724403c551bcfa7cb1207"dependencies = ["tokio",] - edit in Cargo.lock at line 208
name = "bincode"version = "1.3.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"dependencies = ["serde",][[package]]name = "bincode"version = "2.0.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"dependencies = ["bincode_derive","serde","unty",][[package]]name = "bincode_derive"version = "2.0.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"dependencies = ["virtue",][[package]] - edit in Cargo.lock at line 308
[[package]]name = "cfg_aliases"version = "0.1.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - edit in Cargo.lock at line 316
name = "clap"version = "4.5.38"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"dependencies = ["clap_builder",][[package]]name = "clap_builder"version = "4.5.38"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"dependencies = ["anstream","anstyle","clap_lex","strsim",][[package]]name = "clap_lex"version = "0.7.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"[[package]]name = "colorchoice"version = "1.0.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"[[package]] - edit in Cargo.lock at line 432
name = "elfedit-cli"version = "0.1.0"dependencies = ["clap","elfedit",][[package]] - edit in Cargo.lock at line 443
"async-tempfile", - edit in Cargo.lock at line 445
"bincode 2.0.1", - edit in Cargo.lock at line 447
"bytes 1.10.1", - edit in Cargo.lock at line 451
"futures-util", - edit in Cargo.lock at line 458
"privdrop", - edit in Cargo.lock at line 463
"serde","serde_derive", - edit in Cargo.lock at line 470
"tokio-bincode","tokio-util", - edit in Cargo.lock at line 476
"xz", - edit in Cargo.lock at line 1031
[[package]]name = "is_terminal_polyfill"version = "1.70.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - edit in Cargo.lock at line 1226
name = "nix"version = "0.28.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"dependencies = ["bitflags","cfg-if","cfg_aliases","libc",][[package]] - edit in Cargo.lock at line 1383
][[package]]name = "privdrop"version = "0.5.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "879d008129b086c1c067a3b7ce406bb9766c29f20387e7883724d8dddbce7064"dependencies = ["libc","nix", - edit in Cargo.lock at line 1802
[[package]]name = "strsim"version = "0.11.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - edit in Cargo.lock at line 1949
name = "tokio-bincode"version = "0.1.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0caad59482605518068065f14e2f91f43297d7240fa78dab9b4ba289ad0cf5b1"dependencies = ["bincode 1.3.3","bytes 0.4.12","serde","tokio-codec",][[package]]name = "tokio-codec"version = "0.1.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"dependencies = ["bytes 0.4.12","futures 0.1.31","tokio-io",][[package]] - replacement in Cargo.lock at line 2026
version = "0.7.13"version = "0.7.15" - replacement in Cargo.lock at line 2028
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" - edit in Cargo.lock at line 2195
[[package]]name = "unty"version = "0.0.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - edit in Cargo.lock at line 2226
name = "utf8parse"version = "0.2.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"[[package]] - edit in Cargo.lock at line 2250
name = "virtue"version = "0.0.18"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"[[package]] - edit in Cargo.lock at line 2537
][[package]]name = "xz"version = "0.1.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "3c887690ff2a2e233e8e49633461521f98ec57fbff9d59a884c9a4f04ec1da34"dependencies = ["xz2",