{"record":{"id":"d53c99376006d0c0","repo":"uutils/coreutils","slug":"invalid-link-path","errorCode":null,"errorMessage":"invalid link path","messagePattern":"invalid link path","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/uucore/src/lib/features/fs.rs","lineNumber":1129,"sourceCode":"\n        // GNU's template is `CuXXXXXX`: a 2-char prefix plus 6 random chars\n        // from a 62-char alphabet. The ~3% modulo bias per slot is irrelevant\n        // for an 8-char unguessability budget.\n        const ALPHABET: &[u8; 62] =\n            b\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n        match link_at(target, CWD, dest.as_os_str(), symbolic) {\n            Err(e) if e.kind() == ErrorKind::AlreadyExists => {}\n            res => return res,\n        }\n\n        let parent = dest\n            .parent()\n            .filter(|p| !p.as_os_str().is_empty())\n            .unwrap_or_else(|| Path::new(\".\"));\n        let basename = dest\n            .file_name()\n            .ok_or_else(|| Error::new(ErrorKind::InvalidInput, \"invalid link path\"))?;\n        let dir = openat(\n            CWD,\n            parent,\n            OFlags::DIRECTORY | OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW,\n            Mode::empty(),\n        )?;\n        let mut urandom = fs::File::open(\"/dev/urandom\")?;\n\n        for _ in 0..32 {\n            let mut name = *b\"Cu------\";\n            let mut raw = [0u8; 6];\n            urandom.read_exact(&mut raw)?;\n            for (slot, byte) in name[2..].iter_mut().zip(raw) {\n                *slot = ALPHABET[byte as usize % ALPHABET.len()];\n            }\n            let tmp = OsStr::from_bytes(&name);\n\n            match link_at(target, &dir, tmp, symbolic) {","sourceCodeStart":1111,"sourceCodeEnd":1147,"githubUrl":"https://github.com/uutils/coreutils/blob/92995feca29e324a7c559ede91db81c32d9f5d59/src/uucore/src/lib/features/fs.rs#L1111-L1147","documentation":"replace_link creates/updates a hard link by linking a temporary name inside the destination's parent directory and renaming it onto the destination. The destination path must have a final component; if dest ends in '..' or is empty/'/', file_name() returns None and the function fails with 'invalid link path'.","triggerScenarios":"Calling replace_link (via `link` or copy hard-link handling) with a destination such as \".\", \"..\", \"/\", \"dir/\" or an empty path, so dest.file_name() is None.","commonSituations":"Scripts interpolating variables that end up empty; user passing a directory instead of a filename; paths built by joining with trailing separators then canonicalized awkwardly.","solutions":["Ensure the destination path includes a real file name component","Validate the destination with Path::file_name().is_some() before calling","If linking into a directory, append the desired file name explicitly"],"exampleFix":"// before\nreplace_link(src, Path::new(\"/some/dir/\"))\n// after\nreplace_link(src, &Path::new(\"/some/dir\").join(\"linkname\"))","handlingStrategy":"validation","validationCode":"fn valid_link_target(dest: &Path) -> bool {\n    !dest.as_os_str().is_empty() && dest.file_name().is_some()\n}","typeGuard":"fn has_file_name(p: &Path) -> Option<&std::ffi::OsStr> { p.file_name() }","tryCatchPattern":"match replace_link(src, dest) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => eprintln!(\"destination needs a file name: {dest:?}\"),\n    other => other?,\n}","preventionTips":["Always end destination paths with an explicit file name","Reject '.'/'..'/empty paths from user input before linking","Join directory targets with a default name instead of passing them raw"],"tags":["filesystem","hardlink","path","invalid-input"],"backgroundTag":"invalid-argument-value","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"}