summaryrefslogtreecommitdiffstats
path: root/fcp/src/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fcp/src/misc.c')
-rw-r--r--fcp/src/misc.c937
1 files changed, 937 insertions, 0 deletions
diff --git a/fcp/src/misc.c b/fcp/src/misc.c
new file mode 100644
index 0000000..5fdfbfd
--- /dev/null
+++ b/fcp/src/misc.c
@@ -0,0 +1,937 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * File: fcp_misc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: misc helpers
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/version.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+#define COMPARE_SIZE 256UL
+
+static regex_t * regex_create(const char * str)
+{
+ assert(str != NULL);
+
+ regex_t * self = (regex_t *)malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ return self;
+ }
+
+ if (regcomp(self, str, REG_ICASE | REG_NOSUB) != 0) {
+ free(self);
+ ERRNO(errno);
+ return NULL;
+ }
+
+ return self;
+}
+
+static int regex_free(regex_t * self)
+{
+ if (self == NULL)
+ return 0;
+
+ regfree(self);
+ free(self);
+
+ return 0;
+}
+
+int parse_offset(const char *str, off_t *offset)
+{
+ assert(offset != NULL);
+
+ if (str == NULL) {
+ *offset = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *offset = strtoull(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") ||
+ !strcasecmp(end, "KB") ||
+ !strcasecmp(end, "K"))
+ *offset <<= 10;
+ else if (!strcmp(end, "MiB") ||
+ !strcasecmp(end, "MB") ||
+ !strcasecmp(end, "M"))
+ *offset <<= 20;
+ else if (!strcmp(end, "GiB") ||
+ !strcasecmp(end, "GB") ||
+ !strcasecmp(end, "G"))
+ *offset <<= 30;
+ else {
+ UNEXPECTED("invalid offset specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_size(const char *str, size_t *size)
+{
+ assert(size != NULL);
+
+ if (str == NULL) {
+ *size = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *size = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") || !strcasecmp(end, "K") ||
+ !strcasecmp(end, "KB"))
+ *size <<= 10;
+ else if (!strcmp(end, "MiB") || !strcasecmp(end, "M") ||
+ !strcasecmp(end, "MB"))
+ *size <<= 20;
+ else if (!strcmp(end, "GiB") || !strcasecmp(end, "G") ||
+ !strcasecmp(end, "GB"))
+ *size <<= 30;
+ else {
+ UNEXPECTED("invalid size specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_number(const char *str, size_t *num)
+{
+ assert(num != NULL);
+
+ if (str == NULL) {
+ *num = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *num = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ UNEXPECTED("invalid number specified '%s'", end);
+ return -1;
+ }
+
+ return 0;
+}
+
+int parse_path(const char * path, char ** type, char ** target, char ** name)
+{
+ assert(path != NULL);
+
+ *type = *target = *name = NULL;
+
+ char * delim1 = strchr(path, ':');
+ char * delim2 = strrchr(path, ':');
+
+ if (delim1 == NULL && delim2 == NULL) { // <target>
+ if (asprintf(target, "%s", path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ } else if (delim1 == delim2) { // <target>:<name>
+ if (asprintf(target, "%.*s", delim1 - path, path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim1++;
+ if (asprintf(name, "%s", delim1) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (valid_type(*target) == true) {
+ *type = *target;
+ *target = *name;
+ *name = NULL;
+ }
+ } else if (delim1 != delim2) { // <type>:<target>:<name>
+ if (asprintf(type, "%.*s", delim1 - path, path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim1++;
+ if (asprintf(target, "%.*s", delim2 - delim1, delim1) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim2++;
+ if (asprintf(name, "%s", delim2) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int dump_errors(const char * name, FILE * out)
+{
+ assert(name != NULL);
+
+ if (out == NULL)
+ out = stderr;
+
+ err_t * err = NULL;
+
+ while ((err = err_get()) != NULL) {
+ switch (err_type(err)) {
+ case ERR_VERSION:
+ fprintf(out, "%s: %s : %s(%d) : v%d.%02d.%04d %.*s\n",
+ basename((char *)name), err_type_name(err),
+ basename(err_file(err)), err_line(err),
+ VER_TO_MAJOR(err_code(err)),
+ VER_TO_MINOR(err_code(err)),
+ VER_TO_PATCH(err_code(err)),
+ err_size(err), (char *)err_data(err));
+ break;
+ default:
+ fprintf(out, "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ basename((char *)name), err_type_name(err),
+ basename(err_file(err)), err_line(err),
+ err_code(err), err_size(err),
+ (char *)err_data(err));
+ }
+ }
+
+ return 0;
+}
+
+int check_file(const char * path, FILE * file, off_t offset) {
+ assert(file != NULL);
+
+ switch (__ffs_fcheck(file, offset)) {
+ case 0:
+ return 0;
+ case FFS_CHECK_HEADER_MAGIC:
+ UNEXPECTED("'%s' no partition table found at offset '%llx'\n",
+ path, offset);
+ return -1;
+ case FFS_CHECK_HEADER_CHECKSUM:
+ UNEXPECTED("'%s' partition table at offset '%llx', is "
+ "corrupted\n", path, offset);
+ return -1;
+ case FFS_CHECK_ENTRY_CHECKSUM:
+ UNEXPECTED("'%s' partition table at offset '%llx', has "
+ "corrupted entries\n", path, offset);
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+entry_list_t * entry_list_create(ffs_t * ffs)
+{
+ entry_list_t * self = (entry_list_t *)malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ list_init(&self->list);
+
+ self->ffs = ffs;
+
+ return self;
+}
+
+entry_list_t * entry_list_create_by_regex(ffs_t * ffs, const char * name)
+{
+ assert(ffs != NULL);
+
+ if (name == NULL)
+ name = ".*";
+
+ RAII(regex_t*, rx, regex_create(name), regex_free);
+ if (rx == NULL)
+ return NULL;
+
+ entry_list_t * self = entry_list_create(ffs);
+ if (self == NULL)
+ return NULL;
+
+ int entry_list(ffs_entry_t * entry)
+ {
+ assert(entry != NULL);
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+ if (regexec(rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, entry, sizeof(*entry));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+ }
+
+ if (__ffs_iterate_entries(ffs, entry_list) < 0) {
+ entry_list_delete(self);
+ return NULL;
+ }
+
+ return self;
+}
+
+int entry_list_add(entry_list_t * self, ffs_entry_t * entry)
+{
+ assert(self != NULL);
+ assert(entry != NULL);
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, entry, sizeof(entry_node->entry));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+}
+
+int entry_list_add_child(entry_list_t * self, ffs_entry_t * parent)
+{
+ assert(self != NULL);
+ assert(parent != NULL);
+
+ int child_entry_list(ffs_entry_t * child)
+ {
+ assert(child != NULL);
+
+ if (child->pid != parent->id)
+ return 0;
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, child, sizeof(*child));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+ }
+
+ if (__ffs_iterate_entries(self->ffs, child_entry_list) < 0)
+ return -1;
+
+ return 0;
+}
+
+int entry_list_remove(entry_list_t * self, entry_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ list_remove_node(&self->list, &node->node);
+
+ return 0;
+}
+
+int entry_list_delete(entry_list_t * self)
+{
+ if (self == NULL)
+ return 0;
+
+ while (!list_empty(&self->list)) {
+ free(container_of(list_remove_head(&self->list), entry_node_t,
+ node));
+ }
+
+ return 0;
+}
+
+int entry_list_exists(entry_list_t * self, ffs_entry_t * entry)
+{
+ assert(self != NULL);
+ assert(entry != NULL);
+
+ list_iter_t it;
+ entry_node_t * entry_node;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * __entry = &entry_node->entry;
+
+ if (__entry->base == entry->base &&
+ __entry->size == entry->size)
+ return 1;
+ }
+
+ return 0;
+}
+
+ffs_entry_t * entry_list_find(entry_list_t * self, const char * name)
+{
+ assert(self != NULL);
+ assert(name != NULL);
+
+ list_iter_t it;
+ entry_node_t * entry_node = NULL;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+
+ if (strcmp(entry->name, name) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+int entry_list_dump(entry_list_t * self, FILE * out)
+{
+ assert(self != NULL);
+ if (out == NULL)
+ out = stdout;
+
+ list_iter_t it;
+ entry_node_t * entry_node = NULL;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+
+ fprintf(stderr, "id[%d] pid[%d] name[%s]\n",
+ entry->id, entry->pid, entry->name);
+ }
+
+ return 0;
+}
+
+FILE *__fopen(const char * type, const char * target, const char * mode,
+ int debug)
+{
+ assert(target != NULL);
+ assert(mode != NULL);
+
+ FILE *file = NULL;
+ size_t port = 0;
+
+ if (type == NULL)
+ type = TYPE_FILE;
+
+ if (strcasecmp(type, TYPE_AA) == 0) {
+ if (parse_number(target, &port) < 0)
+ return NULL;
+ UNEXPECTED("Removed support");
+ //file = fopen_aaflash(port, mode, debug);
+ } else if (strcasecmp(type, TYPE_RW) == 0) {
+ UNEXPECTED("Removed support");
+ //file = fopen_rwflash(target, mode, debug);
+ } else if (strcasecmp(type, TYPE_SFC) == 0) {
+ UNEXPECTED("FIX ME");
+ return NULL;
+ } else if (strcasecmp(type, TYPE_FILE) == 0) {
+ file = fopen(target, mode);
+ if (file == NULL)
+ ERRNO(errno);
+ } else {
+ errno = EINVAL;
+ ERRNO(errno);
+ }
+
+ return file;
+}
+
+int is_file(const char * type, const char * target, const char * name)
+{
+ return type == NULL && target != NULL && name == NULL;
+}
+
+int valid_type(const char * type)
+{
+ return type == NULL ? 0 :
+ strcasecmp(type, TYPE_FILE) == 0 ||
+ strcasecmp(type, TYPE_RW) == 0 ||
+ strcasecmp(type, TYPE_AA) == 0 ||
+ strcasecmp(type, TYPE_SFC) == 0;
+}
+
+int fcp_read_entry(ffs_t * src, const char * name, FILE * out)
+{
+ assert(src != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(src, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(src, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(src, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: read partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, name, buffer, offset, count);
+
+
+ rc = fwrite(buffer, 1, rc, out);
+ if (rc <= 0 && ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_write_entry(ffs_t * dst, const char * name, FILE * in)
+{
+ assert(dst != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(dst, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: write partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = fread(buffer, 1, count, in);
+ if (rc <= 0) {
+ if (feof(in)) {
+ break;
+ } else if (ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ rc = __ffs_entry_write(dst, name, buffer, offset, rc);
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_erase_entry(ffs_t * dst, const char * name, char fill)
+{
+ assert(dst != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memset(buffer, fill, buffer_size);
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(dst, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.size * block_size;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: erase partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_write(dst, name, buffer, offset, count);
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.size * block_size,
+ total);
+ }
+ }
+
+ if (__ffs_entry_truncate(dst, name, 0ULL) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_copy_entry(ffs_t * src, const char * src_name,
+ ffs_t * dst, const char * dst_name)
+{
+ assert(src != NULL);
+ assert(src_name != NULL);
+ assert(dst != NULL);
+ assert(dst_name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t src_entry;
+ if (__ffs_entry_find(src, src_name, &src_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, src_name);
+ return -1;
+ }
+
+ ffs_entry_t dst_entry;
+ if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, dst_name);
+ return -1;
+ }
+
+ size_t total = 0;
+ size_t size = src_entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8llx: %s: copy partition %8x/%8x",
+ src->offset, dst_name, src_entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, src_name, buffer, offset, count);
+ if (rc < 0)
+ return -1;
+ rc = __ffs_entry_write(dst, dst_name, buffer, offset, rc);
+ if (rc < 0)
+ return -1;
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", src_entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_compare_entry(ffs_t * src, const char * src_name,
+ ffs_t * dst, const char * dst_name)
+{
+ assert(src != NULL);
+ assert(src_name != NULL);
+ assert(dst != NULL);
+ assert(dst_name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+
+ RAII(void*, src_buffer, malloc(buffer_size), free);
+ if (src_buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ RAII(void*, dst_buffer, malloc(buffer_size), free);
+ if (dst_buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t src_entry;
+ if (__ffs_entry_find(src, src_name, &src_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, src_name);
+ return -1;
+ }
+
+ ffs_entry_t dst_entry;
+ if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, dst_name);
+ return -1;
+ }
+
+ size_t total = 0;
+ size_t size = src_entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8llx: %s: compare partition %8x/%8x",
+ src->offset, dst_name, src_entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, src_name, src_buffer, offset, count);
+ if (rc < 0)
+ return -1;
+ rc = __ffs_entry_read(dst, dst_name, dst_buffer, offset, rc);
+ if (rc < 0)
+ return -1;
+
+ char * src_ptr = src_buffer;
+ char * dst_ptr = dst_buffer;
+ size_t cnt = 0;
+
+ while (cnt < count) {
+ size_t cmp_sz = min(count - cnt, COMPARE_SIZE);
+
+ if (memcmp(src_ptr, dst_ptr, cmp_sz) != 0) {
+ UNEXPECTED("MISCOMPARE! '%s' != '%s' at "
+ "offset '%llx'\n", src_name,
+ dst_name, offset + cnt);
+
+ if (isatty(fileno(stderr)))
+ fprintf(stderr, " <== [ERROR]\n");
+
+ return -1;
+ }
+
+ src_ptr += cmp_sz;
+ dst_ptr += cmp_sz;
+ cnt += cmp_sz;
+ }
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", src_entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
OpenPOWER on IntegriCloud