diff options
Diffstat (limited to '')
-rw-r--r-- | mtdutils/mtdutils.c | 257 |
1 files changed, 247 insertions, 10 deletions
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index cc3033444..14be57f1e 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -28,12 +28,9 @@ #include "mtdutils.h" -struct MtdPartition { - int device_index; - unsigned int size; - unsigned int erase_size; - char *name; -}; +#ifdef RK3X + #include "rk3xhack.h" +#endif struct MtdReadContext { const MtdPartition *partition; @@ -327,8 +324,8 @@ static int read_block(const MtdPartition *partition, int fd, char *data) ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) { - size_t read = 0; - while (read < len) { + ssize_t read = 0; + while (read < (int) len) { if (ctx->consumed < ctx->partition->erase_size) { size_t avail = ctx->partition->erase_size - ctx->consumed; size_t copy = len - read < avail ? len - read : avail; @@ -344,12 +341,12 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) read += ctx->partition->erase_size; } - if (read >= len) { + if (read >= (int)len) { return read; } // Read the next block into the buffer - if (ctx->consumed == ctx->partition->erase_size && read < len) { + if (ctx->consumed == ctx->partition->erase_size && read < (int) len) { if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; ctx->consumed = 0; } @@ -432,11 +429,19 @@ static int write_block(MtdWriteContext *ctx, const char *data) erase_info.length = size; int retry; for (retry = 0; retry < 2; ++retry) { +#ifdef RK3X + if (rk30_zero_out(fd, pos, size) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } +#else if (ioctl(fd, MEMERASE, &erase_info) < 0) { printf("mtd: erase failure at 0x%08lx (%s)\n", pos, strerror(errno)); continue; } +#endif if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos || TEMP_FAILURE_RETRY(write(fd, data, size)) != size) { printf("mtd: write error at 0x%08lx (%s)\n", @@ -466,7 +471,12 @@ static int write_block(MtdWriteContext *ctx, const char *data) // Try to erase it once more as we give up on this block add_bad_block_offset(ctx, pos); printf("mtd: skipping write block at 0x%08lx\n", pos); +#ifdef RK3X + rk30_zero_out(fd, pos, size); +#else + ioctl(fd, MEMERASE, &erase_info); +#endif pos += partition->erase_size; } @@ -539,9 +549,15 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) struct erase_info_user erase_info; erase_info.start = pos; erase_info.length = ctx->partition->erase_size; +#ifdef RK3X + if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); + } +#else if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { printf("mtd: erase failure at 0x%08lx\n", pos); } +#endif pos += ctx->partition->erase_size; } @@ -559,3 +575,224 @@ int mtd_write_close(MtdWriteContext *ctx) free(ctx); return r; } + +/* Return the offset of the first good block at or after pos (which + * might be pos itself). + */ +off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { + int i; + for (i = 0; i < ctx->bad_block_count; ++i) { + if (ctx->bad_block_offsets[i] == pos) { + pos += ctx->partition->erase_size; + } else if (ctx->bad_block_offsets[i] > pos) { + return pos; + } + } + return pos; +} + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) +#define HEADER_SIZE 2048 + +int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename) +{ + const MtdPartition *ptn; + MtdWriteContext *write; + void *data; + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "error opening %s", filename); + return -1; + } + + if (mtd_scan_partitions() <= 0) + { + fprintf(stderr, "error scanning partitions"); + return -1; + } + const MtdPartition *mtd = mtd_find_partition_by_name(partition_name); + if (mtd == NULL) + { + fprintf(stderr, "can't find %s partition", partition_name); + return -1; + } + + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + printf("error writing %s", partition_name); + return -1; + } + + int success = 1; + char* buffer = malloc(BUFSIZ); + int read; + while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + int wrote = mtd_write_data(ctx, buffer, read); + success = success && (wrote == read); + } + free(buffer); + fclose(f); + + if (!success) { + fprintf(stderr, "error writing %s", partition_name); + return -1; + } + + if (mtd_erase_blocks(ctx, -1) == -1) { + fprintf(stderr, "error erasing blocks of %s\n", partition_name); + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "error closing write of %s\n", partition_name); + } + printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name); + return 0; +} + + +int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename) +{ + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + + partition = mtd_find_partition_by_name(partition_name); + if (partition == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + if (mtd_partition_info(partition, &partition_size, NULL, NULL)) { + printf("can't get info of partition %s", partition_name); + return -1; + } + + if (!strcmp(filename, "-")) { + fd = fileno(stdout); + } + else { + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + in = mtd_read_partition(partition); + if (in == NULL) { + close(fd); + unlink(filename); + printf("error opening %s: %s\n", partition_name, strerror(errno)); + return -1; + } + + total = 0; + while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) { + wrote = write(fd, buf, len); + if (wrote != len) { + close(fd); + unlink(filename); + printf("error writing %s", filename); + return -1; + } + total += BLOCK_SIZE; + } + + mtd_read_close(in); + + if (close(fd)) { + unlink(filename); + printf("error closing %s", filename); + return -1; + } + return 0; +} + +int cmd_mtd_erase_raw_partition(const char *partition_name) +{ + MtdWriteContext *out; + size_t erased; + size_t total_size; + size_t erase_size; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + const MtdPartition *p = mtd_find_partition_by_name(partition_name); + if (p == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + out = mtd_write_partition(p); + if (out == NULL) + { + printf("could not estabilish write context for %s", partition_name); + return -1; + } + + // do the actual erase, -1 = full partition erase + erased = mtd_erase_blocks(out, -1); + + // erased = bytes erased, if zero, something borked + if (!erased) + { + printf("error erasing %s", partition_name); + return -1; + } + + return 0; +} + +int cmd_mtd_erase_partition(const char *partition, const char *filesystem) +{ + return cmd_mtd_erase_raw_partition(partition); +} + + +int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + mtd_scan_partitions(); + const MtdPartition *p; + p = mtd_find_partition_by_name(partition); + if (p == NULL) { + return -1; + } + return mtd_mount_partition(p, mount_point, filesystem, read_only); +} + +int cmd_mtd_get_partition_device(const char *partition, char *device) +{ + mtd_scan_partitions(); + MtdPartition *p = mtd_find_partition_by_name(partition); + if (p == NULL) + return -1; + sprintf(device, "/dev/block/mtdblock%d", p->device_index); + return 0; +} |