Skip to content

Archive extraction in host mounted directory with parent referencing symlink fails on MacOS #28817

@beewoolie

Description

@beewoolie

Issue Description

I am including a script that demonstrates the issue.

To be more specific, unpacking a tar archive in the container, onto a host mounted filesystem, where the container has a symlink referencing a parent, fails in a way that does not yield similar behavior on a Linux host. This appears to be new since upgrading to podman 5.8 on MacOS.

> podman --version
podman version 5.8.2
> sw_vers
ProductName:		macOS
ProductVersion:		15.7.4
BuildVersion:		24G517
> system_profiler SPHardwareDataType
Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: Mac14,9
      Model Number: Z17L001P8LL/A
      Chip: Apple M2 Pro
      Total Number of Cores: 12 (8 performance and 4 efficiency)
      Memory: 32 GB
      System Firmware Version: 13822.81.10
      OS Loader Version: 11881.140.96
      Serial Number (system): LLQH4XMWL0
      Hardware UUID: 6CD1BF3A-2C07-57C7-AC5B-47C8A8B19BC8
      Provisioning UDID: 00006020-000910591AD3C01E
      Activation Lock Status: Enabled

Here is a run of the script:

./linkfail.sh

  • apt-get -qqy update
  • DEBIAN_FRONTEND=noninteractive
  • apt-get install strace -qqy
    Selecting previously unselected package strace.
    (Reading database ... 4933 files and directories currently installed.)
    Preparing to unpack .../strace_6.13+ds-1_arm64.deb ...
    Unpacking strace (6.13+ds-1) ...
    Setting up strace (6.13+ds-1) ...
  • mkdir -p build/project/inside
  • date
  • ln -s ../foo ./build/project/inside/foo
  • tar -C build -cf ./project.tar project
  • strace -o strace.log tar xf project.tar
    tar: project/inside/foo: Cannot open: Permission denied
    tar: Exiting with failure status due to previous errors
  • ls -l project/inside
    total 0
    ----------? 1 root root 0 May 29 17:00 foo
  • cat strace.log
  • grep project/inside
    mkdirat(AT_FDCWD, "project/inside", 0700) = 0
    openat(AT_FDCWD, "project/inside/foo", O_WRONLY|O_CREAT|O_EXCL, 000) = -1 EACCES (Permission denied)
    write(2, "project/inside/foo: Cannot open", 31) = 31
    utimensat(AT_FDCWD, "project/inside", [UTIME_OMIT, {tv_sec=1780074047, tv_nsec=0} /* 2026-05-29T17:00:47+0000 */], AT_SYMLINK_NOFOLLOW) = 0
    fchownat(AT_FDCWD, "project/inside", 0, 0, AT_SYMLINK_NOFOLLOW) = 0
    fchmodat2(AT_FDCWD, "project/inside", 0755, AT_SYMLINK_NOFOLLOW) = 0

Steps to reproduce the issue

Steps to reproduce the issue

  1. On MacOS host, create directory and copy script into it.
  2. Run script from within the directory

linkfail.sh

Describe the results you received

I think the crux of the issue is that the link isn't created. It's not that it would not be supported to create the link. It's that a peculiar file is created by the openat call even though it returns an error.

Describe the results you expected

There ought to have been a symlink created.

podman info output

While this isn't podman output, this is the strace from within the container.


execve("/usr/bin/tar", ["tar", "xf", "project.tar"], 0xffffe84a5360 /* 7 vars */) = 0
brk(NULL)                               = 0xaaaaadf09000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff9ccd8000
faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=4211, ...}) = 0
mmap(NULL, 4211, PROT_READ, MAP_PRIVATE, 3, 0) = 0xffff9ccd6000
close(3)                                = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=67512, ...}) = 0
mmap(NULL, 196656, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff9cc71000
mmap(0xffff9cc80000, 131120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff9cc80000
munmap(0xffff9cc71000, 61440)           = 0
munmap(0xffff9cca1000, 48)              = 0
mprotect(0xffff9cc88000, 94208, PROT_NONE) = 0
mmap(0xffff9cc9f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0xffff9cc9f000
close(3)                                = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=264424, ...}) = 0
mmap(NULL, 403000, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff9cc1d000
mmap(0xffff9cc20000, 337464, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff9cc20000
munmap(0xffff9cc1d000, 12288)           = 0
munmap(0xffff9cc73000, 50744)           = 0
mprotect(0xffff9cc51000, 122880, PROT_NONE) = 0
mmap(0xffff9cc6f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3f000) = 0xffff9cc6f000
mmap(0xffff9cc71000, 5688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffff9cc71000
close(3)                                = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\200$\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1716616, ...}) = 0
mmap(NULL, 1892368, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff9ca51000
mmap(0xffff9ca60000, 1826832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff9ca60000
munmap(0xffff9ca51000, 61440)           = 0
munmap(0xffff9cc1f000, 16)              = 0
mprotect(0xffff9cbfc000, 69632, PROT_NONE) = 0
mmap(0xffff9cc0d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0xffff9cc0d000
mmap(0xffff9cc12000, 49168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffff9cc12000
close(3)                                = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libpcre2-8.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=657952, ...}) = 0
mmap(NULL, 787080, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff9c99f000
mmap(0xffff9c9a0000, 721544, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff9c9a0000
munmap(0xffff9c99f000, 4096)            = 0
munmap(0xffff9ca51000, 57992)           = 0
mprotect(0xffff9ca36000, 102400, PROT_NONE) = 0
mmap(0xffff9ca4f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x9f000) = 0xffff9ca4f000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff9ccd4000
set_tid_address(0xffff9ccd4ad0)         = 57
set_robust_list(0xffff9ccd4ae0, 24)     = 0
rseq(0xffff9ccd52c0, 0x20, 0, 0xd428bc00) = 0
mprotect(0xffff9cc0d000, 12288, PROT_READ) = 0
mprotect(0xffff9ca4f000, 4096, PROT_READ) = 0
mprotect(0xffff9cc6f000, 4096, PROT_READ) = 0
mprotect(0xffff9cc9f000, 4096, PROT_READ) = 0
mprotect(0xaaaaac79e000, 8192, PROT_READ) = 0
mprotect(0xffff9cce0000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xffff9ccd6000, 4211)            = 0
statfs("/sys/fs/selinux", {f_type=TMPFS_MAGIC, f_bsize=4096, f_blocks=49742, f_bfree=49257, f_bavail=49257, f_files=49742, f_ffree=48796, f_fsid={val=[0x25f4f7c7, 0x5f477812]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_RDONLY|ST_NOSUID|ST_NODEV|ST_RELATIME}) = 0
statfs("/selinux", 0xfffffca8a310)      = -1 ENOENT (No such file or directory)
getrandom("\x63\x71\x3b\x05\xfe\x1a\x3b\xa5", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0xaaaaadf09000
brk(0xaaaaadf2a000)                     = 0xaaaaadf2a000
openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "nodev\tsysfs\nnodev\ttmpfs\nnodev\tbd"..., 1024) = 442
close(3)                                = 0
openat(AT_FDCWD, "/proc/mounts", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "a2a0ee2c717462feb1de2f5afd59de5f"..., 1024) = 1024
read(3, "s/storage/overlay/l/DWNIFJNLXNBY"..., 1024) = 1024
read(3, "de64 0 0\ndevtmpfs /dev/zero devt"..., 1024) = 1024
read(3, "e,size=198968k,nr_inodes=49742,m"..., 1024) = 824
read(3, "", 1024)                       = 0
close(3)                                = 0
faccessat(AT_FDCWD, "/etc/selinux/config", F_OK) = -1 ENOENT (No such file or directory)
fcntl(0, F_GETFD)                       = 0
fcntl(1, F_GETFD)                       = 0
fcntl(2, F_GETFD)                       = 0
rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[CHLD], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
geteuid()                               = 0
umask(000)                              = 022
openat(AT_FDCWD, "project.tar", O_RDONLY) = 3
read(3, "project/\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240) = 10240
fstat(3, {st_mode=S_IFREG|0644, st_size=10240, ...}) = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
newfstatat(AT_FDCWD, "/etc/nsswitch.conf", {st_mode=S_IFREG|0644, st_size=494, ...}, 0) = 0
newfstatat(AT_FDCWD, "/", {st_mode=S_IFDIR|0555, st_size=73, ...}, 0) = 0
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=494, ...}) = 0
read(4, "# /etc/nsswitch.conf\n#\n# Example"..., 4096) = 494
read(4, "", 4096)                       = 0
fstat(4, {st_mode=S_IFREG|0644, st_size=494, ...}) = 0
close(4)                                = 0
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=839, ...}) = 0
lseek(4, 0, SEEK_SET)                   = 0
read(4, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 839
close(4)                                = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
newfstatat(AT_FDCWD, "/etc/nsswitch.conf", {st_mode=S_IFREG|0644, st_size=494, ...}, 0) = 0
openat(AT_FDCWD, "/etc/group", O_RDONLY|O_CLOEXEC) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=434, ...}) = 0
lseek(4, 0, SEEK_SET)                   = 0
read(4, "root:x:0:\ndaemon:x:1:\nbin:x:2:\ns"..., 4096) = 434
close(4)                                = 0
mkdirat(AT_FDCWD, "project", 0700)      = 0
mkdirat(AT_FDCWD, "project/inside", 0700) = 0
**openat(AT_FDCWD, "project/inside/foo", O_WRONLY|O_CREAT|O_EXCL, 000) = -1 EACCES (Permission denied)**
write(2, "tar: ", 5)                    = 5
write(2, "project/inside/foo: Cannot open", 31) = 31
write(2, ": Permission denied", 19)     = 19
write(2, "\n", 1)                       = 1
utimensat(AT_FDCWD, "project/inside", [UTIME_OMIT, {tv_sec=1780074047, tv_nsec=0} /* 2026-05-29T17:00:47+0000 */], AT_SYMLINK_NOFOLLOW) = 0
fchownat(AT_FDCWD, "project/inside", 0, 0, AT_SYMLINK_NOFOLLOW) = 0
fchmodat2(AT_FDCWD, "project/inside", 0755, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "project/foo", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0600) = 4
write(4, "Fri May 29 17:00:47 UTC 2026\n", 29) = 29
utimensat(4, NULL, [UTIME_OMIT, {tv_sec=1780074047, tv_nsec=0} /* 2026-05-29T17:00:47+0000 */], 0) = 0
fchown(4, 0, 0)                         = 0
fchmod(4, 0644)                         = 0
close(4)                                = 0
close(3)                                = 0
utimensat(AT_FDCWD, "project", [UTIME_OMIT, {tv_sec=1780074047, tv_nsec=0} /* 2026-05-29T17:00:47+0000 */], AT_SYMLINK_NOFOLLOW) = 0
fchownat(AT_FDCWD, "project", 0, 0, AT_SYMLINK_NOFOLLOW) = 0
fchmodat2(AT_FDCWD, "project", 0755, AT_SYMLINK_NOFOLLOW) = 0
write(2, "tar: ", 5)                    = 5
write(2, "Exiting with failure status due "..., 50) = 50
write(2, "\n", 1)                       = 1
close(1)                                = 0
close(2)                                = 0
exit_group(2)                           = ?
+++ exited with 2 +++

Podman in a container

No

Privileged Or Rootless

Rootless

Upstream Latest Release

Yes

Additional environment details

Additional environment details

Additional information

There are two more things to share.

The workflow that leads to this issue is using a container to build historical firmware releases. We're pulling sources from git using git-archive and extracting them in the container. During one phase of the work, we mount the build directory on the host so we can more easily edit and analyze the sources. We started this work a few months ago and had no trouble with the un-archiving of the sources.

We were able to work-around the issue without too much inconvenience. We extract the archive into container directory (overlay) and then move it to the working directory, links and all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions