/usr/lib/python3/dist-packages/diffoscope/comparators/cbfs.py is in diffoscope 93ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # -*- coding: utf-8 -*-
#
# diffoscope: in-depth comparison of files, archives, and directories
#
# Copyright © 2015 Jérémy Bobbio <lunar@debian.org>
#
# diffoscope is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# diffoscope is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
import io
import os
import re
import struct
import logging
import subprocess
from diffoscope.tools import tool_required
from diffoscope.difference import Difference
from .utils.file import File
from .utils.archive import Archive
from .utils.command import Command
logger = logging.getLogger(__name__)
class CbfsListing(Command):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._header_re = re.compile(r'^.*: ([^,]+, bootblocksize [0-9]+, romsize [0-9]+, offset 0x[0-9A-Fa-f]+)$')
@tool_required('cbfstool')
def cmdline(self):
return ['cbfstool', self.path, 'print']
def filter(self, line):
return self._header_re.sub('\\1', line.decode('utf-8')).encode('utf-8')
class CbfsContainer(Archive):
@tool_required('cbfstool')
def entries(self, path):
cmd = ['cbfstool', path, 'print']
output = subprocess.check_output(cmd, shell=False).decode('utf-8')
header = True
for line in output.rstrip('\n').split('\n'):
if header:
if line.startswith('Name'):
header = False
continue
name = line.split()[0]
if name == '(empty)':
continue
yield name
def open_archive(self):
return self
def close_archive(self):
pass
def get_member_names(self):
return list(self.entries(self.source.path))
@tool_required('cbfstool')
def extract(self, member_name, dest_dir):
dest_path = os.path.join(dest_dir, os.path.basename(member_name))
cmd = ['cbfstool', self.source.path, 'extract', '-n', member_name, '-f', dest_path]
logger.debug("cbfstool extract %s to %s", member_name, dest_path)
subprocess.check_call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
return dest_path
CBFS_HEADER_MAGIC = 0x4F524243
CBFS_HEADER_VERSION1 = 0x31313131
CBFS_HEADER_VERSION2 = 0x31313132
CBFS_HEADER_SIZE = 8 * 4 # 8 * uint32_t
# On 2015-12-15, the largest image produced by coreboot is 16 MiB
CBFS_MAXIMUM_FILE_SIZE = 24 * 2 ** 20 # 24 MiB
def is_header_valid(buf, size, offset=0):
magic, version, romsize, bootblocksize, align, cbfs_offset, architecture, pad = struct.unpack_from('!IIIIIIII', buf, offset)
return magic == CBFS_HEADER_MAGIC and \
(version == CBFS_HEADER_VERSION1 or version == CBFS_HEADER_VERSION2) and \
(romsize <= size) and \
(cbfs_offset < romsize)
class CbfsFile(File):
DESCRIPTION = "Coreboot CBFS filesystem images"
CONTAINER_CLASS = CbfsContainer
@classmethod
def recognizes(cls, file):
size = os.stat(file.path).st_size
if size < CBFS_HEADER_SIZE or size > CBFS_MAXIMUM_FILE_SIZE:
return False
with open(file.path, 'rb') as f:
# pick at the latest byte as it should contain the relative offset of the header
f.seek(-4, io.SEEK_END)
# <pgeorgi> given the hardware we support so far, it looks like
# that field is now bound to be little endian
# -- #coreboot, 2015-10-14
rel_offset = struct.unpack('<i', f.read(4))[0]
if rel_offset < 0 and -rel_offset > CBFS_HEADER_SIZE and -rel_offset < size:
f.seek(rel_offset, io.SEEK_END)
logger.debug('looking for header at offset: %x', f.tell())
if is_header_valid(f.read(CBFS_HEADER_SIZE), size):
return True
elif not file.name.endswith('.rom'):
return False
else:
logger.debug('CBFS relative offset seems wrong, scanning whole image')
f.seek(0, io.SEEK_SET)
offset = 0
buf = f.read(CBFS_HEADER_SIZE)
while len(buf) >= CBFS_HEADER_SIZE:
if is_header_valid(buf, size, offset):
return True
if len(buf) - offset <= CBFS_HEADER_SIZE:
buf = f.read(32768)
offset = 0
else:
offset += 1
return False
def compare_details(self, other, source=None):
return [Difference.from_command(CbfsListing, self.path, other.path)]
|