Skip to content

Commit fcdd465

Browse files
authored
Merge branch 'main' into patch-4
2 parents 3d8002e + 15d22c2 commit fcdd465

5 files changed

Lines changed: 65 additions & 29 deletions

File tree

.github/workflows/GnuTests.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ jobs:
8080
run: |
8181
## Install dependencies
8282
sudo apt-get update
83-
sudo apt-get install -y autopoint gperf gdb python3-pyinotify valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev libselinux1-dev attr quilt
83+
## Check that build-gnu.sh works on the non SELinux system by installing libselinux only on lima
84+
sudo apt-get install -y autopoint gperf gdb python3-pyinotify valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev attr quilt
8485
- name: Add various locales
8586
shell: bash
8687
run: |
@@ -109,7 +110,7 @@ jobs:
109110
run: |
110111
## Build binaries
111112
cd 'uutils'
112-
bash util/build-gnu.sh --release-build
113+
env PROFILE=release-small bash util/build-gnu.sh
113114
114115
### Run tests as user
115116
- name: Run GNU tests
@@ -244,7 +245,7 @@ jobs:
244245
### Build
245246
- name: Build binaries
246247
run: |
247-
lima bash -c "cd ~/work/uutils/ && SELINUX_ENABLED=1 bash util/build-gnu.sh --release-build"
248+
lima bash -c "cd ~/work/uutils/ && SELINUX_ENABLED=1 PROFILE=release-small bash util/build-gnu.sh"
248249
249250
### Run tests as user
250251
- name: Generate SELinux tests list

DEVELOPMENT.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ To run uutils against the GNU test suite locally, run the following commands:
230230
```shell
231231
bash util/build-gnu.sh
232232
# Build uutils with release optimizations
233-
bash util/build-gnu.sh --release-build
233+
env PROFILE=release bash util/build-gnu.sh
234+
# Build uutils with SELinux
235+
env SELINUX_ENABLED=1 bash util/build-gnu.sh
234236
bash util/run-gnu-test.sh
235237
# To run a single test:
236238
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example

src/uu/cp/src/cp.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,10 +1375,19 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult
13751375
{
13761376
// There is already a file and it isn't a symlink (managed in a different place)
13771377
if copied_destinations.contains(&dest) && options.backup != BackupMode::Numbered {
1378-
// If the target file was already created in this cp call, do not overwrite
1379-
return Err(CpError::Error(
1380-
translate!("cp-error-will-not-overwrite-just-created", "dest" => dest.quote(), "source" => source.quote()),
1381-
));
1378+
// If the target was already created in this cp call, check if it's a directory.
1379+
// Directories should be merged (GNU cp behavior), but files should not be overwritten.
1380+
let dest_is_dir = fs::metadata(&dest).is_ok_and(|m| m.is_dir());
1381+
let source_is_dir = fs::metadata(source).is_ok_and(|m| m.is_dir());
1382+
1383+
// Only prevent overwriting if both source and dest are files (not directories)
1384+
// Directories should be merged, which is handled by copy_directory
1385+
if !dest_is_dir || !source_is_dir {
1386+
// If the target file was already created in this cp call, do not overwrite
1387+
return Err(CpError::Error(
1388+
translate!("cp-error-will-not-overwrite-just-created", "dest" => dest.quote(), "source" => source.quote()),
1389+
));
1390+
}
13821391
}
13831392
}
13841393

tests/by-util/test_cp.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,41 @@ fn test_cp_duplicate_folder() {
142142
assert!(at.dir_exists(format!("{TEST_COPY_TO_FOLDER}/{TEST_COPY_FROM_FOLDER}").as_str()));
143143
}
144144

145+
#[test]
146+
fn test_cp_duplicate_directories_merge() {
147+
let (at, mut ucmd) = at_and_ucmd!();
148+
149+
// Source directory 1
150+
at.mkdir_all("src_dir/subdir");
151+
at.write("src_dir/subdir/file1.txt", "content1");
152+
at.write("src_dir/subdir/file2.txt", "content2");
153+
154+
// Source directory 2
155+
at.mkdir_all("src_dir2/subdir");
156+
at.write("src_dir2/subdir/file1.txt", "content3");
157+
158+
// Destination
159+
at.mkdir("dest");
160+
161+
// Perform merge copy
162+
ucmd.arg("-r")
163+
.arg("src_dir/subdir")
164+
.arg("src_dir2/subdir")
165+
.arg("dest")
166+
.succeeds();
167+
168+
// Verify directory exists
169+
assert!(at.dir_exists("dest/subdir"));
170+
171+
// file1.txt should be overwritten by src_dir2/subdir/file1.txt
172+
assert!(at.file_exists("dest/subdir/file1.txt"));
173+
assert_eq!(at.read("dest/subdir/file1.txt"), "content3");
174+
175+
// file2.txt should remain from first copy
176+
assert!(at.file_exists("dest/subdir/file2.txt"));
177+
assert_eq!(at.read("dest/subdir/file2.txt"), "content2");
178+
}
179+
145180
#[test]
146181
fn test_cp_duplicate_files_normalized_path() {
147182
let (at, mut ucmd) = at_and_ucmd!();

util/build-gnu.sh

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,17 @@ NPROC=$(command -v gnproc||command -v nproc)
1414
READLINK=$(command -v greadlink||command -v readlink)
1515
SED=$(command -v gsed||command -v sed)
1616

17+
SYSTEM_TIMEOUT=$(command -v timeout)
18+
SYSTEM_YES=$(command -v yes)
19+
1720
ME="${0}"
1821
ME_dir="$(dirname -- "$("${READLINK}" -fm -- "${ME}")")"
1922
REPO_main_dir="$(dirname -- "${ME_dir}")"
2023

21-
# Default profile is 'debug'
22-
UU_MAKE_PROFILE='debug'
23-
CARGO_FEATURE_FLAGS=""
24-
25-
for arg in "$@"
26-
do
27-
if [ "$arg" == "--release-build" ]; then
28-
UU_MAKE_PROFILE='release'
29-
break
30-
fi
31-
done
3224

33-
echo "UU_MAKE_PROFILE='${UU_MAKE_PROFILE}'"
25+
: ${PROFILE:=debug} # default profile
26+
export PROFILE
27+
CARGO_FEATURE_FLAGS=""
3428

3529
### * config (from environment with fallback defaults); note: GNU is expected to be a sibling repo directory
3630

@@ -39,11 +33,6 @@ path_GNU="$("${READLINK}" -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")"
3933

4034
###
4135

42-
SYSTEM_TIMEOUT=$(command -v timeout)
43-
SYSTEM_YES=$(command -v yes)
44-
45-
###
46-
4736
release_tag_GNU="v9.9"
4837

4938
# check if the GNU coreutils has been cloned, if not print instructions
@@ -71,9 +60,9 @@ echo "path_GNU='${path_GNU}'"
7160
###
7261

7362
if [[ ! -z "$CARGO_TARGET_DIR" ]]; then
74-
UU_BUILD_DIR="${CARGO_TARGET_DIR}/${UU_MAKE_PROFILE}"
63+
UU_BUILD_DIR="${CARGO_TARGET_DIR}/${PROFILE}"
7564
else
76-
UU_BUILD_DIR="${path_UUTILS}/target/${UU_MAKE_PROFILE}"
65+
UU_BUILD_DIR="${path_UUTILS}/target/${PROFILE}"
7766
fi
7867
echo "UU_BUILD_DIR='${UU_BUILD_DIR}'"
7968

@@ -105,9 +94,9 @@ fi
10594
cd -
10695

10796
# Pass the feature flags to make, which will pass them to cargo
108-
"${MAKE}" PROFILE="${UU_MAKE_PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}"
97+
"${MAKE}" PROFILE="${PROFILE}" CARGOFLAGS="${CARGO_FEATURE_FLAGS}"
10998
# min test for SELinux
110-
[ "${SELINUX_ENABLED}" = 1 ] && touch g && "${UU_MAKE_PROFILE}"/stat -c%C g && rm g
99+
[ "${SELINUX_ENABLED}" = 1 ] && touch g && "${PROFILE}"/stat -c%C g && rm g
111100

112101
cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
113102
# Create *sum binaries

0 commit comments

Comments
 (0)