util::unzip (public)
util::unzip -source source -destination destination [ -overwrite ]
Defined in packages/acs-tcl/tcl/utilities-procs.tcl
Unzip helper, performing generic path checks (../, absolute paths, Windows absolute paths). The function prefer bsdtar (Debian libarchive) when present and no CP850 recoding is needed. For legacy CP850 filenames, use unzip -O CP850, but only if the archive does not contain symlink entries.
- Switches:
- -source (required)
- must be the name of a valid zip file to be decompressed
- -destination (required)
- must be the name of a valid directory to contain decompressed files
- -overwrite (optional, boolean)
- Boolean flag
- Testcases:
- zip_and_unzip
Source code: # # Follow the following preferences_ # - Prefer bsdtar (libarchive) when present and no CP850 recoding is needed. # - For legacy CP850 filenames, use unzip -O CP850, but only if the archive # does not contain symlink entries. # - Reject path traversal (../) and absolute paths (Unix + Windows style) # in archive entry names before extraction. # # Make sure the destination directory exists. if {![file isdirectory $destination]} { file mkdir $destination } # Generic path checks. Errors out on failures. util::archive_check_paths -archive $source # Tools for extraction set bsdtarCmd [util::which bsdtar] set unzipCmd [util::which unzip] set zipinfoCmd [util::which zipinfo] # # Check whether we have to use CP850 handling (unzip -O CP850). # This is Linux-only and depends on util::zip_file_contains_valid_filenames. # set need_cp850 0 if {[info commands ::util::zip_file_contains_valid_filenames] ne "" && $::tcl_platform(os) eq "Linux" && ![::util::zip_file_contains_valid_filenames $source]} { # # The option "-O" works apparently only under Linux and might # depend on the version of "unzip". We assume here that the # broken characters are from Windows (code page 850). # set need_cp850 1 } # # If CP850 recoding is needed, be conservative: reject archives # that contain symlinks, since recoding can do harm with symlink # paths and targets. # if {$need_cp850} { if {[catch {util::archive_has_symlinks -archive $source} result]} { # # We cannot reliably check for symlinks in this archive, but we # know we would have to recode filenames. Be conservative. # error "Zip archive '$source' appears to need CP850 recoding, but symlink inspection failed: $result. Refusing to extract for safety." } if {$result} { error "Zip archive '$source' requires CP850 recoding and contains symbolic links. This combination is rejected for security reasons." } } # # Prefer bsdtar when: # - it is available # - we do NOT need special CP850 handling # if {$bsdtarCmd ne "" && !$need_cp850} { # # bsdtar can read ZIP files as well. # # -x extract # -f file archive file # -C dir chdir before extracting (like unzip -d) # -k do not overwrite existing files (like unzip -n) # --no-same-owner do not restore uid/gid from archive # --no-same-permissions do not restore exact permissions # set cmd [list exec -- $bsdtarCmd -x] if {!$overwrite_p} { lappend cmd -k } lappend cmd --no-same-owner --no-same-permissions lappend cmd -f $source -C $destination {*}$cmd return } # # Fallback: classic unzip, with optional CP850 handling. # if {$unzipCmd eq ""} { if {$need_cp850} { error "Zip file contains non-UTF-8 filenames and 'unzip' (needed for -O CP850) is not available on the system." } else { error "Neither bsdtar nor unzip command found on the system." } } set extra_options {} if {$need_cp850} { lappend extra_options -O CP850 } # -n means we don't overwrite existing files, -o forces overwrite set overwrite_opt [expr {$overwrite_p ? "-o" : "-n"}] exec $unzipCmd {*}$extra_options $overwrite_opt $source -d $destinationXQL Not present: PostgreSQL, Oracle Generic XQL file: packages/acs-tcl/tcl/utilities-procs.xql