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

Commit b7dc0481 authored by Kelvin Zhang's avatar Kelvin Zhang Committed by Cherrypicker Worker
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
(cherry picked from https://android-review.googlesource.com/q/commit:e473ce9e805d1eb303de2b020c5a634f632f6c3c)
Merged-In: Ia7d6ac8ffb03807680a36ff648aa11afafb7f481
Change-Id: Ia7d6ac8ffb03807680a36ff648aa11afafb7f481
parent ae2b8d5c
Loading
Loading
Loading
Loading
+25 −2
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ import shlex
import shutil
import shutil
import subprocess
import subprocess
import sys
import sys
import stat
import tempfile
import tempfile
import threading
import threading
import time
import time
@@ -2102,6 +2103,26 @@ def Gunzip(in_filename, out_filename):
    shutil.copyfileobj(in_file, out_file)
    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):
def UnzipToDir(filename, dirname, patterns=None):
  """Unzips the archive to the given directory.
  """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.
      # There isn't any matching files. Don't unzip anything.
      if not filtered:
      if not filtered:
        return
        return
      input_zip.extractall(dirname, filtered)
      for info in filtered:
        UnzipSingleFile(input_zip, info, dirname)
    else:
    else:
      input_zip.extractall(dirname, entries)
      for info in entries:
        UnzipSingleFile(input_zip, info, dirname)




def UnzipTemp(filename, patterns=None):
def UnzipTemp(filename, patterns=None):