Created
September 24, 2026 14:58
-
-
Save stigtsp/ee8f0b93ebffd7f5514dc100b4611c1c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| umask 077 | |
| usage() { | |
| printf 'Usage: %s ARCHIVE.tar[.gz|.bz2|.xz|.zst] [DESTINATION]\n' "${0##*/}" >&2 | |
| exit 2 | |
| } | |
| die() { printf 'error: %s\n' "$*" >&2; exit 1; } | |
| [[ $# -ge 1 && $# -le 2 ]] || usage | |
| for tool in bwrap tar timeout realpath find; do | |
| command -v "$tool" >/dev/null || die "missing $tool" | |
| done | |
| archive=$(realpath -e -- "$1") || die 'archive does not exist' | |
| [[ -f $archive && -r $archive ]] || die 'archive must be a readable regular file' | |
| case ${1,,} in | |
| *.tar.gz|*.tgz) compressor=(--gzip) ;; | |
| *.tar.bz2|*.tbz2) compressor=(--bzip2) ;; | |
| *.tar.xz|*.txz) compressor=(--xz) ;; | |
| *.tar.zst) compressor=(--zstd) ;; | |
| *.tar) compressor=() ;; | |
| *) die 'unsupported archive suffix' ;; | |
| esac | |
| if [[ $# -eq 2 ]]; then | |
| dest=$2 | |
| else | |
| name=${1##*/} | |
| dest=./${name}-extracted | |
| fi | |
| [[ ! -L $dest ]] || die 'destination must not be a symlink' | |
| if [[ ! -e $dest ]]; then | |
| mkdir -m 700 -- "$dest" || die 'cannot create destination' | |
| fi | |
| [[ -d $dest && -w $dest ]] || die 'destination must be a writable directory' | |
| dest=$(realpath -e -- "$dest") | |
| [[ $archive != "$dest/"* ]] || die 'archive is inside destination' | |
| [[ -z $(find "$dest" -mindepth 1 -maxdepth 1 -print -quit) ]] || die 'destination must be empty' | |
| # GNU tar uses external decompressors for compressed archives. | |
| mounts=() | |
| for dir in /usr /bin /lib /lib64; do | |
| [[ ! -d $dir ]] || mounts+=(--ro-bind "$dir" "$dir") | |
| done | |
| # Keep host resources and files outside /out unavailable to the extractor. | |
| # A separate filesystem quota is still needed to cap total output bytes. | |
| if env -i PATH=/usr/bin:/bin LC_ALL=C timeout --signal=TERM --kill-after=5s 60s \ | |
| bwrap --unshare-all --die-with-parent --new-session \ | |
| "${mounts[@]}" \ | |
| --ro-bind "$archive" /archive \ | |
| --bind "$dest" /out \ | |
| --tmpfs /tmp \ | |
| --chdir /out \ | |
| /usr/bin/tar --extract "${compressor[@]}" --file=/archive \ | |
| --no-same-owner --no-same-permissions \ | |
| --no-acls --no-xattrs --no-selinux \ | |
| --keep-old-files --no-overwrite-dir --delay-directory-restore; then | |
| printf 'Extracted to: %s\n' "$dest" | |
| else | |
| die "extraction failed; inspect or remove the partial output at $dest" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment