add context to all fs calls (#423)

Adds additional context in error handling to all `std::fs` and `tokio::fs` calls.

Fixes #309
This commit is contained in:
bmc-msft
2021-01-11 15:55:22 -05:00
committed by GitHub
parent d573100a97
commit 465727680d
14 changed files with 166 additions and 52 deletions

View File

@ -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<bool> {
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())
}

View File

@ -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());
}

View File

@ -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()
)
})?;
}
}
}

View File

@ -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<Path>) -> Result<Vec<EventData>, 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);

View File

@ -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<Path>) -> Result<Self> {
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<Self> {
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)

View File

@ -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(())
}

View File

@ -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))

View File

@ -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 {}",

View File

@ -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(())
}

View File

@ -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 = {}",

View File

@ -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<PathBuf> {
}
pub async fn has_files(path: impl AsRef<Path>) -> Result<bool> {
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<Path>) -> Result<Vec<PathBuf>> {
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<Path>, 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<Path>) -> 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(())
}

View File

@ -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<Option<String>> {
#[cfg(target_os = "linux")]
pub async fn get_os_machine_id() -> Result<Uuid> {
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)
}

View File

@ -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<Item = impl AsRef<[u8]>>) -> String {
}
pub async fn digest_file(file: impl AsRef<Path>) -> Result<String> {
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)))
}

View File

@ -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())
}),
}
}