Bug 1809575 - Implement enough of a CPIO unpacker to handle pkg files. r=gsvelto

Instead of using an external tool.

Differential Revision: https://phabricator.services.mozilla.com/D166503
This commit is contained in:
Mike Hommey 2023-01-11 22:10:14 +00:00
Родитель e2fc89af1c
Коммит 82fc5b6237
2 изменённых файлов: 75 добавлений и 11 удалений

Просмотреть файл

@ -45,6 +45,7 @@ import errno
import logging
import os
import shutil
import stat
import subprocess
import tempfile
import traceback
@ -177,17 +178,16 @@ def extract_payload(payload_path, output_path):
return True
elif header == b"pb":
logging.info("Extracting pbzx payload")
from extract_pbzx import Pbzx
from macpkg import Pbzx, uncpio
for path, mode, content in uncpio(Pbzx(open(payload_path, "rb"))):
if not path or not stat.S_ISREG(mode):
continue
out = os.path.join(output_path, path.decode())
os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, "wb") as fh:
shutil.copyfileobj(content, fh)
# Feed the extracted PBZX into pax.
pax_proc = subprocess.Popen(
["pax", "-r", "-k", "-s", ":^/::"],
stdin=subprocess.PIPE,
cwd=output_path,
)
shutil.copyfileobj(Pbzx(open(payload_path, "rb")), pax_proc.stdin)
pax_proc.stdin.close()
pax_proc.wait()
return True
else:
# Unsupported format
@ -196,7 +196,7 @@ def extract_payload(payload_path, output_path):
)
return False
except subprocess.CalledProcessError:
except Exception:
return False

Просмотреть файл

@ -51,3 +51,67 @@ class Pbzx(object):
# XXX: suboptimal if length is larger than the chunk size
result += self.read(None if length is None else length - len(result))
return result
class Take(object):
"""
File object wrapper that allows to read at most a certain length.
"""
def __init__(self, fileobj, limit):
self.fileobj = fileobj
self.limit = limit
def read(self, length=None):
if length is None:
length = self.limit
else:
length = min(length, self.limit)
result = self.fileobj.read(length)
self.limit -= len(result)
return result
def uncpio(fileobj):
while True:
magic = fileobj.read(6)
# CPIO payloads in mac pkg files are using the portable ASCII format.
if magic != b"070707":
if magic.startswith(b"0707"):
raise Exception("Unsupported CPIO format")
raise Exception("Not a CPIO header")
header = fileobj.read(70)
(
dev,
ino,
mode,
uid,
gid,
nlink,
rdev,
mtime,
namesize,
filesize,
) = struct.unpack(">6s6s6s6s6s6s6s11s6s11s", header)
mode = int(mode, 8)
nlink = int(nlink, 8)
namesize = int(namesize, 8)
filesize = int(filesize, 8)
name = fileobj.read(namesize)
if name[-1] != 0:
raise Exception("File name is not NUL terminated")
name = name[:-1]
if name == b"TRAILER!!!":
break
if b"/../" in name or name.startswith(b"../") or name == b"..":
raise Exception(".. is forbidden in file name")
if name.startswith(b"."):
name = name[1:]
if name.startswith(b"/"):
name = name[1:]
content = Take(fileobj, filesize)
yield name, mode, content
# Ensure the content is totally consumed
while content.read(4096):
pass