TWRP-ify AOSP code

Pull in most TWRP sources
Stub out partition management code
Make it compile -- probably will not boot
Kind of a mess but have to start somewhere
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index ef417fa..ec57408 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -1,18 +1,18 @@
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
 	mtdutils.c \
-	mounts.c
+	mounts.c 
 
 LOCAL_MODULE := libmtdutils
-
+LOCAL_STATIC_LIBRARIES := libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 include $(BUILD_STATIC_LIBRARY)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := flash_image.c
-LOCAL_MODULE := flash_image
-LOCAL_MODULE_TAGS := eng
-LOCAL_STATIC_LIBRARIES := libmtdutils
-LOCAL_SHARED_LIBRARIES := libcutils libc
-include $(BUILD_EXECUTABLE)
+endif	# TARGET_ARCH == arm
+endif	# !TARGET_SIMULATOR
+
diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c
new file mode 100644
index 0000000..c401792
--- /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;
+}
+
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..30b2927 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -17,10 +17,6 @@
 #ifndef MTDUTILS_MOUNTS_H_
 #define MTDUTILS_MOUNTS_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct MountedVolume MountedVolume;
 
 int scan_mounted_volumes(void);
@@ -34,8 +30,4 @@
 
 int remount_read_only(const MountedVolume* volume);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif  // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index 107cbb9..a76b8e9 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -28,13 +28,6 @@
 
 #include "mtdutils.h"
 
-struct MtdPartition {
-    int device_index;
-    unsigned int size;
-    unsigned int erase_size;
-    char *name;
-};
-
 struct MtdReadContext {
     const MtdPartition *partition;
     char *buffer;
@@ -328,8 +321,8 @@
 
 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;
@@ -345,12 +338,12 @@
             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;
         }
@@ -457,7 +450,7 @@
             if (retry > 0) {
                 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
             }
-            fprintf(stderr, "mtd: successfully wrote block at %lx\n", pos);
+            fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
             return 0;  // Success!
         }
 
@@ -569,3 +562,209 @@
     }
     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;
+}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 2708c43..c57d45d 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -19,10 +19,6 @@
 
 #include <sys/types.h>  // for size_t, etc.
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct MtdPartition MtdPartition;
 
 int mtd_scan_partitions(void);
@@ -57,8 +53,11 @@
 off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
 int mtd_write_close(MtdWriteContext *);
 
-#ifdef __cplusplus
-}
-#endif
+struct MtdPartition {
+    int device_index;
+    unsigned int size;
+    unsigned int erase_size;
+    char *name;
+};
 
 #endif  // MTDUTILS_H_