You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.5 KiB
Python
238 lines
8.5 KiB
Python
#!/usr/bin/env python
|
|
#############################################################
|
|
# ubi_reader/ubi
|
|
# (c) 2013 Jason Pruitt (jrspruitt@gmail.com)
|
|
#
|
|
# This program 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.
|
|
#
|
|
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#############################################################
|
|
|
|
from zlib import crc32
|
|
from ubireader import settings
|
|
from ubireader.debug import error, log, verbose_display, verbose_log
|
|
from ubireader.ubi import display
|
|
from ubireader.ubi.defines import UBI_EC_HDR_SZ, UBI_VID_HDR_SZ, UBI_INTERNAL_VOL_START, UBI_EC_HDR_MAGIC, UBI_CRC32_INIT
|
|
from ubireader.ubi.headers import ec_hdr, vid_hdr, vtbl_recs
|
|
|
|
|
|
class description(object):
|
|
"""UBI Block description Object
|
|
|
|
UBI Specifications:
|
|
http://www.linux-mtd.infradead.org/ -- Home page
|
|
<kernel>/drivers/mtd/ubi/ubi-media.h -- Header structs
|
|
and defines
|
|
|
|
Attributes:
|
|
Obj:ec_hdr -- Error Count Header
|
|
Obj:vid_hdr -- Volume ID Header
|
|
List:vtbl_recs -- (Optional) List of Volume Table Records.
|
|
Bool:is_vtbl -- If contains volume records table.
|
|
Bool:is_internal_vol -- If Vol ID is > UBI_INTERNAL_VOL_START
|
|
Bool:is_valid -- If ec_hdr & vid_hdr are error free.
|
|
Int:peb_num -- Physical Erase Block number.
|
|
Int:leb_num -- Logical Erase Block number.
|
|
Int:file_offset -- Address location in file of this block.
|
|
Int:size -- Size of total block data or PEB size.
|
|
Int:data_crc -- crc32 of block data.
|
|
Will print out all information when invoked as a string.
|
|
"""
|
|
|
|
def __init__(self, block_buf):
|
|
|
|
self.file_offset = -1
|
|
self.peb_num = -1
|
|
self.leb_num = -1
|
|
self.size = -1
|
|
|
|
self.vid_hdr = None
|
|
self.is_internal_vol = False
|
|
self.vtbl_recs = []
|
|
|
|
# TODO better understanding of block types/errors
|
|
self.ec_hdr = ec_hdr(block_buf[0:UBI_EC_HDR_SZ])
|
|
|
|
if not self.ec_hdr.errors or settings.ignore_block_header_errors:
|
|
self.vid_hdr = vid_hdr(block_buf[self.ec_hdr.vid_hdr_offset:self.ec_hdr.vid_hdr_offset+UBI_VID_HDR_SZ])
|
|
|
|
if not self.vid_hdr.errors or settings.ignore_block_header_errors:
|
|
self.is_internal_vol = self.vid_hdr.vol_id >= UBI_INTERNAL_VOL_START
|
|
|
|
if self.vid_hdr.vol_id >= UBI_INTERNAL_VOL_START:
|
|
self.vtbl_recs = vtbl_recs(block_buf[self.ec_hdr.data_offset:])
|
|
|
|
self.leb_num = self.vid_hdr.lnum
|
|
|
|
self.is_vtbl = bool(self.vtbl_recs) or False
|
|
self.is_valid = not self.ec_hdr.errors and not self.vid_hdr.errors or settings.ignore_block_header_errors
|
|
|
|
|
|
def __repr__(self):
|
|
return 'Block: PEB# %s: LEB# %s' % (self.peb_num, self.leb_num)
|
|
|
|
|
|
def display(self, tab=''):
|
|
return display.block(self, tab)
|
|
|
|
|
|
|
|
def get_blocks_in_list(blocks, idx_list):
|
|
"""Retrieve block objects in list of indexes
|
|
|
|
Arguments:
|
|
List:blocks -- List of block objects
|
|
List:idx_list -- List of block indexes
|
|
|
|
Returns:
|
|
Dict:blocks -- List of block objects generated
|
|
from provided list of indexes in
|
|
order of idx_list.
|
|
"""
|
|
|
|
return {i:blocks[i] for i in idx_list}
|
|
|
|
|
|
|
|
def extract_blocks(ubi):
|
|
"""Get a list of UBI block objects from file
|
|
|
|
Arguments:.
|
|
Obj:ubi -- UBI object.
|
|
|
|
Returns:
|
|
Dict -- Of block objects keyed by PEB number.
|
|
"""
|
|
|
|
blocks = {}
|
|
ubi.file.seek(ubi.file.start_offset)
|
|
peb_count = 0
|
|
cur_offset = 0
|
|
bad_blocks = []
|
|
|
|
# range instead of xrange, as xrange breaks > 4GB end_offset.
|
|
for i in range(ubi.file.start_offset, ubi.file.end_offset, ubi.file.block_size):
|
|
buf = ubi.file.read(ubi.file.block_size)
|
|
|
|
if buf.startswith(UBI_EC_HDR_MAGIC):
|
|
blk = description(buf)
|
|
blk.file_offset = i
|
|
blk.peb_num = ubi.first_peb_num + peb_count
|
|
blk.size = ubi.file.block_size
|
|
blk.data_crc = (~crc32(buf[blk.ec_hdr.data_offset:blk.ec_hdr.data_offset+blk.vid_hdr.data_size]) & UBI_CRC32_INIT)
|
|
blocks[blk.peb_num] = blk
|
|
peb_count += 1
|
|
log(extract_blocks, blk)
|
|
verbose_log(extract_blocks, 'file addr: %s' % (ubi.file.last_read_addr()))
|
|
ec_hdr_errors = ''
|
|
vid_hdr_errors = ''
|
|
|
|
if blk.ec_hdr.errors:
|
|
ec_hdr_errors = ','.join(blk.ec_hdr.errors)
|
|
|
|
if blk.vid_hdr and blk.vid_hdr.errors:
|
|
vid_hdr_errors = ','.join(blk.vid_hdr.errors)
|
|
|
|
if ec_hdr_errors or vid_hdr_errors:
|
|
if blk.peb_num not in bad_blocks:
|
|
bad_blocks.append(blk.peb_num)
|
|
log(extract_blocks, 'PEB: %s has possible issue EC_HDR [%s], VID_HDR [%s]' % (blk.peb_num, ec_hdr_errors, vid_hdr_errors))
|
|
|
|
verbose_display(blk)
|
|
|
|
else:
|
|
cur_offset += ubi.file.block_size
|
|
ubi.first_peb_num = cur_offset//ubi.file.block_size
|
|
ubi.file.start_offset = cur_offset
|
|
|
|
return blocks
|
|
|
|
|
|
def rm_old_blocks(blocks, block_list):
|
|
del_blocks = []
|
|
|
|
for i in block_list:
|
|
if i in del_blocks:
|
|
continue
|
|
|
|
if blocks[i].is_valid is not True:
|
|
del_blocks.append(i)
|
|
continue
|
|
|
|
for k in block_list:
|
|
if i == k:
|
|
continue
|
|
|
|
if k in del_blocks:
|
|
continue
|
|
|
|
if blocks[k].is_valid is not True:
|
|
del_blocks.append(k)
|
|
continue
|
|
|
|
if blocks[i].leb_num != blocks[k].leb_num:
|
|
continue
|
|
|
|
if blocks[i].ec_hdr.image_seq != blocks[k].ec_hdr.image_seq:
|
|
continue
|
|
|
|
second_newer = blocks[k].vid_hdr.sqnum > blocks[i].vid_hdr.sqnum
|
|
del_block = None
|
|
use_block = None
|
|
|
|
if second_newer:
|
|
if blocks[k].vid_hdr.copy_flag == 0:
|
|
del_block = i
|
|
use_block = k
|
|
|
|
else:
|
|
if blocks[i].vid_hdr.copy_flag == 0:
|
|
del_block = k
|
|
use_block = i
|
|
|
|
if del_block is not None:
|
|
del_blocks.append(del_block)
|
|
log(rm_old_blocks, 'Old block removed (copy_flag): PEB %s, LEB %s, Using PEB%s' % (blocks[del_block].peb_num, blocks[del_block].leb_num, use_block))
|
|
break
|
|
|
|
if second_newer:
|
|
if blocks[k].data_crc != blocks[k].vid_hdr.data_crc:
|
|
del_block = k
|
|
use_block = i
|
|
else:
|
|
del_block = i
|
|
use_block = k
|
|
else:
|
|
if blocks[i].data_crc != blocks[i].vid_hdr.data_crc:
|
|
del_block = i
|
|
use_block = k
|
|
else:
|
|
del_block = k
|
|
use_block = i
|
|
|
|
if del_block is not None:
|
|
del_blocks.append(del_block)
|
|
log(rm_old_blocks, 'Old block removed (data_crc): PEB %s, LEB %s, vid_hdr.data_crc %s / %s, Using PEB %s' % (blocks[del_block].peb_num,
|
|
blocks[del_block].leb_num,
|
|
blocks[del_block].vid_hdr.data_crc,
|
|
blocks[del_block].data_crc,
|
|
use_block))
|
|
|
|
else:
|
|
use_block = min(k, i)
|
|
del_blocks.append(use_block)
|
|
error('Warn', rm_old_blocks, 'Multiple PEB [%s] for LEB %s: Using first.' % (', '.join(i, k), blocks[i].leb_num, use_block))
|
|
|
|
break
|
|
|
|
return [j for j in block_list if j not in del_blocks]
|