diff --git a/src/agent/onefuzz-agent/src/tasks/coverage/libfuzzer_coverage.rs b/src/agent/onefuzz-agent/src/tasks/coverage/libfuzzer_coverage.rs index 0b7f8762b..19edc58ad 100644 --- a/src/agent/onefuzz-agent/src/tasks/coverage/libfuzzer_coverage.rs +++ b/src/agent/onefuzz-agent/src/tasks/coverage/libfuzzer_coverage.rs @@ -36,7 +36,7 @@ use crate::tasks::{ coverage::{recorder::CoverageRecorder, total::TotalCoverage}, utils::default_bool_true, }; -use anyhow::Result; +use anyhow::{Context, Result}; use async_trait::async_trait; use futures::stream::StreamExt; use onefuzz::{ @@ -124,7 +124,9 @@ impl CoverageTask { seen_inputs = true; } - fs::remove_dir_all(&dir.path).await?; + fs::remove_dir_all(&dir.path).await.with_context(|| { + format!("unable to remove readonly_inputs: {}", dir.path.display()) + })?; } if seen_inputs { @@ -154,7 +156,12 @@ impl CoverageTask { processor: &mut CoverageProcessor, corpus_dir: &SyncedDir, ) -> Result { - let mut corpus = fs::read_dir(&corpus_dir.path).await?; + let mut corpus = fs::read_dir(&corpus_dir.path).await.with_context(|| { + format!( + "unable to read corpus coverage directory: {}", + corpus_dir.path.display() + ) + })?; let mut seen_inputs = false; while let Some(input) = corpus.next().await { @@ -208,7 +215,12 @@ impl CoverageProcessor { if !self.module_totals.contains_key(&module) { let parent = &self.config.coverage.path.join("by-module"); - fs::create_dir_all(parent).await?; + fs::create_dir_all(parent).await.with_context(|| { + format!( + "unable to create by-module coverage directory: {}", + parent.display() + ) + })?; let module_total = parent.join(&module); let total = TotalCoverage::new(module_total); self.module_totals.insert(module.clone(), total); @@ -226,7 +238,9 @@ impl CoverageProcessor { for file in &files { verbose!("checking {:?}", file); - let mut content = fs::read(file).await?; + let mut content = fs::read(file) + .await + .with_context(|| format!("unable to read module coverage: {}", file.display()))?; self.update_module_total(file, &content).await?; sum.append(&mut content); } @@ -234,7 +248,9 @@ impl CoverageProcessor { let mut combined = path.as_os_str().to_owned(); combined.push(".cov"); - fs::write(&combined, sum).await?; + fs::write(&combined, sum) + .await + .with_context(|| format!("unable to write combined coverage file: {:?}", combined))?; Ok(combined.into()) } diff --git a/src/agent/onefuzz-agent/src/tasks/coverage/recorder.rs b/src/agent/onefuzz-agent/src/tasks/coverage/recorder.rs index 1556e25e1..857ba191f 100644 --- a/src/agent/onefuzz-agent/src/tasks/coverage/recorder.rs +++ b/src/agent/onefuzz-agent/src/tasks/coverage/recorder.rs @@ -8,7 +8,7 @@ use std::{ sync::Arc, }; -use anyhow::Result; +use anyhow::{Context, Result}; use onefuzz::{ fs::{has_files, OwnedDir}, sha256::digest_file, @@ -47,7 +47,12 @@ impl CoverageRecorder { self.config.coverage.path.join("inputs").join(digest) }; - fs::create_dir_all(&coverage_path).await?; + fs::create_dir_all(&coverage_path).await.with_context(|| { + format!( + "unable to create coverage path: {}", + coverage_path.display() + ) + })?; let script = self.invoke_debugger_script(test_input, &coverage_path)?; let output = script.wait_with_output().await?; @@ -77,7 +82,14 @@ impl CoverageRecorder { } if !has_files(&coverage_path).await? { - tokio::fs::remove_dir(&coverage_path).await?; + tokio::fs::remove_dir(&coverage_path) + .await + .with_context(|| { + format!( + "unable to remove coverage path: {}", + coverage_path.display() + ) + })?; bail!("no coverage files for input: {}", test_input.display()); } diff --git a/src/agent/onefuzz-agent/src/tasks/fuzz/libfuzzer_fuzz.rs b/src/agent/onefuzz-agent/src/tasks/fuzz/libfuzzer_fuzz.rs index 26a353643..29ffd8d53 100644 --- a/src/agent/onefuzz-agent/src/tasks/fuzz/libfuzzer_fuzz.rs +++ b/src/agent/onefuzz-agent/src/tasks/fuzz/libfuzzer_fuzz.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use crate::tasks::{config::CommonConfig, heartbeat::HeartbeatSender, utils::default_bool_true}; -use anyhow::Result; +use anyhow::{Context, Result}; use futures::{future::try_join_all, stream::StreamExt}; use onefuzz::{ fs::list_files, @@ -120,7 +120,15 @@ impl LibFuzzerFuzzTask { let mut entries = tokio::fs::read_dir(local_input_dir.path()).await?; while let Some(Ok(entry)) = entries.next().await { let destination_path = self.config.inputs.path.clone().join(entry.file_name()); - tokio::fs::rename(entry.path(), destination_path).await?; + tokio::fs::rename(&entry.path(), &destination_path) + .await + .with_context(|| { + format!( + "unable to move crashing input into results directory: {} - {}?", + entry.path().display(), + destination_path.display() + ) + })?; } } } diff --git a/src/agent/onefuzz-agent/src/tasks/stats/afl.rs b/src/agent/onefuzz-agent/src/tasks/stats/afl.rs index 4a2115767..194a25e71 100644 --- a/src/agent/onefuzz-agent/src/tasks/stats/afl.rs +++ b/src/agent/onefuzz-agent/src/tasks/stats/afl.rs @@ -1,13 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use anyhow::{Error, Result}; +use anyhow::{Context, Error, Result}; use onefuzz::telemetry::EventData; use std::path::Path; use tokio::io::AsyncBufReadExt; pub async fn read_stats(output_path: impl AsRef) -> Result, Error> { - let f = tokio::fs::File::open(output_path).await?; + let output_path = output_path.as_ref(); + let f = tokio::fs::File::open(&output_path).await.with_context(|| { + format!( + "unable to open AFL stats for read: {}", + output_path.display() + ) + })?; let mut stats = Vec::new(); let reader = tokio::io::BufReader::new(f); diff --git a/src/agent/onefuzz-supervisor/src/config.rs b/src/agent/onefuzz-supervisor/src/config.rs index d235bbbd7..8d496cb97 100644 --- a/src/agent/onefuzz-supervisor/src/config.rs +++ b/src/agent/onefuzz-supervisor/src/config.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use anyhow::Result; +use anyhow::{Context, Result}; use onefuzz::{ http::{is_auth_error_code, ResponseExt}, jitter::delay_with_jitter, @@ -83,7 +83,9 @@ impl StaticConfig { } pub fn from_file(config_path: impl AsRef) -> Result { - let data = std::fs::read(config_path)?; + let config_path = config_path.as_ref(); + let data = std::fs::read(config_path) + .with_context(|| format!("unable to read config file: {}", config_path.display()))?; Self::new(&data) } @@ -151,14 +153,18 @@ impl DynamicConfig { pub async fn save(&self) -> Result<()> { let path = Self::save_path()?; let data = serde_json::to_vec(&self)?; - fs::write(&path, &data).await?; + fs::write(&path, &data) + .await + .with_context(|| format!("unable to save dynamic config: {}", path.display()))?; info!("saved dynamic-config: {}", path.display()); Ok(()) } pub async fn load() -> Result { let path = Self::save_path()?; - let data = fs::read(&path).await?; + let data = fs::read(&path) + .await + .with_context(|| format!("unable to load dynamic config: {}", path.display()))?; let ctx: Self = serde_json::from_slice(&data)?; info!("loaded dynamic-config: {}", path.display()); Ok(ctx) diff --git a/src/agent/onefuzz-supervisor/src/done.rs b/src/agent/onefuzz-supervisor/src/done.rs index 3f63e43ae..8c10fc988 100644 --- a/src/agent/onefuzz-supervisor/src/done.rs +++ b/src/agent/onefuzz-supervisor/src/done.rs @@ -4,12 +4,15 @@ use std::fs::metadata; use std::path::PathBuf; -use anyhow::Result; +use anyhow::{Context, Result}; use onefuzz::fs::onefuzz_root; use tokio::fs; pub async fn set_done_lock() -> Result<()> { - fs::write(done_path()?, "").await?; + let path = done_path()?; + fs::write(&path, "") + .await + .with_context(|| format!("unable to write done lock: {}", path.display()))?; Ok(()) } diff --git a/src/agent/onefuzz-supervisor/src/reboot.rs b/src/agent/onefuzz-supervisor/src/reboot.rs index 518c4f16b..e0fa264f1 100644 --- a/src/agent/onefuzz-supervisor/src/reboot.rs +++ b/src/agent/onefuzz-supervisor/src/reboot.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::process::Command; -use anyhow::Result; +use anyhow::{Context, Result}; use downcast_rs::Downcast; use tokio::fs; @@ -40,13 +40,14 @@ pub struct Reboot; impl Reboot { pub async fn save_context(&mut self, ctx: RebootContext) -> Result<()> { - info!( - "saving reboot context to: {}", - reboot_context_path()?.display() - ); + let path = reboot_context_path()?; + + info!("saving reboot context to: {}", path.display()); let data = serde_json::to_vec(&ctx)?; - fs::write(reboot_context_path()?, &data).await?; + fs::write(&path, &data) + .await + .with_context(|| format!("unable to save reboot context: {}", path.display()))?; verbose!("reboot context saved"); @@ -72,7 +73,9 @@ impl Reboot { let data = data?; let ctx = serde_json::from_slice(&data)?; - fs::remove_file(&path).await?; + fs::remove_file(&path) + .await + .with_context(|| format!("unable to remove reboot context: {}", path.display()))?; info!("loaded reboot context"); Ok(Some(ctx)) diff --git a/src/agent/onefuzz-supervisor/src/setup.rs b/src/agent/onefuzz-supervisor/src/setup.rs index 519637c31..219a67277 100644 --- a/src/agent/onefuzz-supervisor/src/setup.rs +++ b/src/agent/onefuzz-supervisor/src/setup.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use std::process::Stdio; -use anyhow::Result; +use anyhow::{Context, Result}; use downcast_rs::Downcast; use onefuzz::az_copy; use onefuzz::process::Output; @@ -48,7 +48,9 @@ impl SetupRunner { .join(setup_dir); // `azcopy sync` requires the local dir to exist. - fs::create_dir_all(&setup_dir).await?; + fs::create_dir_all(&setup_dir).await.with_context(|| { + format!("unable to create setup container: {}", setup_dir.display()) + })?; az_copy::sync(setup_url.to_string(), &setup_dir, false).await?; verbose!( @@ -106,8 +108,15 @@ async fn create_setup_symlink(setup_dir: &Path, work_unit: &WorkUnit) -> Result< use std::os::windows::fs::symlink_dir; use tokio::task::spawn_blocking; - fs::create_dir(&work_unit.working_dir()?).await?; - let task_setup_dir = work_unit.working_dir()?.join("setup"); + let working_dir = work_unit.working_dir()?; + + fs::create_dir(&working_dir).await.with_context(|| { + format!( + "unable to create working directory: {}", + working_dir.display() + ) + })?; + let task_setup_dir = working_dir.join("setup"); // Tokio does not ship async versions of the `std::fs::os` symlink // functions (unlike the Unix equivalents). @@ -129,10 +138,27 @@ async fn create_setup_symlink(setup_dir: &Path, work_unit: &WorkUnit) -> Result< async fn create_setup_symlink(setup_dir: &Path, work_unit: &WorkUnit) -> Result<()> { use tokio::fs::os::unix::symlink; - tokio::fs::create_dir_all(work_unit.working_dir()?).await?; + let working_dir = work_unit.working_dir()?; - let task_setup_dir = work_unit.working_dir()?.join("setup"); - symlink(&setup_dir, &task_setup_dir).await?; + tokio::fs::create_dir_all(&working_dir) + .await + .with_context(|| { + format!( + "unable to create working directory: {}", + working_dir.display() + ) + })?; + + let task_setup_dir = working_dir.join("setup"); + symlink(&setup_dir, &task_setup_dir) + .await + .with_context(|| { + format!( + "unable to create symlink from {} to {}", + setup_dir.display(), + task_setup_dir.display() + ) + })?; verbose!( "created symlink from {} to {}", diff --git a/src/agent/onefuzz-supervisor/src/work.rs b/src/agent/onefuzz-supervisor/src/work.rs index 199dbbff1..e66aef540 100644 --- a/src/agent/onefuzz-supervisor/src/work.rs +++ b/src/agent/onefuzz-supervisor/src/work.rs @@ -4,7 +4,7 @@ use std::io::ErrorKind; use std::path::PathBuf; -use anyhow::Result; +use anyhow::{Context, Result}; use downcast_rs::Downcast; use onefuzz::{blob::BlobContainerUrl, http::is_auth_error}; use storage_queue::QueueClient; @@ -63,7 +63,9 @@ impl WorkSet { info!("saving workset context: {}", path.display()); let data = serde_json::to_vec(&self)?; - fs::write(path, &data).await?; + fs::write(&path, &data) + .await + .with_context(|| format!("unable to save WorkSet context: {}", path.display()))?; Ok(()) } diff --git a/src/agent/onefuzz-supervisor/src/worker.rs b/src/agent/onefuzz-supervisor/src/worker.rs index 3f0890a68..a75ae223f 100644 --- a/src/agent/onefuzz-supervisor/src/worker.rs +++ b/src/agent/onefuzz-supervisor/src/worker.rs @@ -3,7 +3,7 @@ use std::process::{Child, Command, Stdio}; -use anyhow::Result; +use anyhow::{Context as AnyhowContext, Result}; use downcast_rs::Downcast; use onefuzz::process::{ExitStatus, Output}; use tokio::fs; @@ -189,13 +189,20 @@ impl IWorkerRunner for WorkerRunner { verbose!("worker working dir = {}", working_dir.display()); - fs::create_dir_all(&working_dir).await?; + fs::create_dir_all(&working_dir).await.with_context(|| { + format!( + "unable to create working directory: {}", + working_dir.display() + ) + })?; verbose!("created worker working dir: {}", working_dir.display()); let config_path = work.config_path()?; - fs::write(&config_path, work.config.expose_ref()).await?; + fs::write(&config_path, work.config.expose_ref()) + .await + .with_context(|| format!("unable to save task config: {}", config_path.display()))?; verbose!( "wrote worker config to config_path = {}", diff --git a/src/agent/onefuzz/src/fs.rs b/src/agent/onefuzz/src/fs.rs index ceb8ef876..099242f59 100644 --- a/src/agent/onefuzz/src/fs.rs +++ b/src/agent/onefuzz/src/fs.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use anyhow::Result; +use anyhow::{Context, Result}; use futures::stream::StreamExt; use std::path::{Path, PathBuf}; #[cfg(target_os = "linux")] @@ -25,13 +25,19 @@ pub fn onefuzz_etc() -> Result { } pub async fn has_files(path: impl AsRef) -> Result { - let mut paths = fs::read_dir(&path).await?; + let path = path.as_ref(); + let mut paths = fs::read_dir(&path) + .await + .with_context(|| format!("unable to check if directory has files: {}", path.display()))?; let result = paths.next_entry().await?.is_some(); Ok(result) } pub async fn list_files(path: impl AsRef) -> Result> { - let paths = fs::read_dir(&path).await?; + let path = path.as_ref(); + let paths = fs::read_dir(&path) + .await + .with_context(|| format!("unable to list files: {}", path.display()))?; let mut files = paths .filter_map(|x| async { @@ -106,8 +112,12 @@ pub async fn write_file(path: impl AsRef, content: &str) -> Result<()> { let parent = path .parent() .ok_or_else(|| format_err!("no parent for: {}", path.display()))?; - fs::create_dir_all(parent).await?; - fs::write(path, content).await?; + fs::create_dir_all(parent) + .await + .with_context(|| format!("unable to create nested path: {}", parent.display()))?; + fs::write(path, content) + .await + .with_context(|| format!("unable to write file: {}", path.display()))?; Ok(()) } @@ -115,10 +125,14 @@ pub async fn reset_dir(dir: impl AsRef) -> Result<()> { let dir = dir.as_ref(); if exists(dir).await? { - fs::remove_dir_all(dir).await?; + fs::remove_dir_all(dir).await.with_context(|| { + format!("unable to remove directory and contents: {}", dir.display()) + })?; } - fs::create_dir_all(dir).await?; + fs::create_dir_all(dir) + .await + .with_context(|| format!("unable to create directory: {}", dir.display()))?; Ok(()) } @@ -143,7 +157,9 @@ impl OwnedDir { } pub async fn create_if_missing(&self) -> Result<()> { - fs::create_dir_all(self.path()).await?; + fs::create_dir_all(self.path()) + .await + .with_context(|| format!("unable to create directory: {}", self.path().display()))?; Ok(()) } diff --git a/src/agent/onefuzz/src/machine_id.rs b/src/agent/onefuzz/src/machine_id.rs index 341204087..3e3a4ca1c 100644 --- a/src/agent/onefuzz/src/machine_id.rs +++ b/src/agent/onefuzz/src/machine_id.rs @@ -2,8 +2,9 @@ // Licensed under the MIT License. use crate::fs::{onefuzz_etc, write_file}; -use anyhow::Result; +use anyhow::{Context, Result}; use reqwest_retry::SendRetry; +use std::path::Path; use std::time::Duration; use tokio::fs; use uuid::Uuid; @@ -83,7 +84,10 @@ pub async fn get_scaleset_name() -> Result> { #[cfg(target_os = "linux")] pub async fn get_os_machine_id() -> Result { - let contents = fs::read_to_string("/etc/machine-id").await?; + let path = Path::new("/etc/machine-id"); + let contents = fs::read_to_string(&path) + .await + .with_context(|| format!("unable to read machine_id: {}", path.display()))?; let uuid = Uuid::parse_str(contents.trim())?; Ok(uuid) } diff --git a/src/agent/onefuzz/src/sha256.rs b/src/agent/onefuzz/src/sha256.rs index 8397f6581..37059746c 100644 --- a/src/agent/onefuzz/src/sha256.rs +++ b/src/agent/onefuzz/src/sha256.rs @@ -3,7 +3,7 @@ use std::path::Path; -use anyhow::Result; +use anyhow::{Context, Result}; use sha2::{Digest, Sha256}; use tokio::fs; @@ -22,7 +22,10 @@ pub fn digest_iter(data: impl IntoIterator>) -> String { } pub async fn digest_file(file: impl AsRef) -> Result { - let data = fs::read(file).await?; + let file = file.as_ref(); + let data = fs::read(file) + .await + .with_context(|| format!("unable to read file to generate digest: {}", file.display()))?; Ok(hex::encode(Sha256::digest(&data))) } diff --git a/src/agent/onefuzz/src/syncdir.rs b/src/agent/onefuzz/src/syncdir.rs index e6c308dc4..3a41104cd 100644 --- a/src/agent/onefuzz/src/syncdir.rs +++ b/src/agent/onefuzz/src/syncdir.rs @@ -9,7 +9,7 @@ use crate::{ telemetry::{Event, EventData}, uploader::BlobUploader, }; -use anyhow::Result; +use anyhow::{Context, Result}; use futures::stream::StreamExt; use std::{path::PathBuf, str, time::Duration}; use tokio::fs; @@ -55,7 +55,9 @@ impl SyncedDir { anyhow::bail!("File with name '{}' already exists", self.path.display()); } } - Err(_) => fs::create_dir(&self.path).await.map_err(|e| e.into()), + Err(_) => fs::create_dir(&self.path).await.with_context(|| { + format!("unable to create local SyncedDir: {}", self.path.display()) + }), } }