Example #1
0
static int
mtd_refresh(const char *mtd)
{
	int fd;

	if (quiet < 2)
		fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	if (ioctl(fd, MTDREFRESH, NULL)) {
		fprintf(stderr, "Failed to refresh the MTD device\n");
		close(fd);
		exit(1);
	}
	close(fd);

	if (quiet < 2)
		fprintf(stderr, "\n");

	return 0;
}
Example #2
0
static int
mtd_erase(const char *mtd)
{
	int fd;
	struct erase_info_user mtdEraseInfo;

	if (quiet < 2)
		fprintf(stderr, "Erasing %s ...\n", mtd);

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	mtdEraseInfo.length = erasesize;

	for (mtdEraseInfo.start = 0;
		 mtdEraseInfo.start < mtdsize;
		 mtdEraseInfo.start += erasesize) {
		if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
			if (!quiet)
				fprintf(stderr, "\nSkipping bad block at 0x%x   ", mtdEraseInfo.start);
		} else {
			ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
			if(ioctl(fd, MEMERASE, &mtdEraseInfo))
				fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
		}
	}

	close(fd);
	return 0;

}
Example #3
0
static int mtd_check(const char *mtd)
{
	char *next = NULL;
	char *str = NULL;
	int fd;

	if (strchr(mtd, ':')) {
		str = strdup(mtd);
		mtd = str;
	}

	do {
		next = strchr(mtd, ':');
		if (next) {
			*next = 0;
			next++;
		}

		fd = mtd_check_open(mtd);
		if (fd < 0)
			return 0;

		if (!buf)
			buf = malloc(erasesize);

		close(fd);
		mtd = next;
	} while (next);

	if (str)
		free(str);

	return 1;
}
Example #4
0
static struct fis_image_desc *
fis_open(void)
{
	struct fis_image_desc *desc;

	if (fis_fd >= 0)
		fis_close();

	fis_fd = mtd_check_open("FIS directory");
	if (fis_fd < 0)
		goto error;

	close(fis_fd);
	fis_fd = mtd_open("FIS directory", true);
	if (fis_fd < 0)
		goto error;

	fis_erasesize = erasesize;
	desc = mmap(NULL, erasesize, PROT_READ|PROT_WRITE, MAP_SHARED, fis_fd, 0);
	if (desc == MAP_FAILED)
		goto error;

	fis_desc = desc;
	return desc;

error:
	fis_close();
	return NULL;
}
Example #5
0
static int
mtd_unlock(const char *mtd)
{
	int fd;
	struct erase_info_user mtdLockInfo;

	fd = mtd_check_open(mtd);
	if(fd <= 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	if (quiet < 2) 
		fprintf(stderr, "Unlocking %s ...\n", mtd);

	mtdLockInfo.start = 0;
	mtdLockInfo.length = mtdsize;
	if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
		close(fd);
		return 0;
	}
		
	close(fd);
	return 0;
}
Example #6
0
static int
mtd_verify(const char *mtd, char *file)
{
	uint32_t f_md5[4], m_md5[4];
	struct stat s;
	md5_ctx_t ctx;
	int ret = 0;
	int fd;

	if (quiet < 2)
		fprintf(stderr, "Verifying %s against %s ...\n", mtd, file);

	if (stat(file, &s) || md5sum(file, f_md5)) {
		fprintf(stderr, "Failed to hash %s\n", file);
		return -1;
	}

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		return -1;
	}

	md5_begin(&ctx);
	do {
		char buf[256];
		int len = (s.st_size > sizeof(buf)) ? (sizeof(buf)) : (s.st_size);
		int rlen = read(fd, buf, len);

		if (rlen < 0) {
			if (errno == EINTR)
				continue;
			ret = -1;
			goto out;
		}
		if (!rlen)
			break;
		md5_hash(buf, rlen, &ctx);
		s.st_size -= rlen;
	} while (s.st_size > 0);

	md5_end(m_md5, &ctx);

	fprintf(stderr, "%08x%08x%08x%08x - %s\n", m_md5[0], m_md5[1], m_md5[2], m_md5[3], mtd);
	fprintf(stderr, "%08x%08x%08x%08x - %s\n", f_md5[0], f_md5[1], f_md5[2], f_md5[3], file);

	ret = memcmp(f_md5, m_md5, sizeof(m_md5));
	if (!ret)
		fprintf(stderr, "Success\n");
	else
		fprintf(stderr, "Failed\n");

out:
	close(fd);
	return ret;
}
Example #7
0
static int
mtd_dump(const char *mtd, int part_offset, int size)
{
	int ret = 0, offset = 0;
	int fd;
	char *buf;

	if (quiet < 2)
		fprintf(stderr, "Dumping %s ...\n", mtd);

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		return -1;
	}

	if (!size)
		size = mtdsize;

	if (part_offset)
		lseek(fd, part_offset, SEEK_SET);

	buf = malloc(erasesize);
	if (!buf)
		return -1;

	do {
		int len = (size > erasesize) ? (erasesize) : (size);
		int rlen = read(fd, buf, len);

		if (rlen < 0) {
			if (errno == EINTR)
				continue;
			ret = -1;
			goto out;
		}
		if (!rlen || rlen != len)
			break;
		if (mtd_block_is_bad(fd, offset)) {
			fprintf(stderr, "skipping bad block at 0x%08x\n", offset);
		} else {
			size -= rlen;
			write(1, buf, rlen);
		}
		offset += rlen;
	} while (size > 0);

out:
	close(fd);
	return ret;
}
Example #8
0
static int mtd_check(const char *mtd)
{
	int fd;

	fd = mtd_check_open(mtd);
	if (!fd)
		return 0;

	if (!buf)
		buf = malloc(erasesize);

	close(fd);
	return 1;
}
Example #9
0
static int
mtd_unlock(const char *mtd)
{
	struct erase_info_user mtdLockInfo;
	char *next = NULL;
	char *str = NULL;
	int fd;

	if (strchr(mtd, ':')) {
		str = strdup(mtd);
		mtd = str;
	}

	do {
		next = strchr(mtd, ':');
		if (next) {
			*next = 0;
			next++;
		}

		fd = mtd_check_open(mtd);
		if(fd < 0) {
			fprintf(stderr, "Could not open mtd device: %s\n", mtd);
			exit(1);
		}

		if (quiet < 2)
			fprintf(stderr, "Unlocking %s ...\n", mtd);

		mtdLockInfo.start = 0;
		mtdLockInfo.length = mtdsize;
		ioctl(fd, MEMUNLOCK, &mtdLockInfo);
		close(fd);
		mtd = next;
	} while (next);

	if (str)
		free(str);

	return 0;
}
Example #10
0
static int
mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
{
	char *next = NULL;
	char *str = NULL;
	int fd, result;
	ssize_t r, w, e;
	ssize_t skip = 0;
	uint32_t offset = 0;
	int jffs2_replaced = 0;
	int skip_bad_blocks = 0;

#ifdef FIS_SUPPORT
	static struct fis_part new_parts[MAX_ARGS];
	static struct fis_part old_parts[MAX_ARGS];
	int n_new = 0, n_old = 0;

	if (fis_layout) {
		const char *tmp = mtd;
		char *word, *brkt;
		int ret;

		memset(&old_parts, 0, sizeof(old_parts));
		memset(&new_parts, 0, sizeof(new_parts));

		do {
			next = strchr(tmp, ':');
			if (!next)
				next = (char *) tmp + strlen(tmp);

			memcpy(old_parts[n_old].name, tmp, next - tmp);

			n_old++;
			tmp = next + 1;
		} while(*next);

		for (word = strtok_r(fis_layout, ",", &brkt);
		     word;
			 word = strtok_r(NULL, ",", &brkt)) {

			tmp = strtok(word, ":");
			strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1);

			tmp = strtok(NULL, ":");
			if (!tmp)
				goto next;

			new_parts[n_new].size = strtoul(tmp, NULL, 0);

			tmp = strtok(NULL, ":");
			if (!tmp)
				goto next;

			new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16);
next:
			n_new++;
		}
		ret = fis_validate(old_parts, n_old, new_parts, n_new);
		if (ret < 0) {
			fprintf(stderr, "Failed to validate the new FIS partition table\n");
			exit(1);
		}
		if (ret == 0)
			fis_layout = NULL;
	}
#endif

	if (strchr(mtd, ':')) {
		str = strdup(mtd);
		mtd = str;
	}

	r = 0;

resume:
	next = strchr(mtd, ':');
	if (next) {
		*next = 0;
		next++;
	}

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}
	if (part_offset > 0) {
		fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset);
		lseek(fd, part_offset, SEEK_SET);
	}

	indicate_writing(mtd);

	w = e = 0;
	for (;;) {
		/* buffer may contain data already (from trx check or last mtd partition write attempt) */
		while (buflen < erasesize) {
			r = read(imagefd, buf + buflen, erasesize - buflen);
			if (r < 0) {
				if ((errno == EINTR) || (errno == EAGAIN))
					continue;
				else {
					perror("read");
					break;
				}
			}

			if (r == 0)
				break;

			buflen += r;
		}

		if (buflen == 0)
			break;

		if (buflen < erasesize) {
			/* Pad block to eraseblock size */
			memset(&buf[buflen], 0xff, erasesize - buflen);
			buflen = erasesize;
		}

		if (skip > 0) {
			skip -= buflen;
			buflen = 0;
			if (skip <= 0)
				indicate_writing(mtd);

			continue;
		}

		if (jffs2file && w >= jffs2_skip_bytes) {
			if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
				if (!quiet)
					fprintf(stderr, "\b\b\b   ");
				if (quiet < 2)
					fprintf(stderr, "\nAppending jffs2 data from %s to %s...", jffs2file, mtd);
				/* got an EOF marker - this is the place to add some jffs2 data */
				skip = mtd_replace_jffs2(mtd, fd, e, jffs2file);
				jffs2_replaced = 1;

				/* don't add it again */
				jffs2file = NULL;

				w += skip;
				e += skip;
				skip -= buflen;
				buflen = 0;
				offset = 0;
				continue;
			}
			/* no EOF marker, make sure we figure out the last inode number
			 * before appending some data */
			mtd_parse_jffs2data(buf, jffs2dir);
		}

		/* need to erase the next block before writing data to it */
		if(!no_erase)
		{
			while (w + buflen > e - skip_bad_blocks) {
				if (!quiet)
					fprintf(stderr, "\b\b\b[e]");

				if (mtd_block_is_bad(fd, e)) {
					if (!quiet)
						fprintf(stderr, "\nSkipping bad block at 0x%08zx   ", e);

					skip_bad_blocks += erasesize;
					e += erasesize;

					// Move the file pointer along over the bad block.
					lseek(fd, erasesize, SEEK_CUR);
					continue;
				}

				if (mtd_erase_block(fd, e) < 0) {
					if (next) {
						if (w < e) {
							write(fd, buf + offset, e - w);
							offset = e - w;
						}
						w = 0;
						e = 0;
						close(fd);
						mtd = next;
						fprintf(stderr, "\b\b\b   \n");
						goto resume;
					} else {
						fprintf(stderr, "Failed to erase block\n");
						exit(1);
					}
				}

				/* erase the chunk */
				e += erasesize;
			}
		}

		if (!quiet)
			fprintf(stderr, "\b\b\b[w]");

		if ((result = write(fd, buf + offset, buflen)) < buflen) {
			if (result < 0) {
				fprintf(stderr, "Error writing image.\n");
				exit(1);
			} else {
				fprintf(stderr, "Insufficient space.\n");
				exit(1);
			}
		}
		w += buflen;

		buflen = 0;
		offset = 0;
	}

	if (jffs2_replaced && trx_fixup) {
		trx_fixup(fd, mtd);
	}

	if (!quiet)
		fprintf(stderr, "\b\b\b\b    ");

	if (quiet < 2)
		fprintf(stderr, "\n");

#ifdef FIS_SUPPORT
	if (fis_layout) {
		if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
			fprintf(stderr, "Failed to update the FIS partition table\n");
	}
#endif

	close(fd);
	return 0;
}
Example #11
0
int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
{
	int err = -1, fdeof = 0;

	outfd = mtd_check_open(mtd);
	if (outfd < 0)
		return -1;

	if (quiet < 2)
		fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
	
	buf = malloc(erasesize);
	if (!buf) {
		fprintf(stderr, "Out of memory!\n");
		goto done;
	}

	if (!*dir)
		target_ino = 1;

	/* parse the structure of the jffs2 first
	 * locate the directory that the file is going to be placed in */
	for(;;) {
		struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;

		if (read(outfd, buf, erasesize) != erasesize) {
			fdeof = 1;
			break;
		}
		mtdofs += erasesize;

		if (node->magic == 0x8519) {
			fprintf(stderr, "Error: wrong endianness filesystem\n");
			goto done;
		}

		/* assume  no magic == end of filesystem
		 * the filesystem will probably end with be32(0xdeadc0de) */
		if (node->magic != 0x1985)
			break;

		mtd_parse_jffs2data(buf, dir);
	}

	if (fdeof) {
		fprintf(stderr, "Error: No room for additional data\n");
		goto done;
	}

	/* jump back one eraseblock */
	mtdofs -= erasesize;
	lseek(outfd, mtdofs, SEEK_SET);

	ofs = 0;

	if (!last_ino)
		last_ino = 1;

	if (!target_ino)
		target_ino = add_dir(dir, 1);

	add_file(filename, target_ino);
	pad(erasesize);

	/* add eof marker, pad to eraseblock size and write the data */
	add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
	pad(erasesize);

	err = 0;

#ifdef target_brcm
	trx_fixup(outfd, mtd);
#endif

done:
	close(outfd);
	if (buf)
		free(buf);

	return err;
}
Example #12
0
static int
mtd_write(int imagefd, const char *mtd)
{
	int fd, result;
	ssize_t r, w, e;

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}
		
	if (quiet < 2)
		fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);

	r = w = e = 0;
	if (!quiet)
		fprintf(stderr, " [ ]");

	for (;;) {
		/* buffer may contain data already (from trx check) */
		do {
			r = read(imagefd, buf + buflen, erasesize - buflen);
			if (r < 0) {
				if ((errno == EINTR) || (errno == EAGAIN))
					continue;
				else {
					perror("read");
					break;
				}
			}

			if (r == 0)
				break;

			buflen += r;
		} while (buflen < erasesize);

		if (buflen == 0)
			break;

		if (jffs2file) {
			if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF)) == 0) {
				if (!quiet)
					fprintf(stderr, "\b\b\b   ");
				if (quiet < 2)
					fprintf(stderr, "\nAppending jffs2 data to from %s to %s...", jffs2file, mtd);
				/* got an EOF marker - this is the place to add some jffs2 data */
				mtd_replace_jffs2(mtd, fd, e, jffs2file);
				goto done;
			}
			/* no EOF marker, make sure we figure out the last inode number
			 * before appending some data */
			mtd_parse_jffs2data(buf, jffs2dir);
		}

		/* need to erase the next block before writing data to it */
		while (w + buflen > e) {
			if (!quiet)
				fprintf(stderr, "\b\b\b[e]");

			mtd_erase_block(fd, e);

			/* erase the chunk */
			e += erasesize;
		}
		
		if (!quiet)
			fprintf(stderr, "\b\b\b[w]");
		
		if ((result = write(fd, buf, buflen)) < buflen) {
			if (result < 0) {
				fprintf(stderr, "Error writing image.\n");
				exit(1);
			} else {
				fprintf(stderr, "Insufficient space.\n");
				exit(1);
			}
		}
		w += buflen;

		buflen = 0;
	}
	if (!quiet)
		fprintf(stderr, "\b\b\b\b");

done:
	if (quiet < 2)
		fprintf(stderr, "\n");

	close(fd);
	return 0;
}
Example #13
0
int
mtd_fixwrgg(const char *mtd, size_t offset, size_t data_size)
{
	int fd;
	char *first_block;
	ssize_t res;
	size_t block_offset;
	size_t data_offset;
	struct wrgg03_header *shdr;

	if (quiet < 2)
		fprintf(stderr, "Trying to fix WRGG header in %s at 0x%x...\n",
			mtd, offset);

	block_offset = offset & ~(erasesize - 1);
	offset -= block_offset;

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	if (block_offset + erasesize > mtdsize) {
		fprintf(stderr, "Offset too large, device size 0x%x\n",
			mtdsize);
		exit(1);
	}

	first_block = malloc(erasesize);
	if (!first_block) {
		perror("malloc");
		exit(1);
	}

	res = pread(fd, first_block, erasesize, block_offset);
	if (res != erasesize) {
		perror("pread");
		exit(1);
	}

	shdr = (struct wrgg03_header *)(first_block + offset);

	/* The magic is always stored in little-endian byte order */
	if (le32_to_cpu((uint8_t *)&shdr->magic1) != WRGG03_MAGIC) {
		fprintf(stderr, "magic1 = %x\n", shdr->magic1);
		fprintf(stderr, "WRGG03_MAGIC = %x\n", WRGG03_MAGIC);
		fprintf(stderr, "No WRGG header found\n");
		exit(1);
	} else if (!shdr->size) {
		fprintf(stderr, "WRGG entity with empty image\n");
		exit(1);
	}

	data_offset = offset + sizeof(struct wrgg03_header);
	if (!data_size)
		data_size = mtdsize - data_offset;
	if (data_size > shdr->size)
		data_size = shdr->size;
	if (wrgg_fix_md5(shdr, fd, data_offset, data_size))
		goto out;

	if (mtd_erase_block(fd, block_offset)) {
		fprintf(stderr, "Can't erease block at 0x%x (%s)\n",
			block_offset, strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "Rewriting block at 0x%x\n", block_offset);

	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "Done.\n");

out:
	close (fd);
	sync();

	return 0;
}
Example #14
0
static int
mtd_fixtrx(const char *mtd, size_t offset)
{
	int fd;
	struct trx_header *trx;
	char *buf;
	ssize_t res;
	size_t block_offset;

	if (quiet < 2)
		fprintf(stderr, "Trying to fix trx header in %s at 0x%x...\n", mtd, offset);

	block_offset = offset & ~(erasesize - 1);
	offset -= block_offset;

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	if (block_offset + erasesize > mtdsize) {
		fprintf(stderr, "Offset too large, device size 0x%x\n", mtdsize);
		exit(1);
	}

	buf = malloc(erasesize);
	if (!buf) {
		perror("malloc");
		exit(1);
	}

	res = pread(fd, buf, erasesize, block_offset);
	if (res != erasesize) {
		perror("pread");
		exit(1);
	}

	trx = (struct trx_header *) (buf + offset);
	if (trx->magic != STORE32_LE(0x30524448)) {
		fprintf(stderr, "No trx magic found\n");
		exit(1);
	}

	if (trx->len == STORE32_LE(erasesize - offset)) {
		if (quiet < 2)
			fprintf(stderr, "Header already fixed, exiting\n");
		close(fd);
		return 0;
	}

	trx->len = STORE32_LE(erasesize - offset);

	trx->crc32 = STORE32_LE(crc32buf((char*) &trx->flag_version, erasesize - offset - 3*4));
	if (mtd_erase_block(fd, block_offset)) {
		fprintf(stderr, "Can't erease block at 0x%x (%s)\n", block_offset, strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "New crc32: 0x%x, rewriting block\n", trx->crc32);

	if (pwrite(fd, buf, erasesize, block_offset) != erasesize) {
		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "Done.\n");

	close (fd);
	sync();
	return 0;

}
Example #15
0
int
mtd_fixseama(const char *mtd, size_t offset)
{
	int fd;
	char *first_block;
	ssize_t res;
	size_t block_offset;
	size_t data_offset;
	size_t data_size;
	struct seama_entity_header *shdr;

	if (quiet < 2)
		fprintf(stderr, "Trying to fix SEAMA header in %s at 0x%x...\n",
			mtd, offset);

	block_offset = offset & ~(erasesize - 1);
	offset -= block_offset;

	fd = mtd_check_open(mtd);
	if(fd < 0) {
		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
		exit(1);
	}

	if (block_offset + erasesize > mtdsize) {
		fprintf(stderr, "Offset too large, device size 0x%x\n",
			mtdsize);
		exit(1);
	}

	first_block = malloc(erasesize);
	if (!first_block) {
		perror("malloc");
		exit(1);
	}

	res = pread(fd, first_block, erasesize, block_offset);
	if (res != erasesize) {
		perror("pread");
		exit(1);
	}

	shdr = (struct seama_entity_header *)(first_block + offset);
	if (shdr->magic != htonl(SEAMA_MAGIC)) {
		fprintf(stderr, "No SEAMA header found\n");
		exit(1);
	} else if (!ntohl(shdr->size)) {
		fprintf(stderr, "Seama entity with empty image\n");
		exit(1);
	}

	data_offset = offset + sizeof(struct seama_entity_header) + ntohs(shdr->metasize);
	data_size = mtdsize - data_offset;
	if (data_size > ntohl(shdr->size))
		data_size = ntohl(shdr->size);
	if (seama_fix_md5(shdr, fd, data_offset, data_size))
		goto out;

	if (mtd_erase_block(fd, block_offset)) {
		fprintf(stderr, "Can't erease block at 0x%x (%s)\n",
			block_offset, strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "Rewriting block at 0x%x\n", block_offset);

	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
		exit(1);
	}

	if (quiet < 2)
		fprintf(stderr, "Done.\n");

out:
	close (fd);
	sync();

	return 0;
}
Example #16
0
int mtd_resetbc(const char *mtd)
{
	struct mtd_info_user mtd_info;
	struct bootcounter *curr = (struct bootcounter *)page;
	unsigned int i;
	unsigned int bc_offset_increment;
	int last_count = 0;
	int num_bc;
	int fd;
	int ret;
	int retval = 0;

	DLOG_OPEN();

	fd = mtd_check_open(mtd);

	if (ioctl(fd, MEMGETINFO, &mtd_info) < 0) {
		DLOG_ERR("Unable to obtain mtd_info for given partition name.");

		retval = -1;
		goto out;
	}


	/* Detect need to override increment (for EA6350v3) */

	if (mtd_info.writesize < BC_OFFSET_INCREMENT_MIN) {

		bc_offset_increment = BC_OFFSET_INCREMENT_MIN;
		DLOG_DEBUG("Offset increment set to %i for writesize of %i",
			   bc_offset_increment, mtd_info.writesize);
	} else {

		bc_offset_increment = mtd_info.writesize;
	}

	num_bc = mtd_info.size / bc_offset_increment;

	for (i = 0; i < num_bc; i++) {
		pread(fd, curr, sizeof(*curr), i * bc_offset_increment);

		/* Existing code assumes erase is to 0xff; left as-is (2019) */

		if (curr->magic != BOOTCOUNT_MAGIC &&
		    curr->magic != 0xffffffff) {
			DLOG_ERR("Unexpected magic %08x at offset %08x; aborting.",
				 curr->magic, i * bc_offset_increment);

			retval = -2;
			goto out;
		}

		if (curr->magic == 0xffffffff)
			break;

		last_count = curr->count;
	}


	if (last_count == 0) {	/* bootcount is already 0 */

		retval = 0;
		goto out;
	}


	if (i == num_bc) {
		DLOG_NOTICE("Boot-count log full with %i entries; erasing (expected occasionally).",
			    i);

		struct erase_info_user erase_info;
		erase_info.start = 0;
		erase_info.length = mtd_info.size;

		ret = ioctl(fd, MEMERASE, &erase_info);
		if (ret < 0) {
			DLOG_ERR("Failed to erase boot-count log MTD; ioctl() MEMERASE returned %i",
				 ret);

			retval = -3;
			goto out;
		}

		i = 0;
	}

	memset(curr, 0xff, bc_offset_increment);

	curr->magic = BOOTCOUNT_MAGIC;
	curr->count = 0;
	curr->checksum = BOOTCOUNT_MAGIC;

	/* Assumes bc_offset_increment is a multiple of mtd_info.writesize */

	ret = pwrite(fd, curr, bc_offset_increment, i * bc_offset_increment);
	if (ret < 0) {
		DLOG_ERR("Failed to write boot-count log entry; pwrite() returned %i",
			 errno);
		retval = -4;
		goto out;

	} else {
		sync();

		DLOG_NOTICE("Boot count sucessfully reset to zero.");

		retval = 0;
		goto out;
	}

out:
	close(fd);
	return retval;
}