зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
e2fc89af1c
Коммит
82fc5b6237
|
@ -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
|
Загрузка…
Ссылка в новой задаче