static int process_chunk(struct sparse_file *s, int fd, off64_t offset, unsigned int chunk_hdr_sz, chunk_header_t *chunk_header, unsigned int cur_block, uint32_t *crc_ptr) { int ret; unsigned int chunk_data_size; chunk_data_size = chunk_header->total_sz - chunk_hdr_sz; switch (chunk_header->chunk_type) { case CHUNK_TYPE_RAW: ret = process_raw_chunk(s, chunk_data_size, fd, offset, chunk_header->chunk_sz, cur_block, crc_ptr); if (ret < 0) { verbose_error(s->verbose, ret, "data block at %" PRId64, offset); return ret; } return chunk_header->chunk_sz; case CHUNK_TYPE_FILL: ret = process_fill_chunk(s, chunk_data_size, fd, chunk_header->chunk_sz, cur_block, crc_ptr); if (ret < 0) { verbose_error(s->verbose, ret, "fill block at %" PRId64, offset); return ret; } return chunk_header->chunk_sz; case CHUNK_TYPE_DONT_CARE: ret = process_skip_chunk(s, chunk_data_size, fd, chunk_header->chunk_sz, cur_block, crc_ptr); if (chunk_data_size != 0) { if (ret < 0) { verbose_error(s->verbose, ret, "skip block at %" PRId64, offset); return ret; } } return chunk_header->chunk_sz; case CHUNK_TYPE_CRC32: ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr); if (ret < 0) { verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset); return ret; } return 0; default: verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type, offset); } return 0; }
/* -1 : error; x : buf is finished to write */ int write_simg2emmc(char* interface, int dev, wchar_t* partname, u8* buf, unsigned long length) { unsigned int i; chunk_header_t chunk_header; u32 crc32 = 0; int ret; unsigned int bufferindex = 0; g_buf_index = 0; g_emmc_handle.pdev = get_dev(interface, dev); if (g_emmc_handle.pdev == NULL) { printf("Block device %s %d not supported\n", interface, dev); goto fail; } if (get_partition_info_by_name(g_emmc_handle.pdev, partname, &g_emmc_handle.info)) goto fail; g_emmc_handle.cardPartiton = 0; g_emmc_handle.startBlock = g_emmc_handle.info.start; //printf("download_status = %d current_chunks = %d length = %d\n", download_status, current_chunks, length); if (download_status == END) { current_chunks = 0; total_blocks = 0; g_emmc_handle.offset = 0; gbufindex = 0; memset(&sparse_header, 0, sizeof(sparse_header_t)); ret = read_all(buf, g_buf_index, &sparse_header, sizeof(sparse_header)); //printf("sparse_header file_hdr_sz = %d chunk_hdr_sz = %d blk_sz = %d total_blks = %d total_chunks = %d\n", sparse_header.file_hdr_sz, sparse_header.chunk_hdr_sz, sparse_header.blk_sz, sparse_header.total_blks, sparse_header.total_chunks); if (ret != sizeof(sparse_header)) { printf("Error reading sparse file header\n"); goto fail; } if (sparse_header.magic != SPARSE_HEADER_MAGIC) { printf("Bad magic\n"); goto fail; } if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { printf("Unknown major version number\n"); goto fail; } if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) { /* Skip the remaining bytes in a header that is longer than * we expected. */ g_buf_index += (sparse_header.file_hdr_sz - SPARSE_HEADER_LEN); } download_status = START; } for (i=current_chunks; i<sparse_header.total_chunks; i++) { ///////////////////////////////////////////////////////////////////////////////// memset(&chunk_header, 0, sizeof(chunk_header_t)); bufferindex = g_buf_index; if ((bufferindex + sizeof(chunk_header)) > length) { current_chunks = i; break; } preread_chunk((void*)buf, bufferindex, &chunk_header, sizeof(chunk_header)); /*if (gbufindex) printf("i = %d bufferindex = %d chunk_type = 0x%04x chunk_sz = %d total_sz = %d currentoffset = %d\n", i, gbufindex + bufferindex, chunk_header.chunk_type, chunk_header.chunk_sz, chunk_header.total_sz, g_emmc_handle.offset * EMMC_BLOCK_SIZE); else printf("i = %d bufferindex = %d chunk_type = 0x%04x chunk_sz = %d total_sz = %d currentoffset = %d\n", i, bufferindex, chunk_header.chunk_type, chunk_header.chunk_sz, chunk_header.total_sz, g_emmc_handle.offset * EMMC_BLOCK_SIZE);*/ if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) { bufferindex += sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN; } ret = 0; switch (chunk_header.chunk_type) { case CHUNK_TYPE_RAW: case CHUNK_TYPE_FILL: case CHUNK_TYPE_DONT_CARE: case CHUNK_TYPE_CRC32: if ((bufferindex + chunk_header.total_sz) > length) { printf("type : 0x%04x exceed %d : %d\n", chunk_header.chunk_type, bufferindex + chunk_header.total_sz, length); ret = 1; } break; default: printf("Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type); } if (ret == 1) { current_chunks = i; break; } ///////////////////////////////////////////////////////////////////////////////// memset(&chunk_header, 0, sizeof(chunk_header_t)); ret = read_all((void*)buf, g_buf_index, &chunk_header, sizeof(chunk_header)); /*if (gbufindex) printf("i = %d g_buf_index = %d chunk_type = 0x%04x chunk_sz = %d total_sz = %d currentoffset = %d\n", i, gbufindex + g_buf_index, chunk_header.chunk_type, chunk_header.chunk_sz, chunk_header.total_sz, g_emmc_handle.offset * EMMC_BLOCK_SIZE); else printf("i = %d g_buf_index = %d chunk_type = 0x%04x chunk_sz = %d total_sz = %d currentoffset = %d\n", i, g_buf_index, chunk_header.chunk_type, chunk_header.chunk_sz, chunk_header.total_sz, g_emmc_handle.offset * EMMC_BLOCK_SIZE);*/ if (ret != sizeof(chunk_header)) { printf("Error reading chunk header\n"); goto fail; } if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) { /* Skip the remaining bytes in a header that is longer than * we expected. */ g_buf_index += sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN; } switch (chunk_header.chunk_type) { case CHUNK_TYPE_RAW: if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz + (chunk_header.chunk_sz * sparse_header.blk_sz)) ) { printf("Bogus chunk size for chunk %d, type Raw\n", i); goto fail; } total_blocks += process_raw_chunk((void*)buf, &g_emmc_handle, chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); break; case CHUNK_TYPE_FILL: if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz + sizeof(u32)) ) { printf("Bogus chunk size for chunk %d, type Fill\n", i); goto fail; } total_blocks += process_fill_chunk((void*)buf, &g_emmc_handle, chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); break; case CHUNK_TYPE_DONT_CARE: if (chunk_header.total_sz != sparse_header.chunk_hdr_sz) { printf("Bogus chunk size for chunk %d, type Dont Care\n", i); goto fail; } total_blocks += process_skip_chunk(&g_emmc_handle, chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); break; case CHUNK_TYPE_CRC32: if(process_crc32_chunk((void*)buf, crc32) != 0){ goto fail; } break; default: printf("Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type); } } //printf("offset = %d g_buf_index = %d length = %d Wrote %d blocks, expected to write %d blocks\n", g_emmc_handle.offset, g_buf_index, length, total_blocks, sparse_header.total_blks); if (sparse_header.total_blks != total_blocks) { gbufindex += g_buf_index; return g_buf_index; } else { download_status = END; current_chunks = 0; g_emmc_handle.offset = 0; total_blocks = 0; gbufindex = 0; return 0; } fail: return -1; }
int main(int argc, char *argv[]) { FILE *in, *out; unsigned int i; sparse_header_t sparse_header; chunk_header_t chunk_header; u32 crc32 = 0; u32 total_blocks = 0; if (argc != 3) { usage(); exit(-1); } if ( (copybuf = malloc(COPY_BUF_SIZE)) == 0) { fprintf(stderr, "Cannot malloc copy buf\n"); exit(-1); } if ((in = fopen(argv[1], "rb")) == 0) { fprintf(stderr, "Cannot open input file %s\n", argv[1]); exit(-1); } if ((out = fopen(argv[2], "wb")) == 0) { fprintf(stderr, "Cannot open output file %s\n", argv[2]); exit(-1); } if (fread(&sparse_header, sizeof(sparse_header), 1, in) != 1) { fprintf(stderr, "Error reading sparse file header\n"); exit(-1); } if (sparse_header.magic != SPARSE_HEADER_MAGIC) { fprintf(stderr, "Bad magic\n"); exit(-1); } if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { fprintf(stderr, "Unknown major version number\n"); exit(-1); } if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) { /* Skip the remaining bytes in a header that is longer than * we expected. */ fseek(in, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR); } if ( (zerobuf = malloc(sparse_header.blk_sz)) == 0) { fprintf(stderr, "Cannot malloc zero buf\n"); exit(-1); } for (i=0; i<sparse_header.total_chunks; i++) { if (fread(&chunk_header, sizeof(chunk_header), 1, in) != 1) { fprintf(stderr, "Error reading chunk header\n"); exit(-1); } if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) { /* Skip the remaining bytes in a header that is longer than * we expected. */ fseek(in, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR); } switch (chunk_header.chunk_type) { case CHUNK_TYPE_RAW: if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz + (chunk_header.chunk_sz * sparse_header.blk_sz)) ) { fprintf(stderr, "Bogus chunk size for chunk %d, type Raw\n", i); exit(-1); } total_blocks += process_raw_chunk(in, out, chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); break; case CHUNK_TYPE_DONT_CARE: if (chunk_header.total_sz != sparse_header.chunk_hdr_sz) { fprintf(stderr, "Bogus chunk size for chunk %d, type Dont Care\n", i); exit(-1); } total_blocks += process_skip_chunk(out, chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); break; default: fprintf(stderr, "Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type); exit(-1); } } /* If the last chunk was a skip, then the code just did a seek, but * no write, and the file won't actually be the correct size. This * will make the file the correct size. Make sure the offset is * computed in 64 bits, and the function called can handle 64 bits. */ if (ftruncate(fileno(out), (u64)total_blocks * sparse_header.blk_sz)) { fprintf(stderr, "Error calling ftruncate() to set the image size\n"); exit(-1); } fclose(in); fclose(out); if (sparse_header.total_blks != total_blocks) { fprintf(stderr, "Wrote %d blocks, expected to write %d blocks\n", total_blocks, sparse_header.total_blks); exit(-1); } if (sparse_header.image_checksum != crc32) { fprintf(stderr, "computed crc32 of 0x%8.8x, expected 0x%8.8x\n", crc32, sparse_header.image_checksum); exit(-1); } exit(0); }