mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
fix(object-capacity): resolve a symlinked scan root instead of silently counting zero (#4564)
walkdir defaults to follow_root_links=true precisely so a root that is itself a symlink still descends, but the scanner overrode it with the general follow_symlinks flag (default false). For the common indirection layout (/data/rustfs0 -> /mnt/vol0) the walk yielded a single skipped symlink entry and returned used_bytes=0, file_count=0, is_estimated=false with no error and no log — an exact 0 committed straight into the per-disk baseline (S05). Canonicalize the scan root before walking (also covering nested indirection and keeping the dedicated-mount statvfs check on the real path); resolution failures surface as scan errors instead of a silent zero. Inner-symlink policy is unchanged. Ref: rustfs/backlog#1015 (S05 from audit rustfs/backlog#1010)
This commit is contained in:
@@ -733,6 +733,16 @@ fn scan_dir_blocking(path: &Path, limits: &ScanLimits, cancelled: &AtomicBool) -
|
||||
));
|
||||
}
|
||||
|
||||
// Resolve the scan root before walking. A drive_path that is itself a
|
||||
// symlink (container/k8s indirection) combined with
|
||||
// follow_root_links(false) used to yield a single skipped symlink
|
||||
// entry and commit an exact 0-byte result into the per-disk baseline
|
||||
// with no error and no log (backlog#1015 S05). Resolution failures are
|
||||
// surfaced instead of silently counting zero.
|
||||
let resolved_root = std::fs::canonicalize(path)
|
||||
.map_err(|err| std::io::Error::new(err.kind(), format!("failed to resolve scan root {path:?}: {err}")))?;
|
||||
let path = resolved_root.as_path();
|
||||
|
||||
let start_time = Instant::now();
|
||||
let mut exact_prefix_bytes = 0u64;
|
||||
let mut overflow_sampled_bytes = 0u64;
|
||||
@@ -1419,6 +1429,30 @@ mod tests {
|
||||
assert_eq!(outer_scan_budget(&tight_limits(Duration::ZERO)), Duration::from_secs(5));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_scan_dir_blocking_resolves_symlink_root() {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
let target_dir = tempfile::TempDir::new().unwrap();
|
||||
for i in 0..3 {
|
||||
let mut file = File::create(target_dir.path().join(format!("f{i}"))).unwrap();
|
||||
file.write_all(b"data").unwrap();
|
||||
}
|
||||
|
||||
// drive_path is a symlink to the real data directory; with symlink
|
||||
// following disabled this used to return exact 0 bytes / 0 files.
|
||||
let link_parent = tempfile::TempDir::new().unwrap();
|
||||
let link = link_parent.path().join("rustfs0");
|
||||
std::os::unix::fs::symlink(target_dir.path(), &link).unwrap();
|
||||
|
||||
let result = scan_dir_blocking(&link, &tight_limits(Duration::from_secs(600)), &AtomicBool::new(false)).unwrap();
|
||||
assert_eq!(result.used_bytes, 12);
|
||||
assert_eq!(result.file_count, 3);
|
||||
assert!(!result.is_estimated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scan_dir_blocking_generous_budget_still_exact() {
|
||||
use std::fs::File;
|
||||
|
||||
Reference in New Issue
Block a user