fix(sd_bench): remove the scratch tree without trusting dirent types

std's remove_dir_all reads directories as char devices through the
prebuilt libc's generic-unix DT table, unlinks them, and FatFS refuses
with FR_DENIED — the 6b fan-out subdirs were the first to trigger it.
Recurse with the same both-tables decoding as the palette walk.
This commit is contained in:
Julien Calixte
2026-07-14 09:44:36 +02:00
parent cdfeabc152
commit 7378d30450

View File

@@ -65,7 +65,7 @@ fn run() -> Result<()> {
);
// Fresh scratch dir.
let _ = fs::remove_dir_all(BENCH_DIR);
let _ = remove_tree(std::path::Path::new(BENCH_DIR));
fs::create_dir_all(BENCH_DIR).with_context(|| format!("creating {BENCH_DIR}"))?;
// Warm-up: the first write after mount pays one-time settling — don't measure it.
@@ -185,7 +185,8 @@ fn run() -> Result<()> {
}
// Clean up so the card is left as we found it.
fs::remove_dir_all(BENCH_DIR).with_context(|| format!("removing {BENCH_DIR}"))?;
remove_tree(std::path::Path::new(BENCH_DIR))
.with_context(|| format!("removing {BENCH_DIR}"))?;
// 7) THE ~1.5 s LOOSE-WRITE SUSPECT (git_bench, 2026-07-12 second real-repo
// run): lseek inside a huge file. Without CONFIG_FATFS_USE_FASTSEEK,
@@ -258,6 +259,41 @@ fn find_pack() -> Result<Option<String>> {
Ok(best.map(|(_, p)| p))
}
/// `fs::remove_dir_all` replacement: std's version trusts the dirent file
/// type, and the prebuilt std decodes esp-idf's DT constants with the generic
/// unix table (files read as fifos, directories as char devices — same story
/// as the palette walk in main.rs). It therefore `unlink`s subdirectories,
/// which FatFS refuses with FR_DENIED (EACCES) when they're non-empty. Decode
/// the type the same both-tables way and recurse ourselves.
fn remove_tree(dir: &std::path::Path) -> Result<()> {
use std::os::unix::fs::FileTypeExt;
let entries = match fs::read_dir(dir) {
Ok(e) => e,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(e) => return Err(e.into()),
};
let children: Vec<_> = entries
.flatten()
.filter_map(|e| e.file_type().ok().map(|t| (e.path(), t)))
.collect();
for (path, ftype) in children {
let is_dir = if ftype.is_dir() || ftype.is_char_device() {
true
} else if ftype.is_file() || ftype.is_fifo() {
false
} else {
fs::metadata(&path)?.is_dir()
};
if is_dir {
remove_tree(&path)?;
} else {
fs::remove_file(&path)?;
}
}
fs::remove_dir(dir)?;
Ok(())
}
/// Run `op(i)` for `i in 0..N`, returning each call's wall time in microseconds.
fn time_each<F: FnMut(usize) -> Result<()>>(mut op: F) -> Result<Vec<u64>> {
let mut times = Vec::with_capacity(N);