Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b2de037 authored by Kelvin Zhang's avatar Kelvin Zhang
Browse files

Handle symlinks when extracting zipfiles

python3.11's zipfile implementation does not handle symlinks. This
causes important symlinks in ramdisk to be broken, and later causing a
boo failure.

Test: unzip a target files with symlinks, make sure symlinks are created
Bug: 287896098

Merged-In: I7da89f8389c09cc99201cff342483c158bd7e9c1
Change-Id: Ia7d6ac8ffb03807680a36ff648aa11afafb7f481
parent e66c973a
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import shlex
import shutil
import subprocess
import sys
import stat
import tempfile
import threading
import time
@@ -2102,6 +2103,26 @@ def Gunzip(in_filename, out_filename):
    shutil.copyfileobj(in_file, out_file)


def UnzipSingleFile(input_zip: zipfile.ZipFile, info: zipfile.ZipInfo, dirname: str):
  # According to https://stackoverflow.com/questions/434641/how-do-i-set-permissions-attributes-on-a-file-in-a-zip-file-using-pythons-zip/6297838#6297838
  # higher bits of |external_attr| are unix file permission and types
  unix_filetype = info.external_attr >> 16

  def CheckMask(a, mask):
    return (a & mask) == mask

  def IsSymlink(a):
    return CheckMask(a, stat.S_IFLNK)
  # python3.11 zipfile implementation doesn't handle symlink correctly
  if not IsSymlink(unix_filetype):
    return input_zip.extract(info, dirname)
  if dirname is None:
    dirname = os.getcwd()
  target = os.path.join(dirname, info.filename)
  os.makedirs(os.path.dirname(target), exist_ok=True)
  os.symlink(input_zip.read(info).decode(), target)


def UnzipToDir(filename, dirname, patterns=None):
  """Unzips the archive to the given directory.

@@ -2147,9 +2168,11 @@ def UnzipToDir(filename, dirname, patterns=None):
      # There isn't any matching files. Don't unzip anything.
      if not filtered:
        return
      input_zip.extractall(dirname, filtered)
      for info in filtered:
        UnzipSingleFile(input_zip, info, dirname)
    else:
      input_zip.extractall(dirname, entries)
      for info in entries:
        UnzipSingleFile(input_zip, info, dirname)


def UnzipTemp(filename, patterns=None):