diff options
Diffstat (limited to 'mtdutils/bml_over_mtd.c')
-rw-r--r-- | mtdutils/bml_over_mtd.c | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c new file mode 100644 index 000000000..c40179281 --- /dev/null +++ b/mtdutils/bml_over_mtd.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> + +#include "cutils/log.h" + +#include <mtd/mtd-user.h> + +#include "mtdutils.h" + +typedef struct BmlOverMtdReadContext { + const MtdPartition *partition; + char *buffer; + size_t consumed; + int fd; +} BmlOverMtdReadContext; + +typedef struct BmlOverMtdWriteContext { + const MtdPartition *partition; + char *buffer; + size_t stored; + int fd; + + off_t* bad_block_offsets; + int bad_block_alloc; + int bad_block_count; +} BmlOverMtdWriteContext; + + +static BmlOverMtdReadContext *bml_over_mtd_read_partition(const MtdPartition *partition) +{ + BmlOverMtdReadContext *ctx = (BmlOverMtdReadContext*) malloc(sizeof(BmlOverMtdReadContext)); + if (ctx == NULL) return NULL; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDONLY); + if (ctx->fd < 0) { + free(ctx); + free(ctx->buffer); + return NULL; + } + + ctx->partition = partition; + ctx->consumed = partition->erase_size; + return ctx; +} + +static void bml_over_mtd_read_close(BmlOverMtdReadContext *ctx) +{ + close(ctx->fd); + free(ctx->buffer); + free(ctx); +} + +static BmlOverMtdWriteContext *bml_over_mtd_write_partition(const MtdPartition *partition) +{ + BmlOverMtdWriteContext *ctx = (BmlOverMtdWriteContext*) malloc(sizeof(BmlOverMtdWriteContext)); + if (ctx == NULL) return NULL; + + ctx->bad_block_offsets = NULL; + ctx->bad_block_alloc = 0; + ctx->bad_block_count = 0; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDWR); + if (ctx->fd < 0) { + free(ctx->buffer); + free(ctx); + return NULL; + } + + ctx->partition = partition; + ctx->stored = 0; + return ctx; +} + +static int bml_over_mtd_write_close(BmlOverMtdWriteContext *ctx) +{ + int r = 0; + if (close(ctx->fd)) r = -1; + free(ctx->bad_block_offsets); + free(ctx->buffer); + free(ctx); + return r; +} + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "bml_over_mtd" + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) + +#define EXIT_CODE_BAD_BLOCKS 15 + +static int die(const char *msg, ...) { + int err = errno; + va_list args; + va_start(args, msg); + char buf[1024]; + vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (err != 0) { + strlcat(buf, ": ", sizeof(buf)); + strlcat(buf, strerror(err), sizeof(buf)); + } + + fprintf(stderr, "%s\n", buf); + return 1; +} + +static unsigned short* CreateEmptyBlockMapping(const MtdPartition* pSrcPart) +{ + size_t srcTotal, srcErase, srcWrite; + if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) + { + fprintf(stderr, "Failed to access partition.\n"); + return NULL; + } + + int numSrcBlocks = srcTotal/srcErase; + + unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); + if (pMapping == NULL) + { + fprintf(stderr, "Failed to allocate block mapping memory.\n"); + return NULL; + } + memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); + return pMapping; +} + +static const unsigned short* CreateBlockMapping(const MtdPartition* pSrcPart, int srcPartStartBlock, + const MtdPartition *pReservoirPart, int reservoirPartStartBlock) +{ + size_t srcTotal, srcErase, srcWrite; + if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0) + { + fprintf(stderr, "Failed to access partition.\n"); + return NULL; + } + + int numSrcBlocks = srcTotal/srcErase; + + unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short)); + if (pMapping == NULL) + { + fprintf(stderr, "Failed to allocate block mapping memory.\n"); + return NULL; + } + memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short)); + + size_t total, erase, write; + if (mtd_partition_info(pReservoirPart, &total, &erase, &write) != 0) + { + fprintf(stderr, "Failed to access reservoir partition.\n"); + free(pMapping); + return NULL; + } + + if (erase != srcErase || write != srcWrite) + { + fprintf(stderr, "Source partition and reservoir partition differ in size properties.\n"); + free(pMapping); + return NULL; + } + + printf("Partition info: Total %d, Erase %d, write %d\n", total, erase, write); + + BmlOverMtdReadContext *readctx = bml_over_mtd_read_partition(pReservoirPart); + if (readctx == NULL) + { + fprintf(stderr, "Failed to open reservoir partition for reading.\n"); + free(pMapping); + return NULL; + } + + if (total < erase || total > INT_MAX) + { + fprintf(stderr, "Unsuitable reservoir partition properties.\n"); + free(pMapping); + bml_over_mtd_read_close(readctx); + return NULL; + } + + int foundMappingTable = 0; + + int currOffset = total; //Offset *behind* the last byte + while (currOffset > 0) + { + currOffset -= erase; + loff_t pos = lseek64(readctx->fd, currOffset, SEEK_SET); + int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); + if (mgbb != 0) + { + printf("Bad block %d in reservoir area, skipping.\n", currOffset/erase); + continue; + } + ssize_t readBytes = read(readctx->fd, readctx->buffer, erase); + if (readBytes != (ssize_t)erase) + { + fprintf(stderr, "Failed to read good block in reservoir area (%s).\n", + strerror(errno)); + free(pMapping); + bml_over_mtd_read_close(readctx); + return NULL; + } + if (readBytes >= 0x2000) + { + char* buf = readctx->buffer; + if (buf[0]=='U' && buf[1]=='P' && buf[2]=='C' && buf[3]=='H') + { + printf ("Found mapping block mark at 0x%x (block %d).\n", currOffset, currOffset/erase); + + unsigned short* mappings = (unsigned short*) &buf[0x1000]; + if (mappings[0]==0 && mappings[1]==0xffff) + { + printf("Found start of mapping table.\n"); + foundMappingTable = 1; + //Skip first entry (dummy) + unsigned short* mappingEntry = mappings + 2; + while (mappingEntry - mappings < 100 + && mappingEntry[0] != 0xffff) + { + unsigned short rawSrcBlk = mappingEntry[0]; + unsigned short rawDstBlk = mappingEntry[1]; + + printf("Found raw block mapping %d -> %d\n", rawSrcBlk, + rawDstBlk); + + unsigned int srcAbsoluteStartAddress = srcPartStartBlock * erase; + unsigned int resAbsoluteStartAddress = reservoirPartStartBlock * erase; + + int reservoirLastBlock = reservoirPartStartBlock + numSrcBlocks - 1; + if (rawDstBlk < reservoirPartStartBlock + || rawDstBlk*erase >= resAbsoluteStartAddress+currOffset) + { + fprintf(stderr, "Mapped block not within reasonable reservoir area.\n"); + foundMappingTable = 0; + break; + } + + int srcLastBlock = srcPartStartBlock + numSrcBlocks - 1; + if (rawSrcBlk >= srcPartStartBlock && rawSrcBlk <= srcLastBlock) + { + + unsigned short relSrcBlk = rawSrcBlk - srcPartStartBlock; + unsigned short relDstBlk = rawDstBlk - reservoirPartStartBlock; + printf("Partition relative block mapping %d -> %d\n",relSrcBlk, relDstBlk); + + printf("Absolute mapped start addresses 0x%x -> 0x%x\n", + srcAbsoluteStartAddress+relSrcBlk*erase, + resAbsoluteStartAddress+relDstBlk*erase); + printf("Partition relative mapped start addresses 0x%x -> 0x%x\n", + relSrcBlk*erase, relDstBlk*erase); + + //Set mapping entry. For duplicate entries, later entries replace former ones. + //*Assumption*: Bad blocks in reservoir area will not be mapped themselves in + //the mapping table. User partition blocks will not be mapped to bad blocks + //(only) in the reservoir area. This has to be confirmed on a wider range of + //devices. + pMapping[relSrcBlk] = relDstBlk; + + } + mappingEntry+=2; + } + break; //We found the mapping table, no need to search further + } + + + } + } + + } + bml_over_mtd_read_close(readctx); + + if (foundMappingTable == 0) + { + fprintf(stderr, "Cannot find mapping table in reservoir partition.\n"); + free(pMapping); + return NULL; + } + + //Consistency and validity check + int mappingValid = 1; + readctx = bml_over_mtd_read_partition(pSrcPart); + if (readctx == NULL) + { + fprintf(stderr, "Cannot open source partition for reading.\n"); + free(pMapping); + return NULL; + } + int currBlock = 0; + for (;currBlock < numSrcBlocks; ++currBlock) + { + loff_t pos = lseek64(readctx->fd, currBlock*erase, SEEK_SET); + int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos); + if (mgbb == 0) + { + if (pMapping[currBlock]!=0xffff) + { + fprintf(stderr, "Consistency error: Good block has mapping entry %d -> %d\n", currBlock, pMapping[currBlock]); + mappingValid = 0; + } + } else + { + //Bad block! + if (pMapping[currBlock]==0xffff) + { + fprintf(stderr, "Consistency error: Bad block has no mapping entry \n"); + mappingValid = 0; + } else + { + BmlOverMtdReadContext* reservoirReadCtx = bml_over_mtd_read_partition(pReservoirPart); + if (reservoirReadCtx == 0) + { + fprintf(stderr, "Reservoir partition cannot be opened for reading in consistency check.\n"); + mappingValid = 0; + } else + { + pos = lseek64(reservoirReadCtx->fd, pMapping[currBlock]*erase, SEEK_SET); + mgbb = ioctl(reservoirReadCtx->fd, MEMGETBADBLOCK, &pos); + if (mgbb == 0) + { + printf("Bad block has properly mapped reservoir block %d -> %d\n",currBlock, pMapping[currBlock]); + } + else + { + fprintf(stderr, "Consistency error: Mapped block is bad, too. (%d -> %d)\n",currBlock, pMapping[currBlock]); + mappingValid = 0; + } + + } + bml_over_mtd_read_close(reservoirReadCtx); + } + + } + + } + bml_over_mtd_read_close(readctx); + + + if (!mappingValid) + { + free(pMapping); + return NULL; + } + + return pMapping; +} + +static void ReleaseBlockMapping(const unsigned short* blockMapping) +{ + free((void*)blockMapping); +} + +static int dump_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, + const unsigned short* blockMapping, const char* filename) +{ + int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0) + { + fprintf(stderr, "error opening %s", filename); + return -1; + } + BmlOverMtdReadContext* pSrcRead = bml_over_mtd_read_partition(pSrcPart); + if (pSrcRead == NULL) + { + close(fd); + fprintf(stderr, "dump_bml_partition: Error opening src part for reading.\n"); + return -1; + } + + BmlOverMtdReadContext* pResRead = bml_over_mtd_read_partition(pReservoirPart); + if (pResRead == NULL) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + fprintf(stderr, "dump_bml_partition: Error opening reservoir part for reading.\n"); + return -1; + } + + + int numBlocks = pSrcPart->size / pSrcPart->erase_size; + int currblock = 0; + for (;currblock < numBlocks; ++currblock) + { + int srcFd = -1; + if (blockMapping[currblock] == 0xffff) + { + //Good block, use src partition + srcFd = pSrcRead->fd; + if (lseek64(pSrcRead->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: lseek in src partition failed\n"); + return -1; + } + } else + { + //Bad block, use mapped block in reservoir partition + srcFd = pResRead->fd; + if (lseek64(pResRead->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: lseek in reservoir partition failed\n"); + return -1; + } + } + size_t blockBytesRead = 0; + while (blockBytesRead < pSrcPart->erase_size) + { + ssize_t len = read(srcFd, pSrcRead->buffer + blockBytesRead, + pSrcPart->erase_size - blockBytesRead); + if (len <= 0) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: reading partition failed\n"); + return -1; + } + blockBytesRead += len; + } + + size_t blockBytesWritten = 0; + while (blockBytesWritten < pSrcPart->erase_size) + { + ssize_t len = write(fd, pSrcRead->buffer + blockBytesWritten, + pSrcPart->erase_size - blockBytesWritten); + if (len <= 0) + { + close(fd); + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + fprintf(stderr, "dump_bml_partition: writing partition dump file failed\n"); + return -1; + } + blockBytesWritten += len; + } + + } + + bml_over_mtd_read_close(pSrcRead); + bml_over_mtd_read_close(pResRead); + + if (close(fd)) { + unlink(filename); + printf("error closing %s", filename); + return -1; + } + + return 0; +} + +static ssize_t bml_over_mtd_write_block(int fd, ssize_t erase_size, char* data) +{ + off_t pos = lseek(fd, 0, SEEK_CUR); + if (pos == (off_t) -1) return -1; + + ssize_t size = erase_size; + loff_t bpos = pos; + int ret = ioctl(fd, MEMGETBADBLOCK, &bpos); + if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) { + fprintf(stderr, + "Mapping failure: Trying to write bad block at 0x%08lx (ret %d errno %d)\n", + pos, ret, errno); + return -1; + } + + struct erase_info_user erase_info; + erase_info.start = pos; + erase_info.length = size; + int retry; + for (retry = 0; retry < 2; ++retry) { + if (ioctl(fd, MEMERASE, &erase_info) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + if (lseek(fd, pos, SEEK_SET) != pos || + write(fd, data, size) != size) { + fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", + pos, strerror(errno)); + } + + char verify[size]; + if (lseek(fd, pos, SEEK_SET) != pos || + read(fd, verify, size) != size) { + fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + if (memcmp(data, verify, size) != 0) { + fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + + if (retry > 0) { + fprintf(stderr, "mtd: wrote block after %d retries\n", retry); + } + fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos); + return size; // Success! + } + + + fprintf(stderr, "mtd: Block at %llx could not be properly written.\n", pos); + // Ran out of space on the device + errno = ENOSPC; + return -1; +} + +static int flash_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart, + const unsigned short* blockMapping, const char* filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "error opening %s", filename); + return -1; + } + BmlOverMtdWriteContext* pSrcWrite = bml_over_mtd_write_partition(pSrcPart); + if (pSrcWrite == NULL) + { + close(fd); + fprintf(stderr, "flash_bml_partition: Error opening src part for writing.\n"); + return -1; + } + +#ifdef DUMMY_WRITING + close(pSrcWrite->fd); + pSrcWrite->fd = open("/sdcard/srcPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); +#endif + + BmlOverMtdWriteContext* pResWrite = bml_over_mtd_write_partition(pReservoirPart); + if (pResWrite == NULL) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + fprintf(stderr, "flash_bml_partition: Error opening reservoir part for writing.\n"); + return -1; + } +#ifdef DUMMY_WRITING + close(pResWrite->fd); + pResWrite->fd = open("/sdcard/resPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666); +#endif + + struct stat fileStat; + if (fstat(fd, &fileStat) != 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: Failed to stat source file.\n"); + return -1; + + } + if (fileStat.st_size > pSrcPart->size) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: Source file too large for target partition.\n"); + return -1; + } + + int numBlocks = (fileStat.st_size + pSrcPart->erase_size - 1) / pSrcPart->erase_size; + int currblock; + for (currblock = 0 ;currblock < numBlocks; ++currblock) + { + memset(pSrcWrite->buffer, 0xFF, pSrcPart->erase_size); + size_t blockBytesRead = 0; + while (blockBytesRead < pSrcPart->erase_size) + { + ssize_t len = read(fd, pSrcWrite->buffer + blockBytesRead, + pSrcPart->erase_size - blockBytesRead); + if (len < 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: read source file failed\n"); + return -1; + } + if (len == 0) + { + //End of file + break; + } + + blockBytesRead += len; + } + + + + int srcFd = -1; + if (blockMapping[currblock] == 0xffff) + { + //Good block, use src partition + srcFd = pSrcWrite->fd; + if (lseek64(pSrcWrite->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: lseek in src partition failed\n"); + return -1; + } + } else + { + //Bad block, use mapped block in reservoir partition + srcFd = pResWrite->fd; + if (lseek64(pResWrite->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: lseek in reservoir partition failed\n"); + return -1; + } + } + size_t blockBytesWritten = 0; + while (blockBytesWritten < pSrcPart->erase_size) + { +#ifdef DUMMY_WRITING + ssize_t len = write(srcFd, pSrcWrite->buffer + blockBytesWritten, + pSrcPart->erase_size - blockBytesWritten); +#else + ssize_t len = bml_over_mtd_write_block(srcFd, pSrcPart->erase_size, pSrcWrite->buffer); +#endif + if (len <= 0) + { + close(fd); + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + fprintf(stderr, "flash_bml_partition: writing to partition failed\n"); + return -1; + } + blockBytesWritten += len; + } + + + } + + bml_over_mtd_write_close(pSrcWrite); + bml_over_mtd_write_close(pResWrite); + + if (close(fd)) { + printf("error closing %s", filename); + return -1; + } + + return 0; +} + +static int scan_partition(const MtdPartition* pPart) +{ + BmlOverMtdReadContext* readCtx = bml_over_mtd_read_partition(pPart); + if (readCtx == NULL) + { + fprintf(stderr, "Failed to open partition for reading.\n"); + return -1; + } + + int numBadBlocks = 0; + size_t numBlocks = pPart->size / pPart->erase_size; + size_t currBlock; + for (currBlock = 0; currBlock < numBlocks; ++currBlock) + { + + loff_t pos = currBlock * pPart->erase_size; + int mgbb = ioctl(readCtx->fd, MEMGETBADBLOCK, &pos); + if (mgbb != 0) + { + printf("Bad block %d at 0x%x.\n", currBlock, (unsigned int)pos); + numBadBlocks++; + } + } + + bml_over_mtd_read_close(readCtx); + if (numBadBlocks == 0) + { + printf("No bad blocks.\n"); + return 0; + } + return -1 ; +} + +int main(int argc, char **argv) +{ + if (argc != 7 && (argc != 3 || (argc == 3 && strcmp(argv[1],"scan"))!=0) + && (argc != 6 || (argc == 6 && strcmp(argv[1],"scan"))!=0)) + return die("Usage: %s dump|flash <partition> <partition_start_block> <reservoirpartition> <reservoir_start_block> <file>\n" + "E.g. %s dump boot 72 reservoir 2004 file.bin\n" + "Usage: %s scan <partition> [<partition_start_block> <reservoirpartition> <reservoir_start_block>]\n" + ,argv[0], argv[0], argv[0]); + int num_partitions = mtd_scan_partitions(); + const MtdPartition *pSrcPart = mtd_find_partition_by_name(argv[2]); + if (pSrcPart == NULL) + return die("Cannot find partition %s", argv[2]); + + int scanResult = scan_partition(pSrcPart); + + if (argc == 3 && strcmp(argv[1],"scan")==0) + { + return (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); + } + + int retVal = 0; + const MtdPartition* pReservoirPart = mtd_find_partition_by_name(argv[4]); + if (pReservoirPart == NULL) + return die("Cannot find partition %s", argv[4]); + + int srcPartStartBlock = atoi(argv[3]); + int reservoirPartStartBlock = atoi(argv[5]); + const unsigned short* pMapping = CreateBlockMapping(pSrcPart, srcPartStartBlock, + pReservoirPart, reservoirPartStartBlock); + + if (pMapping == NULL && scanResult == 0) + { + printf("Generating empty block mapping table for error-free partition.\n"); + pMapping = CreateEmptyBlockMapping(pSrcPart); + } + + if (argc == 6 && strcmp(argv[1],"scan")==0) + { + retVal = (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS); + } + + if (pMapping == NULL) + return die("Failed to create block mapping table"); + + if (strcmp(argv[1],"dump")==0) + { + retVal = dump_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); + if (retVal == 0) + printf("Successfully dumped partition to %s\n", argv[6]); + } + + if (strcmp(argv[1],"flash")==0) + { + retVal = flash_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]); + if (retVal == 0) + printf("Successfully wrote %s to partition\n", argv[6]); + + } + + + ReleaseBlockMapping(pMapping); + return retVal; +} + |