{"record":{"id":"79605d521358c27c","repo":"uutils/coreutils","slug":"mv-error-permission-denied","errorCode":null,"errorMessage":"mv-error-permission-denied","messagePattern":"mv-error-permission-denied","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/uu/mv/src/mv.rs","lineNumber":1446,"sourceCode":"            {\n                // Create a hardlink to the first moved file instead of copying\n                fs::hard_link(&existing_target, to)?;\n                fs::remove_file(from)?;\n                return Ok(());\n            }\n        }\n    }\n\n    // Open src/dst with O_NOFOLLOW and keep the fds alive across copy,\n    // chown, xattr, and chmod so a concurrent path-swap can't redirect any\n    // step to a different inode.\n    #[cfg(unix)]\n    {\n        use std::fs::Permissions;\n        use std::os::unix::fs::{MetadataExt, PermissionsExt};\n        use uucore::safe_copy::{create_dest_restrictive, open_source};\n        let src_file = open_source(from, /* nofollow */ true)\n            .map_err(|err| io::Error::new(err.kind(), translate!(\"mv-error-permission-denied\")))?;\n        let src_mode = src_file\n            .metadata()\n            .map_err(|err| io::Error::new(err.kind(), translate!(\"mv-error-permission-denied\")))?\n            .mode()\n            & 0o7777;\n        let mut dst_file = create_dest_restrictive(to, /* nofollow */ true)\n            .map_err(|err| io::Error::new(err.kind(), translate!(\"mv-error-permission-denied\")))?;\n        uucore::buf_copy::copy_fast(&mut &src_file, &mut dst_file)\n            .map_err(|err| io::Error::new(err.kind(), translate!(\"mv-error-permission-denied\")))?;\n\n        #[cfg(any(\n            target_os = \"freebsd\",\n            target_os = \"hurd\",\n            target_os = \"linux\",\n            target_os = \"android\",\n            target_os = \"netbsd\"\n        ))]\n        {","sourceCodeStart":1428,"sourceCodeEnd":1464,"githubUrl":"https://github.com/uutils/coreutils/blob/92995feca29e324a7c559ede91db81c32d9f5d59/src/uu/mv/src/mv.rs#L1428-L1464","documentation":"In mv's rename fallback path (rename_file_fallback), the source file cannot be opened with open_source (no-follow) because the caller lacks read permission on the file. The code replaces the underlying io::Error's message with the localized 'mv-error-permission-denied' message while preserving the io::ErrorKind, so users see a generic permission-denied message for the copy-based move.","triggerScenarios":"mv falls back to copy+delete when rename() fails (e.g. cross-device move) and open_source(from, nofollow=true) fails because the source file's mode denies read permission to the current user (e.g. mode 0o200 or 0o000 file, or a file owned by another user).","commonSituations":"Moving a write-only or unreadable file across filesystems; scripts running as a non-root user trying to move root-owned 0600 files; moving files on a mounted USB/network drive (cross-device) where the source is unreadable.","solutions":["Check the source file permissions with ls -l and add read permission for the invoking user (chmod u+r, or use sudo).","Verify you own the file or are root; permission checks are against the effective uid/gid.","If the file only lacks rename permission in the parent dir, confirm the fallback is actually needed (same-filesystem mv does a plain rename and does not need read access).","Check for ACLs/SELinux/AppArmor restrictions beyond the classic mode bits."],"exampleFix":"// before (shell): mv -x /mnt/usb/secret /home/user/  -> permission denied (copy fallback, unreadable 0600 root file)\nsudo mv /mnt/usb/secret /home/user/\n// after: move succeeds, or first fix perms\nchmod u+r /mnt/usb/secret && mv /mnt/usb/secret /home/user/","handlingStrategy":"validation","validationCode":"// Check readability before mv cross-device\nconst path = '/mnt/usb/secret';\nconst stats = fs.statSync(path);\nconst readable = (stats.mode & parseInt('444', 8)) !== 0 || process.getuid() === 0;\nif (!readable) throw new Error(`cannot read ${path}: permission denied`);","typeGuard":"function isReadable(p) {\n  try { fs.accessSync(p, fs.constants.R_OK); return true; }\n  catch { return false; }\n}","tryCatchPattern":"try {\n  spawnSync('mv', [src, dst]);\n} catch (e) {\n  if (e.message.includes('permission denied')) {\n    console.error(`mv failed: check read access on ${src} or use sudo`);\n  }\n}","preventionTips":["Check ls -l and ACLs on source files before cross-device moves","Run as root or the file owner when moving protected files","Prefer same-filesystem renames to avoid the copy fallback","Audit SELinux/AppArmor denials when mode bits look correct"],"tags":["mv","permission-denied","filesystem","unix"],"backgroundTag":"permission-denied","analyzedSha":"92995feca29e324a7c559ede91db81c32d9f5d59","analyzedAt":"2026-09-20T12:39:35.476Z","contentChangedAt":"2026-09-20T12:39:35.476Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}