When `cp -p` cannot chown the destination to the source's owner
(e.g. a non-root user copying a root-owned setuid file), GNU cp
strips the setuid and setgid bits from the applied mode so the
destination does not give the copying user elevated privileges via
the copy. uutils was unconditionally applying the source mode,
producing user-owned files with a live setuid bit.
Track `ownership_preserved` alongside the existing chown retry
logic and, in the subsequent `handle_preserve(mode, ...)` block,
mask off `0o6000` from the source's mode when ownership could not
be preserved. The sticky bit (01000) is kept, matching GNU.
GNU `cp -p` preserves mode, ownership, and timestamps. xattrs are
NOT preserved unless the user asks for them via `--preserve=xattr`
or `-a`. uutils's `Attributes::DEFAULT` had xattr set to
`Preserve::Yes { required: true }`, which (1) diverges from GNU and
breaks scripts that expect the stock behavior, (2) leaks security
xattrs like file capabilities and SELinux labels into copies when
run as root, and (3) fails hard on destinations that don't support
xattrs.
Remove the xattr override in `Attributes::DEFAULT` so it inherits
`Preserve::No` from `Attributes::NONE`. `Attributes::ALL` (used by
`-a` and `--preserve=all`) still sets xattr to Yes, and
`--preserve=xattr` still works as before.
In `-P` / no-dereference mode, cp now opens the source file with
`O_NOFOLLOW`, matching GNU cp. This closes a TOCTOU window where an
attacker who can swap the source path between cp's `lstat` check and
the subsequent open could redirect the read through a symlink to a
sensitive file (e.g. /etc/shadow). With `O_NOFOLLOW` the open fails
with `ELOOP` instead.
The same flag is propagated to `safe_copy::create_dest_restrictive`,
so the destination open also refuses to follow a symlink in
no-dereference mode. Without that, an attacker who plants the dest
path as a symlink between the caller's check and the open could
redirect the truncate (and the subsequent write) to any file the
caller has permission to write — the symmetric attack to the source
side. With `nofollow=true` the dest open returns `ELOOP` and the
victim file is left untouched.
`copy_on_write` gains a `nofollow` parameter threaded from
`copy_helper`, set to `!options.dereference(source_in_command_line)`.
In deref mode the flag is false and behavior is unchanged — cp still
follows symlinks, matching GNU.
Extends `util/check-safe-traversal.sh` with a cp -P strace check so
the invariant is locked in: future changes that drop `O_NOFOLLOW`
here will fail the smoke test.
cp previously created the destination with mode 0o666 masked by umask
(typically 0o644), then later applied the final permissions via
set_permissions. In a shared directory like /tmp this opened an
observable window where another user could open the destination with
the intermediate broad mode before cp narrowed it, leaking file
contents that were intended to stay private.
Create dest with 0o600 initially in every non-symlink code path —
clone, sparse_copy, sparse_copy_without_hole, fs_copy, the stream
path, and the non-Linux fs::copy fallback. The existing
set_permissions call in copy_file applies the real final mode after
the content is written, so user-visible end state is unchanged; only
the intermediate mode is tightened. Matches GNU cp.
Extend `util/check-safe-traversal.sh` with a cp strace check that
asserts the destination openat carries mode 0600 so a future change
that reintroduces 0666 fails the smoke test.
* cp/mv: suppress xattr ENOTSUP errors for optional preservation
* mv: copy xattrs for symlink fallback and format tests
* cp: fix test expectation for GNU-compatible error format
* fix test portability for Android and xattr detection
---------
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
* cp: set exit code when encountering circular symbolic links error when copying directory
* cp: add test to ensure that cp sets the status code when encountering circular symbolic links during directory copy
* cp: check that the output of circular symbolic link test has the correct message
* cp: update check for stderr message
* cp: update circular symbolic link test to account for directory format in windows
* cp: use std::path::MAIN_SEPARATOR_STR for test
* cp: allow directory merging when destination was just created
Previously, when copying to a destination that was created in the same
cp call, the operation would fail with "will not overwrite just-created"
for both files and directories. This change allows directories to be
merged (matching GNU cp behavior) while still preventing file
overwrites.
The fix checks if both the source and destination are directories
before allowing the merge. If either is a file, the original error
behavior is preserved to prevent accidental file overwrites.
Fixes the case where copying multiple directories to the same
destination path would incorrectly error instead of merging their
contents.
fixes: #9318
* test_cp: Update with the suggestion
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
---------
Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit adds tests to verify that file permissions and timestamps are preserved when copying the current directory (.) to a destination. Specific permissions and timestamps are set on the source files, and assertions are made to ensure that these attributes are correctly maintained in the copied files. The tests are conditionally compiled for non-Windows and non-FreeBSD systems.
This commit introduces several tests to validate the behavior of copying the current directory (.) to both existing and new directories, including checks for verbose output, attribute preservation, and handling of symlinks. Additionally, a test is added to ensure that copying the current directory to itself is disallowed with an appropriate error message. These tests enhance coverage and ensure compliance with expected functionality.
* tests: fix execution on WSL2, Ubuntu 24.04
Skip incompatible parts of tests to ensure that
cargo nextest run --features unix
executes successfully in WSL2, Ubuntu 24.04 distribution.
Changes
* Skip tests that required sys/kernel/profiling which is not
available in WSL2 default configuration.
* Use timezones in tests that are part of the standard tzdata
list and not only available in backwards list. Backwards list is
not installed by default in Ubuntu 24.04 (package tzdata-legacy).
* check that /proc/modules is not empty when expecting content,
because it is empty in WSL2 default configuration.
* Skip logname test not only in WSL1, also in WSL2.
* Add workflow for WSL2
This will setup WSL2 on windows-latest runner, install
Ubuntu-24.04, and run the feat_os_unix tests.
* wsl2: implemented review findings
---------
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
Most attributes can actually be applied to symlinks (mode and
xattr are exceptions), so try to copy them, if possible.
Fort those 2 exceptions, we don't need any special handling as:
- Mode errors would be ignored anyway.
- The source symlink cannot have any xattr anyway, so no
attribute is attempted to be set on the destination.
There was a mismatch between the test, the comment, and the code.
The intention was to _preserve_ attributes if the source of a
fifo/pipe still exists. Perhaps the clearest way is to modify
the test (as that avoids code duplication).
Also add a basic test for that, if permissions are preserved, the
rest should also be preserved correctly.
Fixes the first part of #8402.