uutils/coreutils · error · io::Error
mv-error-dest-appeared (path)
Error message
mv-error-dest-appeared (path)
What it means
`create_dir_fail_closed` is a security-hardening wrapper: after mv removes a destination, `create_dir_all` would silently accept a symlink planted at that path (TOCTOU attack redirecting the move). It uses plain `create_dir` and, if the path already exists, raises this error naming the path rather than following it.
Solutions
- Ensure no other process writes into the destination directory during the move
- Move into a fresh, private destination (e.g. under a 0700 directory)
- Re-run the mv after the concurrent writer finished
- If you see this in a shared /tmp-like location, treat it as a security signal and audit for symlink attacks
Example fix
// before mv src /shared/dest # /shared/dest touched concurrently // after mkdir /tmp/mv-$$ && chmod 700 /tmp/mv-$$ && mv src /tmp/mv-$$/dest
Defensive patterns
Strategy: retry
Validate before calling
# ensure destination tree is quiescent
fuser -s "$dest" 2>/dev/null && { echo 'dest in use' >&2; exit 1; } Type guard
fn is_symlink(p: &Path) -> bool { p.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false) } Try / catch
match mv_result {
Err(e) if e.to_string().contains("destination appeared") => {
eprintln!("concurrent modification at destination; retry after quiescing writers");
}
r => r?,
} Prevention
- Never share a destination directory between concurrent mv/cp processes
- Move into private, freshly created directories
- In multi-user tmp dirs, use mktemp -d with 0700 perms
- Treat this error as a possible symlink attack, not a benign race
When it happens
Trigger: During `mv src dest_dir` with directory-copy fallback (`copy_dir_contents`), something (another process or attacker) creates a file/symlink at a subdirectory path of the destination between removal and creation.
Common situations: Concurrent processes writing into the same destination tree, hostile multi-user tmp directories (symlink attacks), or racy build scripts re-creating paths mid-move.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- could not allocate a unique temp name in destination…
- invalid destination path
- mv-error-dangling-symlink
- mv-error-dest-appeared
- mv-error-inter-device-move-failed
AI-assisted analysis of uutils/coreutils@92995feca2 (2026-09-20).
Data as JSON: /api/errors/1180747e25d5b8b9.
Report an issue: GitHub.
Appendix: source
Thrown at src/uu/mv/src/mv.rs:1184
}
result?;
// Remove the source directory after successful copy
fs::remove_dir_all(from)?;
Ok(())
}
/// Copy directory recursively, optionally preserving hardlinks
/// Create `path`, refusing to reuse anything already there.
///
/// `create_dir_all` would accept a symlink planted at `path` after the caller
/// removed the destination, redirecting the move out of the destination tree.
fn create_dir_fail_closed(path: &Path) -> io::Result<()> {
fs::create_dir(path).map_err(|e| {
if e.kind() == io::ErrorKind::AlreadyExists {
io::Error::new(
io::ErrorKind::AlreadyExists,
translate!("mv-error-dest-appeared", "path" => path.quote()),
)
} else {
e
}
})
}
fn get_dir_size(path: &Path) -> io::Result<u64> {
let metadata = path.symlink_metadata()?;
if !metadata.is_dir() {
return Ok(metadata.len());
}
let mut size = 0;
for entry in fs::read_dir(path)? {View on GitHub (pinned to 92995feca2)