fbpacktool: Update to current version

Source:
https://source.android.com/static/docs/core/architecture/bootloader/tools/pixel/fw_unpack/*.py

Modified to comment out
'from google3.third_party.devsite.androidsource.en.docs.core.architecture.bootloader.tools.pixel.fw_unpack'
from imports.

Change-Id: Ie81b52969633435ff303a89309868509436392ab
This commit is contained in:
Michael Bestas
2023-06-12 02:22:51 +03:00
committed by Michael Bestas
parent f9f47395f1
commit f3c95a9fda
4 changed files with 304 additions and 264 deletions

View File

@@ -12,9 +12,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
import struct import collections
from packedstruct import PackedStruct
from collections import OrderedDict #from google3.third_party.devsite.androidsource.en.docs.core.architecture.bootloader.tools.pixel.fw_unpack import packedstruct
import packedstruct
FBPACK_MAGIC = 0x4b504246 # "FBPK" FastBook PacK FBPACK_MAGIC = 0x4b504246 # "FBPK" FastBook PacK
FBPACK_VERSION = 2 FBPACK_VERSION = 2
@@ -25,10 +26,17 @@ FBPACK_PARTITION_DATA = 1
FBPACK_SIDELOAD_DATA = 2 FBPACK_SIDELOAD_DATA = 2
class PackEntry(PackedStruct): class PackEntry(packedstruct.PackedStruct):
"""Pack entry info.""" """Pack entry info."""
_FIELDS = OrderedDict([ type: int
name: bytes
product: bytes
offset: int
size: int
slotted: int
crc32: int
_FIELDS = collections.OrderedDict([
('type', 'I'), ('type', 'I'),
('name', '36s'), ('name', '36s'),
('product', '40s'), ('product', '40s'),
@@ -38,14 +46,34 @@ class PackEntry(PackedStruct):
('crc32', 'I'), ('crc32', 'I'),
]) ])
def __init__(self, type_=0, name=b'', prod=b'', offset=0, size=0, slotted=0, crc32=0): # Provide defaults.
super(PackEntry, self).__init__(type_, name, prod, offset, size, slotted, crc32) # pylint: disable=useless-super-delegation
def __init__(self,
type_=0,
name=b'',
prod=b'',
offset=0,
size=0,
slotted=0,
crc32=0):
super(PackEntry, self).__init__(type_, name, prod, offset, size, slotted,
crc32)
class PackHeader(PackedStruct): class PackHeader(packedstruct.PackedStruct):
""" A packed image representation""" """ A packed image representation"""
_FIELDS = OrderedDict([ magic: int
version: int
header_size: int
entry_header_size: int
platform: bytes
pack_version: bytes
slot_type: int
data_align: int
total_entries: int
total_size: int
_FIELDS = collections.OrderedDict([
('magic', 'I'), ('magic', 'I'),
('version', 'I'), ('version', 'I'),
('header_size', 'I'), ('header_size', 'I'),
@@ -58,8 +86,7 @@ class PackHeader(PackedStruct):
('total_size', 'I'), ('total_size', 'I'),
]) ])
def __init__( def __init__(self,
self,
magic=FBPACK_MAGIC, magic=FBPACK_MAGIC,
version=FBPACK_VERSION, version=FBPACK_VERSION,
header_size=0, header_size=0,
@@ -70,16 +97,9 @@ class PackHeader(PackedStruct):
data_align=FBPACK_DEFAULT_DATA_ALIGN, data_align=FBPACK_DEFAULT_DATA_ALIGN,
total_entries=0, total_entries=0,
total_size=0): total_size=0):
super(PackHeader, self).__init__( super(PackHeader,
magic, self).__init__(magic, version, header_size, entry_header_size,
version, platform, pack_version, slot_type, data_align,
header_size, total_entries, total_size)
entry_header_size,
platform,
pack_version,
slot_type,
data_align,
total_entries,
total_size)
# update header size once we know all fields # update header size once we know all fields
self.header_size = len(self) self.header_size = len(self)

View File

@@ -1 +0,0 @@
fbpacktool.py

99
fbpacktool/fbpacktool.py Executable file → Normal file
View File

@@ -13,13 +13,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
#
import sys
import os
import argparse import argparse
import logging import logging
from fbpack import * import os
import sys
from lxml.etree import XMLParser
import yaml
#from google3.third_party.devsite.androidsource.en.docs.core.architecture.bootloader.tools.pixel.fw_unpack import fbpack
import fbpack
def bytes_to_str(bstr): def bytes_to_str(bstr):
return bstr.decode().rstrip('\x00') return bstr.decode().rstrip('\x00')
@@ -44,14 +46,14 @@ def print_pack_entry(entry, prefix):
name = bytes_to_str(entry.name) name = bytes_to_str(entry.name)
print('{}name: {}'.format(prefix, name)) print('{}name: {}'.format(prefix, name))
etype = 'unknown' etype = 'unknown'
if entry.type == FBPACK_PARTITION_TABLE: if entry.type == fbpack.FBPACK_PARTITION_TABLE:
etype = 'partiton table' etype = 'partiton table'
elif entry.type == FBPACK_PARTITION_DATA: elif entry.type == fbpack.FBPACK_PARTITION_DATA:
etype = 'partition' etype = 'partition'
elif entry.type == FBPACK_SIDELOAD_DATA: elif entry.type == fbpack.FBPACK_SIDELOAD_DATA:
etype = 'sideload' etype = 'sideload'
else: else:
print("entry else") print('entry else')
print('{}type: {}'.format(prefix, etype)) print('{}type: {}'.format(prefix, etype))
product = bytes_to_str(entry.product) product = bytes_to_str(entry.product)
print('{}product: {}'.format(prefix, product)) print('{}product: {}'.format(prefix, product))
@@ -63,17 +65,17 @@ def print_pack_entry(entry, prefix):
def cmd_info(args): def cmd_info(args):
with open(args.file, 'rb') as f: with open(args.file, 'rb') as f:
pack = PackHeader.from_bytes(f.read(len(PackHeader()))) pack = fbpack.PackHeader.from_bytes(f.read(len(fbpack.PackHeader())))
if pack.version != FBPACK_VERSION: if pack.version != fbpack.FBPACK_VERSION:
raise NotImplementedError('unsupported version {}'.format(pack.version)) raise NotImplementedError('unsupported version {}'.format(pack.version))
print("Header:") print('Header:')
print_pack_header(pack) print_pack_header(pack)
print('\nEntries:') print('\nEntries:')
for i in range(1, pack.total_entries + 1): for i in range(1, pack.total_entries + 1):
entry = PackEntry.from_bytes(f.read(len(PackEntry()))) entry = fbpack.PackEntry.from_bytes(f.read(len(fbpack.PackEntry())))
print('Entry {}: {{'.format(i)) print('Entry {}: {{'.format(i))
print_pack_entry(entry, ' ') print_pack_entry(entry, ' ')
print('}') print('}')
@@ -110,14 +112,15 @@ def create_pack_file(file_name, in_dir_name, pack):
def cmd_create(args): def cmd_create(args):
if args.file.lower().endswith('.xml'): if not (args.file.lower().endswith('.xml') or
import xmlparser as parser args.file.lower().endswith('.yaml')):
elif args.file.lower().endswith('.yaml'):
import yamlparser as parser
else:
raise NotImplementedError('{} type not supported'.format(args.file)) raise NotImplementedError('{} type not supported'.format(args.file))
pack = parser.parse(args.file) pack = None
if args.file.lower().endswith('.yaml'):
pack = yaml.parse(args.file)
else:
pack = XMLParser.parse(args.file)
pack.pack_version = bytes(str(args.pack_version).encode('ascii')) pack.pack_version = bytes(str(args.pack_version).encode('ascii'))
pack.header_size = len(pack) pack.header_size = len(pack)
@@ -131,7 +134,7 @@ def cmd_create(args):
def product_match(products, product): def product_match(products, product):
return product in products.split('|') return product in products.split(b'|')
def copyfileobj(src, dst, file_size): def copyfileobj(src, dst, file_size):
@@ -143,15 +146,15 @@ def copyfileobj(src, dst, file_size):
def cmd_unpack(args): def cmd_unpack(args):
with open(args.file, 'rb') as f: with open(args.file, 'rb') as f:
pack = PackHeader.from_bytes(f.read(len(PackHeader()))) pack = fbpack.PackHeader.from_bytes(f.read(len(fbpack.PackHeader())))
if pack.version != FBPACK_VERSION: if pack.version != fbpack.FBPACK_VERSION:
raise NotImplementedError('unsupported version {}'.format(pack.version)) raise NotImplementedError('unsupported version {}'.format(pack.version))
entries = [] entries = []
# create list of entries we want to extact # create list of entries we want to extact
for _ in range(pack.total_entries): for _ in range(pack.total_entries):
entry = PackEntry.from_bytes(f.read(len(PackEntry()))) entry = fbpack.PackEntry.from_bytes(f.read(len(fbpack.PackEntry())))
name = bytes_to_str(entry.name) name = bytes_to_str(entry.name)
if not args.partitions or name in args.partitions: if not args.partitions or name in args.partitions:
# if both product are valid then match product name too # if both product are valid then match product name too
@@ -162,7 +165,7 @@ def cmd_unpack(args):
if not entries and not args.unpack_ver: if not entries and not args.unpack_ver:
raise RuntimeError('no images to unpack') raise RuntimeError('no images to unpack')
# create output directory if it does not exists # create output directory if it does not exist
if not os.path.isdir(args.out_dir): if not os.path.isdir(args.out_dir):
os.makedirs(args.out_dir, 0o755) os.makedirs(args.out_dir, 0o755)
@@ -170,15 +173,15 @@ def cmd_unpack(args):
# write file per entry # write file per entry
for entry in entries: for entry in entries:
name = bytes_to_str(entry.name) name = bytes_to_str(entry.name)
logging.info( logging.info('Unpacking {} (size: {}, offset: {})'.format(
'Unpacking {} (size: {}, offset: {})'.format(
name, entry.size, entry.offset)) name, entry.size, entry.offset))
f.seek(entry.offset) f.seek(entry.offset)
entry_filename = os.path.join(args.out_dir, name + '.img') entry_filename = os.path.join(args.out_dir, name + '.img')
instance = out_files.get(entry_filename, 0) + 1 instance = out_files.get(entry_filename, 0) + 1
out_files[entry_filename] = instance out_files[entry_filename] = instance
if instance > 1: if instance > 1:
entry_filename = os.path.join(args.out_dir, name + '({}).img'.format(instance - 1)) entry_filename = os.path.join(args.out_dir,
name + '({}).img'.format(instance - 1))
with open(entry_filename, 'wb') as entry_file: with open(entry_filename, 'wb') as entry_file:
copyfileobj(f, entry_file, entry.size) copyfileobj(f, entry_file, entry.size)
@@ -192,36 +195,53 @@ def cmd_unpack(args):
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Tool to create/modify/inspect fastboot packed images") description='Tool to create/modify/inspect fastboot packed images')
parser.add_argument("-v", "--verbosity", action="count", default=0, parser.add_argument(
help="increase output verbosity") '-v',
'--verbosity',
action='count',
default=0,
help='increase output verbosity')
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()
# info command # info command
info = subparsers.add_parser('info') info = subparsers.add_parser('info')
info.add_argument('file', help="packed image file") info.add_argument('file', help='packed image file')
info.set_defaults(func=cmd_info) info.set_defaults(func=cmd_info)
# create command # create command
create = subparsers.add_parser('create') create = subparsers.add_parser('create')
create.add_argument('-d', '--in_dir', help='directory to search for data files', default='.') create.add_argument(
'-d', '--in_dir', help='directory to search for data files', default='.')
create.add_argument( create.add_argument(
'-o', '-o',
'--out_dir', '--out_dir',
help='output directory for the packed image', help='output directory for the packed image',
default='.') default='.')
create.add_argument('-v', '--pack_version', help='Packed image version ', default='') create.add_argument(
create.add_argument('file', help="config file describing packed image (yaml/xml)") '-v', '--pack_version', help='Packed image version ', default='')
create.add_argument(
'file', help='config file describing packed image (yaml/xml)')
create.set_defaults(func=cmd_create) create.set_defaults(func=cmd_create)
# unpack command # unpack command
unpack = subparsers.add_parser('unpack') unpack = subparsers.add_parser('unpack')
unpack.add_argument('-o', '--out_dir', help='directory to store unpacked images', default='.') unpack.add_argument(
unpack.add_argument('-p', '--product', help='filter images by product', default='') '-o', '--out_dir', help='directory to store unpacked images', default='.')
unpack.add_argument('-v', '--unpack_ver', help='Unpack version to a file', action='store_true') unpack.add_argument(
unpack.add_argument('file', help="packed image file") '-p', '--product', help='filter images by product', default='')
unpack.add_argument('partitions', metavar='PART', type=str, nargs='*', unpack.add_argument(
'-v',
'--unpack_ver',
help='Unpack version to a file',
action='store_true')
unpack.add_argument('file', help='packed image file')
unpack.add_argument(
'partitions',
metavar='PART',
type=str,
nargs='*',
help='Partition names to extract (default all).') help='Partition names to extract (default all).')
unpack.set_defaults(func=cmd_unpack) unpack.set_defaults(func=cmd_unpack)
@@ -229,7 +249,7 @@ def parse_args():
# make sure a command was passed # make sure a command was passed
if not hasattr(args, 'func'): if not hasattr(args, 'func'):
parser.print_usage() parser.print_usage()
print("fbpacktool.py: error: no command was passed") print('fbpacktool.py: error: no command was passed')
sys.exit(2) sys.exit(2)
return args return args
@@ -238,7 +258,6 @@ def parse_args():
def main(): def main():
args = parse_args() args = parse_args()
log_level = logging.DEBUG
if args.verbosity >= 2: if args.verbosity >= 2:
log_level = logging.DEBUG log_level = logging.DEBUG
elif args.verbosity == 1: elif args.verbosity == 1:

View File

@@ -12,14 +12,15 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
import collections
import struct import struct
class PackedStruct(object): class PackedStruct(object):
"""Class representing a C style packed structure """Class representing a C style packed structure.
Derived classes need to provide a dictionary with key will be the attributes and the values are Derived classes need to provide a dictionary where the keys are the attributes
the format characters for each field. e.g. and the values are the format characters for each field. e.g.
class Foo(PackedStruct): class Foo(PackedStruct):
_FIELDS = { _FIELDS = {
@@ -27,10 +28,10 @@ class PackedStruct(object):
name: '64s', name: '64s',
} }
In this case Foo.x will represent an "unsigned int" C value, while Foo.name will be a "char[64]" In this case Foo.x will represent an "unsigned int" C value, while Foo.name
C value will be a "char[64]" C value.
""" """
_FIELDS: collections.OrderedDict
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._fmt = '<' + ''.join(fmt for fmt in self._FIELDS.values()) self._fmt = '<' + ''.join(fmt for fmt in self._FIELDS.values())
@@ -44,7 +45,8 @@ class PackedStruct(object):
def __repr__(self): def __repr__(self):
return '{} {{\n'.format(self.__class__.__name__) + ',\n'.join( return '{} {{\n'.format(self.__class__.__name__) + ',\n'.join(
' {!r}: {!r}'.format(k, getattr(self, k)) for k in self._FIELDS) + '\n}' ' {!r}: {!r}'.format(k, getattr(self, k))
for k in self._FIELDS) + '\n}'
def __str__(self): def __str__(self):
return struct.pack(self._fmt, *(getattr(self, x) for x in self._FIELDS)) return struct.pack(self._fmt, *(getattr(self, x) for x in self._FIELDS))