summaryrefslogtreecommitdiffstats
path: root/mtdutils/mtdutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'mtdutils/mtdutils.c')
-rw-r--r--mtdutils/mtdutils.c257
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;
+}