{"record":{"id":"65f34e8225eb1eda","repo":"uutils/coreutils","slug":"mv-error-dest-appeared","errorCode":null,"errorMessage":"mv-error-dest-appeared","messagePattern":"mv-error-dest-appeared","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/uu/mv/src/mv.rs","lineNumber":1219,"sourceCode":"    }\n\n    result?;\n\n    // Remove the source directory after successful copy\n    fs::remove_dir_all(from)?;\n\n    Ok(())\n}\n\n/// Copy directory recursively, optionally preserving hardlinks\n/// Create `path`, refusing to reuse anything already there.\n///\n/// `create_dir_all` would accept a symlink planted at `path` after the caller\n/// removed the destination, redirecting the move out of the destination tree.\nfn create_dir_fail_closed(path: &Path) -> io::Result<()> {\n    fs::create_dir(path).map_err(|e| {\n        if e.kind() == io::ErrorKind::AlreadyExists {\n            io::Error::new(\n                io::ErrorKind::AlreadyExists,\n                translate!(\"mv-error-dest-appeared\", \"path\" => path.quote()),\n            )\n        } else {\n            e\n        }\n    })\n}\n\nfn get_dir_size(path: &Path) -> io::Result<u64> {\n    let metadata = path.symlink_metadata()?;\n\n    if !metadata.is_dir() {\n        return Ok(metadata.len());\n    }\n\n    let mut size = 0;\n    for entry in fs::read_dir(path)? {","sourceCodeStart":1201,"sourceCodeEnd":1237,"githubUrl":"https://github.com/uutils/coreutils/blob/85295bbf788bfd7a6926ba692031563504b304b7/src/uu/mv/src/mv.rs#L1201-L1237","documentation":"create_dir_fail_closed uses plain fs::create_dir (not create_dir_all) when rebuilding the destination directory tree during mv's copy fallback. If the destination path suddenly already exists, it maps AlreadyExists to this localized 'destination appeared' error including the quoted path. This is a deliberate fail-closed TOCTOU defense: create_dir_all would silently follow a symlink planted at `path` after the caller removed the destination, letting the move escape the destination tree.","triggerScenarios":"A concurrent process creates a file or symlink at exactly the directory path mv is about to create, between mv's remove of the old destination and its create_dir call; produces AlreadyExists from fs::create_dir which is translated to this error.","commonSituations":"Another mv/cp/rsync instance running in parallel on the same destination; a watcher or build system recreating the directory; an attacker planting a symlink at the destination path; NFS or network shares with stale caching.","solutions":["Re-run the mv once the concurrent writer is finished; the race is transient.","Ensure only one process moves into the same destination (use a lock file or flock).","Check `path` for an unexpected file/symlink and remove it, then retry.","If a race attack is suspected, audit the destination directory permissions before retrying."],"exampleFix":"// before\nmv src dst & mv src2 dst &        // two movers race on dst/\n// after\nflock /tmp/mv-dst.lock mv src dst && flock /tmp/mv-dst.lock mv src2 dst","handlingStrategy":"retry","validationCode":"if path.symlink_metadata().is_ok() {\n    eprintln!(\"destination already occupied: {}\", path.display());\n}\n// also ensure no concurrent movers:\n// flock lockfile around mv invocation","typeGuard":"fn dest_is_clean_symlink(path: &Path) -> bool {\n    path.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false)\n}","tryCatchPattern":"match mv_result {\n    Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {\n        // destination appeared mid-move: serialize with a lock and retry once\n    }\n    r => r?,\n}","preventionTips":["Serialize moves into shared destinations with flock/lock files","Keep destination directories writable only by the moving process","Audit destination dirs for unexpected symlinks before scripted moves"],"tags":["mv","race-condition","toctou","filesystem"],"backgroundTag":"destination-already-exists","analyzedSha":"85295bbf788bfd7a6926ba692031563504b304b7","analyzedAt":"2026-08-31T11:11:36.175Z","contentChangedAt":"2026-08-31T11:11:36.175Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}