Skip to content

cp -rL returns exit code 0 when encountering circular symbolic links error #9710

@sylvestre

Description

@sylvestre

cp -rL returns exit code 0 when encountering circular symbolic links error

Component

cp -rL

Description

When cp -rL encounters circular symbolic links during directory traversal, it prints error messages but returns exit code 0 instead of a non-zero exit code.
This happens because show_error! macro is used instead of show! macro, which doesn't set the exit code.
In copydir.rs, when WalkDir encounters an error (such as "Too many levels of symbolic links"), the error is handled with show_error! which only prints the message without setting the exit code.

src/uu/cp/src/copydir.rs

pub(crate) fn copy_directory(
// [...]
    // Traverse the contents of the directory, copying each one.
    for direntry_result in WalkDir::new(root)
        .same_file_system(options.one_file_system)
        .follow_links(options.dereference)
    {
        match direntry_result {
            // [...]
            // Print an error message, but continue traversing the directory.
!            Err(e) => show_error!("{e}"),
        }
    }

The show_error! macro only prints to stderr.

macro_rules! show_error(
    ($($args:tt)+) => ({
        eprint!("{}: ", $crate::util_name());
        eprintln!($($args)+);
    })
);

However, show! macro sets the exit code.

macro_rules! show(
    ($err:expr) => ({
        #[allow(unused_imports)]
        use $crate::error::UError;
        let e = $err;
!        $crate::error::set_exit_code(e.code());
        eprintln!("{}: {e}", $crate::util_name());
    })
);

Test / Reproduction Steps

# Create circular symbolic links
$ mkdir -p /tmp/test_circular_symlinks/src_cycle
$ cd /tmp/test_circular_symlinks/src_cycle
$ ln -s a b
$ ln -s b a

# Run cp -rL
$ coreutils cp -rL /tmp/test_circular_symlinks/src_cycle /tmp/test_circular_symlinks/dest_cycle
cp: IO error for operation on /tmp/test_circular_symlinks/src_cycle/a: 
Too many levels of symbolic links (os error 40)
cp: IO error for operation on /tmp/test_circular_symlinks/src_cycle/b: 
Too many levels of symbolic links (os error 40)

# Check exit code (should be non-zero but returns 0)
$ echo $?
0

Impact

Scripts that rely on exit codes to detect copy failures will not properly detect errors when circular symbolic links are encountered. This breaks compatibility with GNU cp behavior, which returns a non-zero exit code in such cases.

Recommendations

Change show_error! to show! in copy_directory function, converting walkdir::Error to CpError::WalkDirErr:

-Err(e) => show_error!("{e}"),
+Err(e) => show!(CpError::WalkDirErr(e)),

This ensures that the exit code is properly set when errors occur during directory traversal.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions