1cf04171创建于 2024年4月17日历史提交
#!/usr/bin/env python
# coding: utf-8
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import os
import hashlib
import errno
import stat
import datetime


def usage():
    print('\n Usage: imgcovert.py <cmd> <input> <output>')
    print('         <cmd>: sparse or unsparse')
    print('         <input>: input image file')
    print('         <output>: ouput image file\n')
    return


def get_fill_cnt(inputfile: str, blocksize: int) -> int:
    flags = os.O_WRONLY
    modes = stat.S_IWUSR | stat.S_IRUSR
    size = os.path.getsize(inputfile)
    fill_cnt = 0
    if size % blocksize != 0:
        fill_cnt = blocksize - size % blocksize
    indata = os.fdopen(os.open(inputfile, flags, modes), 'a')
    for _ in range(fill_cnt):
        indata.write("\0")
    indata.close()
    return fill_cnt


def get_gap_blocksize(length: int, size: int) -> int:
    if length < size:
        cnt = 2
    elif length < (size * 2):
        cnt = 3
    else:
        cnt = 4
    return cnt


def get_block_cnt(inputfile: str, blocksize: int) -> int:
    size = os.path.getsize(inputfile)
    if blocksize != 0:
        totalblocks = size / blocksize
    else:
        sys.exit(1)
    if (size % blocksize) != 0:
        print("len is not eq n * blocksize: ", size, totalblocks)
    return totalblocks


def get_crc_value(inputfile: str, blocksize: int):
    totalblocks = get_block_cnt(inputfile, blocksize)
    with open(inputfile, 'rb') as indata:
        ind = 0
        md5 = hashlib.md5()
        while (ind < totalblocks):
            md5.update(indata.read(blocksize))
            ind += 1
    return md5.hexdigest()


def unsparse(sparseimagefile: str, imagefile: str):
    with open(sparseimagefile, 'r') as header:
        magic_mumber = header.readline()
        version = header.readline()
        blocksize = int(header.readline())
        total_blocks = int(header.readline())
        crc_value = header.readline()
        input_crc_value = header.readline()
        table_numbers = int(header.readline())
        table = []
        flags = os.O_CREAT | os.O_RDWR
        modes = stat.S_IWUSR | stat.S_IRUSR
        i = 0
        while (i < table_numbers):
            start = int(header.readline())
            end = int(header.readline())
            table.append([start, end])
            i += 1
        fill_cnt = int(header.readline())
        length = header.tell()
    with open(sparseimagefile, 'rb') as inputrow:
        inputrow.seek(get_gap_blocksize(length, blocksize) * blocksize)
        output = os.fdopen(os.open(imagefile, flags, modes), 'wb')
        output.truncate(total_blocks * blocksize)
        md5 = hashlib.md5()
        for block in table:
            cnt = block[1] - block[0]
            output.seek(block[0] * blocksize)
            indata = inputrow.read(cnt * blocksize)
            md5.update(indata)
            output.write(indata)
        output.close()
    print("RawFileCRC: ", get_crc_value(imagefile, blocksize), crc_value)
    print("SparseCRC: ", md5.hexdigest(), input_crc_value)
    with open(imagefile, 'r+') as output:
        output.truncate(total_blocks * blocksize - fill_cnt)
    return


def is_empty_block(buff: list, size: int) -> bool:
    ind = 0
    while (ind < size):
        if buff[ind] != 0:
            return False
        ind += 1
    return True


def process_block(inputrow, blocksize, outputtemp, blockid, total_blocks) -> int:
    ind = 0
    start = -1
    table_numbers = 0
    while (ind < total_blocks):
        indata = inputrow.read(blocksize)
        if len(indata) != blocksize:
            print("error Block", ind, len(indata))
        if is_empty_block(indata, blocksize):
            if start != -1:
                blockid.append([start, ind])
                table_numbers += 1
                start = -1
        else:
            outputtemp.write(indata)
            if start == -1:
                start = ind
        ind += 1
    if start != -1:
        blockid.append([start, ind])
        table_numbers += 1
        start = -1
    return table_numbers


def get_raw_datafile(imagefile: str, blockid, total_blocks: int, blocksize: int) -> int:
    temp_file = imagefile + ".tempfile"
    flags = os.O_CREAT | os.O_RDWR
    modes = stat.S_IWUSR | stat.S_IRUSR
    table_numbers = 0
    with open(imagefile, 'rb') as inputrow, os.fdopen(os.open(temp_file, flags, modes), 'wb') as outputtemp:
        table_numbers = process_block(inputrow, blocksize, outputtemp, blockid, total_blocks)
    return table_numbers


def sparse(imagefile: str, sparseimagefile: str):
    temp_file = imagefile + ".tempfile"
    magic_number = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    version = 1.0
    blocksize = 4096
    table_numbers = 0
    blockid = []
    flags = os.O_CREAT | os.O_RDWR
    modes = stat.S_IWUSR | stat.S_IRUSR

    fill_cnt = get_fill_cnt(imagefile, blocksize)
    total_blocks = get_block_cnt(imagefile, blocksize)
    table_numbers = get_raw_datafile(imagefile, blockid, total_blocks, blocksize)

#   save the header
    outputrow = os.fdopen(os.open(sparseimagefile, flags, modes), 'w')
    outputrow.write("%s\n" % (magic_number))
    outputrow.write("%s\n" % (version))
    outputrow.write("%s\n" % (blocksize))
    outputrow.write("%s\n" % (int(total_blocks)))
    outputrow.write("%s\n" % (get_crc_value(imagefile, blocksize)))
    outputrow.write("%s\n" % (get_crc_value(temp_file, blocksize)))
    outputrow.write("%s\n" % (table_numbers))
    for block in blockid:
        outputrow.write("%s\n" % (block[0]))
        outputrow.write("%s\n" % (block[1]))
    outputrow.write("%s\n" % (int(fill_cnt)))
    outputrow.truncate(get_gap_blocksize(outputrow.tell(), blocksize) * blocksize)
    outputrow.close()

#   append the raw data
    outputrow = os.fdopen(os.open(sparseimagefile, flags, modes), 'ab')
    outputtemp = os.fdopen(os.open(temp_file, flags, modes), 'rb')
    blocknum = get_block_cnt(temp_file, blocksize)
    i = 0
    while (i < blocknum):
        outputrow.write(outputtemp.read(blocksize))
        i += 1
    outputtemp.close()
    outputrow.close()
    os.remove(temp_file)
    with open(imagefile, 'r+') as output:
        output.truncate(int(total_blocks) * int(blocksize) - int(fill_cnt))


if __name__ == '__main__':
    if len(sys.argv) != 4:
        usage()
        sys.exit()
    CMD = str(sys.argv[1])
    INPUT_FILE = str(sys.argv[2])
    OUTPUT_FILE = str(sys.argv[3])
    if CMD == 'unsparse':
        unsparse(INPUT_FILE, OUTPUT_FILE)
    elif CMD == 'sparse':
        sparse(INPUT_FILE, OUTPUT_FILE)
    else:
        usage()