wslc container cp silently produces an empty file when the source is a symlink
- Dominant language
- C++
- Stars
- 33.7k
- Forks
- 1.8k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 116
Description
### Windows Version
Microsoft Windows [Version 10.0.26220.8764]
### WSL Version
2.9.4.0
### Are you using WSL 1 or WSL 2?
- [X] WSL 2
- [ ] WSL 1
### Kernel Version
6.18.35.2-1
### Distro Version
Not a distro install — reproduced against the container image `mcr.microsoft.com/azurelinux/base/core:3.0`.
### Other Software
None. This is `wslc`, WSL's own container CLI.
### Repro Steps
```bash
wslc image pull mcr.microsoft.com/azurelinux/base/core:3.0
wslc container run -d --name repro mcr.microsoft.com/azurelinux/base/core:3.0 \
sh -c "mkdir -p /w && printf 'REAL-CONTENT' > /w/real.txt && ln -s /w/real.txt /w/link.txt && sleep 120"
# In the guest both entries are as expected:
# -rw-r--r-- 1 root root 12 real.txt
# lrwxrwxrwx 1 root root 11 link.txt -> /w/real.txt
wslc container cp repro:/w/real.txt ./real.out # control
wslc container cp repro:/w/link.txt ./link.out # the bug
```
### Expected Behavior
Copying a symlink out of a container should do one of three things: preserve the link, follow it and copy the target's bytes, or fail with a diagnostic. Any of those is workable for a caller. The docker CLI, whose surface `wslc container cp` mirrors, preserves the link entry.
### Actual Behavior
`wslc container cp` exits **0**, writes **nothing to stderr**, and creates a **0-byte regular file**. The result is indistinguishable from a file that was legitimately empty.
```
cp real.txt -> exit=0 stderr=[] size=12 content=[REAL-CONTENT]
cp link.txt -> exit=0 stderr=[] size=0 content=[]
```
Three observations narrow this down:
- `-a` / `--archive` makes no difference.
- It is not a general absence of error reporting. The same command fails correctly elsewhere: a directory copied to a file path returns exit 1 with `Cannot copy a directory to a file path. Use a directory target (with trailing separator) instead. Error code: E_FAIL`, and a nonexistent source returns exit 1 with `Could not find the file /w/nope.txt in container . Error code: ERROR_PATH_NOT_FOUND` and creates no file. Only the symlink case reports success while producing nothing.
- It is not containment. A link pointing *inside* the copied tree behaves identically to one pointing outside it, so this does not look like a deliberate refusal to let a copy escape the container.
The guest has the information needed to act on it: `stat -c '%F %s' /w/link.txt` reports `symbolic link 11`.
The practical impact is that a caller cannot distinguish this from an empty file, so the failure propagates silently into whatever consumes the copy. The only workaround is to `exec` into the container and `stat` the path first, which requires the image to contain a shell — something `cp` itself does not need.
### Diagnostic Logs
None attached; the repro above is deterministic and requires no tracing.
Contributor guide
Assessment
This issue has not been assessed yet.