From ee0d7e39c14d04bc4f76a37785ab714da144294c Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Wed, 24 Dec 2025 02:15:42 +0000 Subject: [PATCH] cp: reduce memory usage for cp -al by skipping unnecessary tracking --- src/uu/cp/src/copydir.rs | 12 +++++++++++- src/uu/cp/src/cp.rs | 16 +++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/uu/cp/src/copydir.rs b/src/uu/cp/src/copydir.rs index bbd3aba6297..6c1b5748e9e 100644 --- a/src/uu/cp/src/copydir.rs +++ b/src/uu/cp/src/copydir.rs @@ -28,7 +28,8 @@ use uucore::uio_error; use walkdir::{DirEntry, WalkDir}; use crate::{ - CopyResult, CpError, Options, aligned_ancestors, context_for, copy_attributes, copy_file, + CopyMode, CopyResult, CpError, Options, aligned_ancestors, context_for, copy_attributes, + copy_file, }; /// Ensure a Windows path starts with a `\\?`. @@ -469,6 +470,15 @@ pub(crate) fn copy_directory( let is_dir_for_permissions = entry_is_dir_no_follow || (options.dereference && direntry_path.is_dir()); if is_dir_for_permissions { + // For --link mode, copy attributes immediately to avoid O(n) memory + if options.copy_mode == CopyMode::Link { + copy_attributes( + &entry.source_absolute, + &entry.local_to_target, + &options.attributes, + )?; + continue; + } // Add this directory to our list for permission fixing later dirs_needing_permissions .push((entry.source_absolute.clone(), entry.local_to_target.clone())); diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index c1df9ed139a..6d85805a6bb 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -2421,7 +2421,9 @@ fn copy_file( return Err(translate!("cp-error-cannot-change-attribute", "dest" => dest.quote()).into()); } - if options.preserve_hard_links() { + // When using --link mode, hard link structure is automatically preserved + // because we link to source files (which share inodes). + if options.preserve_hard_links() && options.copy_mode != CopyMode::Link { // if we encounter a matching device/inode pair in the source tree // we can arrange to create a hard link between the corresponding names // in the destination tree. @@ -2536,10 +2538,14 @@ fn copy_file( } } - copied_files.insert( - FileInformation::from_path(source, options.dereference(source_in_command_line))?, - dest.to_path_buf(), - ); + // Skip tracking copied files when using --link mode since hard link + // structure is automatically preserved + if options.copy_mode != CopyMode::Link { + copied_files.insert( + FileInformation::from_path(source, options.dereference(source_in_command_line))?, + dest.to_path_buf(), + ); + } if let Some(progress_bar) = progress_bar { progress_bar.inc(source_metadata.len());