summaryrefslogtreecommitdiffstats
path: root/fcp
diff options
context:
space:
mode:
Diffstat (limited to 'fcp')
-rw-r--r--fcp/.gitignore3
-rw-r--r--fcp/Makefile10
-rw-r--r--fcp/Rules.mk30
-rwxr-xr-xfcp/fcp.sh313
-rw-r--r--fcp/ppc/Makefile9
-rw-r--r--fcp/src/cmd_copy.c582
-rw-r--r--fcp/src/cmd_erase.c203
-rw-r--r--fcp/src/cmd_list.c166
-rw-r--r--fcp/src/cmd_probe.c179
-rw-r--r--fcp/src/cmd_read.c187
-rw-r--r--fcp/src/cmd_trunc.c163
-rw-r--r--fcp/src/cmd_user.c201
-rw-r--r--fcp/src/cmd_write.c202
-rw-r--r--fcp/src/main.c709
-rw-r--r--fcp/src/main.h120
-rw-r--r--fcp/src/misc.c937
-rw-r--r--fcp/src/misc.h72
-rw-r--r--fcp/x86/Makefile7
18 files changed, 4093 insertions, 0 deletions
diff --git a/fcp/.gitignore b/fcp/.gitignore
new file mode 100644
index 0000000..3a1f0a1
--- /dev/null
+++ b/fcp/.gitignore
@@ -0,0 +1,3 @@
+*/ffs
+*/fcp
+test/test_libffs
diff --git a/fcp/Makefile b/fcp/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/fcp/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/fcp/Rules.mk b/fcp/Rules.mk
new file mode 100644
index 0000000..490afd4
--- /dev/null
+++ b/fcp/Rules.mk
@@ -0,0 +1,30 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64
+CFLAGS += -I$(DEPTH)/.. -iquote..
+
+LDFLAGS += -L. -m32
+
+NAME=fcp
+
+CLIB=$(DEPTH)/../clib/x86/libclib.a
+FFS=$(DEPTH)/../ffs/x86/libffs.a
+
+OBJS=cmd_list.o cmd_read.o cmd_write.o cmd_erase.o \
+ cmd_copy.o cmd_trunc.o cmd_user.o misc.o main.o
+
+TARGETS=$(NAME)
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+$(NAME): $(OBJS) $(FFS) $(CLIB)
+ $(CC) $(LDFLAGS) -Wl,-no-whole-archive \
+ -o $@ $^ -lpthread -ldl -lrt
+
+install: $(TARGETS)
+ $(INSTALL) fcp $(FCP_INSTALL)/bin
+ $(INSTALL) ../fcp.sh $(FCP_INSTALL_TEST)
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/fcp/fcp.sh b/fcp/fcp.sh
new file mode 100755
index 0000000..6fa56a1
--- /dev/null
+++ b/fcp/fcp.sh
@@ -0,0 +1,313 @@
+#!/bin/bash
+# ===================================================================
+# 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: ffs cmdline test script
+# Author: Shaun wetzstein <shaun@us.ibm.com>
+# Date: 02/06/13
+#
+# Todo:
+# 1) function to check syntax of each command
+
+CP=cp
+RM=rm
+FFS=ffs
+FCP=fcp
+MKDIR=mkdir
+GREP=grep
+HEAD=head
+HEX=hexdump
+CRC=crc32
+DD=dd
+DIFF=diff
+CAT=cat
+CP=cp
+TRUNC=truncate
+
+TARGET=test.nor
+COPY=copy.nor
+TMP=/tmp/fcp.$$
+URANDOM=/dev/urandom
+POFFSET="0x00000,0x10000"
+
+FAIL=1
+PASS=0
+
+KB=$((1*1024))
+MB=$(($KB*1024))
+
+function expect()
+{
+ local expect=${1}
+ shift
+ local command=${*}
+ eval $command
+ local actual=${?}
+
+ if [[ ${expect} -eq ${actual} ]]; then
+ echo "[PASSED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ else
+ echo "[FAILED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ exit 1
+ fi
+}
+
+function pass()
+{
+ expect ${PASS} ${*}
+}
+
+function fail()
+{
+ expect ${FAIL} ${*}
+}
+
+function size()
+{
+ local size=$(stat -L -c %s ${2})
+ if [[ ${1} == ${size} ]]; then
+ echo "[PASSED] size: '${2}' ===> expect=${1}, actual=${size}"
+ else
+ echo "[FAILED] size: '${2}' ===> expect=${1}, actual=${size}"
+ exit 1
+ fi
+}
+
+function crc()
+{
+ local crc=$(${CRC} ${2})
+ if [[ ${1} == ${crc} ]]; then
+ echo "[PASSED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ else
+ echo "[FAILED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ exit 1
+ fi
+}
+
+function setup()
+{
+ local target=${TMP}/${TARGET}
+ local output=${TMP}/${TARGET}.txt
+
+ pass ${RM} -rf ${TMP}
+ pass ${MKDIR} -p ${TMP}
+ pass ${RM} -f ${target}
+
+ pass ${FFS} -t ${target} -s 64M -b 64K -p 0x3F0000 -C
+ pass ${FFS} -t ${target} -s 64M -b 64K -p 0x7F0000 -C
+
+ for ((j=0; j<2; j++)); do
+ local name="logical${j}"
+ local base=$((${j}*$MB*4))
+
+ pass ${FFS} -t ${target} -n ${name} -g 0 -l -A
+ pass ${FFS} -t ${target} -n ${name} -L > ${output}
+
+ pass ${GREP} ${name} ${output} > /dev/null
+ pass ${GREP} "l-----" ${output} > /dev/null
+
+ for ((i=0; i<4; i++)); do
+ local full="${name}/entry${i}"
+ local offset=$((${base}+${i}*$MB))
+
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ pass ${FFS} -t ${target} -o ${offset} -s ${size} \
+ -g 0 -n ${full} -a ${i} -A
+
+ pass ${FFS} -t ${target} -n ${full} -L > ${output}
+ pass ${GREP} ${full} ${output} > /dev/null
+ pass ${GREP} "d-----" ${output} > /dev/null
+
+ local range=$(printf "%.8x-%.8x" ${offset} \
+ $((${offset}+${size}-1)))
+
+ pass ${GREP} ${range} ${output} > /dev/null
+ pass ${GREP} $(printf "%x" ${size}) ${output} > \
+ /dev/null
+ pass ${RM} -f ${output}
+ done
+ done
+}
+
+function cleanup()
+{
+ pass ${RM} -rf ${TMP}
+}
+
+function erase()
+{
+ local target=${TMP}/${TARGET}
+ local name="logical"
+
+ for ((i=0; i<4; i++)); do
+ local output=${TMP}/entry${i}.hex
+ local full=${name}0/entry${i}
+
+ pass ${FCP} ${target}:${full} -E ${i}
+ pass ${FCP} -T ${target}:${full}
+ pass ${FCP} -R ${target}:${full} - | ${HEX} -C > ${output}
+
+ local p=$(printf "%2.2x %2.2x %2.2x %2.2x" $i $i $i $i)
+ pass ${GREP} \"${p} ${p}\" ${output}
+
+ pass ${RM} -f ${output}
+ done
+
+ pass ${FCP} ${target}:${name}0 -E 0x00
+ pass ${FCP} ${target}:${name}1 -E 0x00
+}
+
+function write()
+{
+ local target=${TMP}/${TARGET}
+ local output=${TMP}/${TARGET}.txt
+
+ local name="logical"
+ local input=${TMP}/write.in
+ local output=${TMP}/write.out
+
+ if [[ -z "${1}" ]]; then
+ local block=$((64*KB))
+ else
+ local block=${1}
+ fi
+
+ for ((i=0; i<4; i++)); do
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ local count=$((${size}/$block))
+
+ for ((c=1; c<=${count}; c++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} \
+ count=${c} 2> /dev/null
+
+ pass ${CRC} ${input} > ${output}
+ local crc=$(${CAT} ${output})
+ local full=${name}0/entry${i}
+
+ pass ${FCP} ${target}:${full} -E 0x00
+ pass ${FCP} -W ${input} ${target}:${full}
+ pass ${FCP} ${target}:${full} -U 0=${crc}
+ pass ${FCP} ${target}:${full} -R - -f | cat > ${output}
+
+ size $((${c}*${block})) ${output}
+ crc ${crc} ${output}
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FCP} ${target}:${full} -o 0x3F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+ pass "${FCP} ${target}:${full} -o 0x7F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input} ${output}
+ done
+ done
+}
+
+function copy()
+{
+ local src=${TMP}/${TARGET}
+ local dst=${TMP}/${TARGET}.copy
+
+ local input=${TMP}/copy.in
+ local output=${TMP}/copy.out
+
+ if [[ -z "${1}" ]]; then
+ local block=$((64*KB))
+ else
+ local block=${1}
+ fi
+
+ pass ${TRUNC} -s $(stat -L -c %s ${src}) ${dst}
+
+ for ((i=0; i<4; i++)); do
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ local count=$((${size}/$block))
+
+ for ((c=1; c<=${count}; c++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${c} 2> /dev/null
+ local crc=$(${CRC} ${input})
+
+ local name="logical0/entry${i}"
+
+ pass ${FCP} ${src}:${name} -E 0x00
+ pass ${FCP} ${input} ${src}:${name} -W
+ pass ${FCP} ${src}:${name} ${output}.src -f -R
+ pass ${FCP} ${src}:${name} -U 0 0=${crc}
+
+ pass ${FCP} ${src}:${name} ${dst}:${name} -C -v -f
+ pass ${FCP} ${src}:${name} ${dst}:${name} -M -v
+
+ pass ${FCP} ${dst}:${name} ${output}.dst -R -f
+ size $((${c}*${block})) ${output}.src
+ size $((${c}*${block})) ${output}.dst
+ crc ${crc} ${output}.src
+ crc ${crc} ${output}.dst
+ pass ${DIFF} ${output}.src ${output}.dst
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FCP} ${dst}:${name} -o 0x3F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+ pass "${FCP} ${dst}:${name} -o 0x7F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input}* ${output}*
+ done
+ done
+
+ pass ${FCP} ${src}":logical0" ${src}":logical1" -C -v # logical mirror
+ pass ${FCP} ${src}":logical1" ${dst}":logical1" -C -v # logical copy
+
+ expect 2 ${DIFF} ${src} ${dst}
+}
+
+function main()
+{
+ erase
+ write $((21*$KB))
+ write $((64*$KB))
+ copy $((21*$KB))
+ copy $((64*$KB))
+}
+
+setup
+if [[ -z "${1:-}" ]]; then
+ main
+else
+ case "$1" in
+ erase ) erase ;;
+ write ) write ;;
+ copy ) copy ;;
+ * ) echo "$1 not implemented"; exit 1 ;;
+ esac
+ exit 0;
+fi
+cleanup
diff --git a/fcp/ppc/Makefile b/fcp/ppc/Makefile
new file mode 100644
index 0000000..65df0dc
--- /dev/null
+++ b/fcp/ppc/Makefile
@@ -0,0 +1,9 @@
+DEPTH = ../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.ppc.mk
+
+ARCH=ppc
+FCP_INSTALL = $(INST_USR)
+FCP_INSTALL_TEST = $(INST_TESTS)
+
+include ../Rules.mk
diff --git a/fcp/src/cmd_copy.c b/fcp/src/cmd_copy.c
new file mode 100644
index 0000000..4b496b6
--- /dev/null
+++ b/fcp/src/cmd_copy.c
@@ -0,0 +1,582 @@
+/*
+ * 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: cmd_copy.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: copy & compare implementation
+ * 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/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"
+
+static int validate_files(ffs_t * src, ffs_t * dst)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ size_t s, d;
+
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' block "
+ "size differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_BLOCK_COUNT, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_COUNT, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' block "
+ "count differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_ENTRY_SIZE, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_ENTRY_SIZE, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' entry "
+ "size differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_VERSION, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_VERSION, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' version "
+ "differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __copy_entry(args_t * args,
+ ffs_t * src_ffs, ffs_entry_t * src_entry,
+ ffs_t * dst_ffs, ffs_entry_t * dst_entry,
+ entry_list_t * done_list)
+{
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ return -1;
+ }
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0) {
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (dst_entry->type == FFS_TYPE_PARTITION) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: partition table (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_PARTITION) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: partition table (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED) {
+ if (dst_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", dst_ffs->offset,
+ full_dst_name);
+ return 0;
+ }
+ if (src_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", src_ffs->offset,
+ full_src_name);
+ return 0;
+ }
+ }
+
+ if (src_entry->size != dst_entry->size) {
+ UNEXPECTED("%8llx: %s: source partition '%s' size mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (src_entry->type != dst_entry->type) {
+ UNEXPECTED("%8llx: %s: source partition '%s' type mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (__ffs_entry_truncate(dst_ffs, full_dst_name,
+ src_entry->actual) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n",
+ dst_ffs->offset, full_dst_name, src_entry->actual);
+
+ uint32_t src_val, dst_val;
+ for (uint32_t i=0; i<FFS_USER_WORDS; i++) {
+ __ffs_entry_user_get(src_ffs, full_src_name, i, &src_val);
+ __ffs_entry_user_get(dst_ffs, full_dst_name, i, &dst_val);
+
+ if (args->force != f_FORCE && i == USER_DATA_VOL)
+ continue;
+
+ if (src_val != dst_val) {
+ if (__ffs_entry_user_put(dst_ffs, full_dst_name,
+ i, src_val) < 0)
+ return -1;
+ }
+ }
+ if (args->verbose == f_VERBOSE)
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy user[] from '%s' "
+ "(done)\n", dst_ffs->offset, full_dst_name,
+ src_ffs->path);
+
+ if (entry_list_exists(done_list, src_entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy from '%s' (skip)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, src_entry) < 0)
+ return -1;
+
+ if (fcp_copy_entry(src_ffs, full_src_name, dst_ffs, full_dst_name) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy from '%s' (done)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+
+ return 0;
+}
+
+static int __compare_entry(args_t * args,
+ ffs_t * src_ffs, ffs_entry_t * src_entry,
+ ffs_t * dst_ffs, ffs_entry_t * dst_entry,
+ entry_list_t * done_list)
+{
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ return -1;
+ }
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0) {
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED) {
+ if (dst_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", dst_ffs->offset,
+ full_dst_name);
+ return 0;
+ }
+ if (src_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", src_ffs->offset,
+ full_src_name);
+ return 0;
+ }
+ }
+
+ if (src_entry->size != dst_entry->size) {
+ UNEXPECTED("%8llx: %s: source partition '%s' size mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (src_entry->type != dst_entry->type) {
+ UNEXPECTED("%8llx: %s: source partition '%s' type mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (entry_list_exists(done_list, src_entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: compare from '%s' (skip)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, src_entry) < 0)
+ return -1;
+
+ if (fcp_compare_entry(src_ffs, full_src_name,
+ dst_ffs, full_dst_name) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: compare from '%s' (done)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+
+ return 0;
+}
+
+static int __force_part(ffs_t * src, FILE * dst)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ char part[block_size];
+ memset(part, 0, block_size);
+
+ if (__ffs_entry_read(src, FFS_PARTITION_NAME, part,
+ 0, block_size) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (__ffs_info(src, FFS_INFO_OFFSET, &offset) < 0)
+ return -1;
+
+ if (fseek(dst, offset, SEEK_SET) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fwrite(part, 1, block_size, dst) != block_size) {
+ if (ferror(dst)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ if (fflush(dst) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __copy_compare(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+ assert(done_list != NULL);
+
+ char * src_type = args->src_type;
+ char * src_target = args->src_target;
+ char * src_name = args->src_name;
+
+ char * dst_type = args->dst_type;
+ char * dst_target = args->dst_target;
+ char * dst_name = src_name;
+
+ if (args->dst_name != NULL && *args->dst_name != '\0')
+ dst_name = args->dst_name;
+
+ if (src_name == NULL)
+ src_name = "*";
+ if (dst_name == NULL)
+ dst_name = "*";
+
+ RAII(FILE*, src_file, __fopen(src_type, src_target, "r", debug),
+ fclose);
+ if (src_file == NULL)
+ return -1;
+ if (check_file(src_target, src_file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, src_ffs, __ffs_fopen(src_file, offset), __ffs_fclose);
+ if (src_ffs == NULL)
+ return -1;
+
+ src_ffs->path = basename(src_target);
+ done_list->ffs = src_ffs;
+
+ RAII(FILE*, dst_file, __fopen(dst_type, dst_target, "r+", debug),
+ fclose);
+ if (dst_file == NULL)
+ return -1;
+
+ if (args->force == f_FORCE && args->cmd == c_COPY) {
+ if (__force_part(src_ffs, dst_file) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: partition table '%s' => '%s' "
+ "(done)\n", offset, src_target, dst_target);
+ }
+
+ if (check_file(dst_target, dst_file, offset) < 0)
+ return -1;
+
+ RAII(ffs_t*, dst_ffs, __ffs_fopen(dst_file, offset), __ffs_fclose);
+ if (dst_ffs == NULL)
+ return -1;
+
+ dst_ffs->path = basename(dst_target);
+
+ if (validate_files(src_ffs, dst_ffs) < 0)
+ return -1;
+
+ if (src_ffs->count <= 0) // fix me
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(src_ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(src_ffs, buffer) < 0)
+ return -1;
+ if (__ffs_buffer(dst_ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t src_parent;
+ ffs_entry_t dst_parent;
+
+ if (strcmp(src_name, "*") == 0 && strcmp(dst_name, "*") == 0) {
+ src_parent.type = FFS_TYPE_LOGICAL;
+ src_parent.id = -1;
+ dst_parent.type = FFS_TYPE_LOGICAL;
+ dst_parent.id = -1;
+ } else if ((src_name != NULL) && (dst_name != NULL)) {
+ if (__ffs_entry_find(src_ffs, src_name, &src_parent) == false) {
+ UNEXPECTED("%8llx: partition entry '%s' not found in "
+ "'%s'\n", src_ffs->offset, src_name,
+ src_target);
+ return -1;
+ }
+
+ if (__ffs_entry_find(dst_ffs, dst_name, &dst_parent) == false) {
+ UNEXPECTED("%8llx: partition entry '%s' not found in "
+ "'%s'\n", dst_ffs->offset, dst_name,
+ dst_target);
+ return -1;
+ }
+ }
+
+ if (src_parent.type == FFS_TYPE_DATA &&
+ dst_parent.type == FFS_TYPE_DATA) {
+ if (args->cmd == c_COPY)
+ return __copy_entry(args, src_ffs, &src_parent,
+ dst_ffs, &dst_parent, done_list);
+ else
+ return __compare_entry(args, src_ffs, &src_parent,
+ dst_ffs, &dst_parent, done_list);
+ } else if (src_parent.type == FFS_TYPE_LOGICAL &&
+ dst_parent.type == FFS_TYPE_LOGICAL) {
+
+ RAII(entry_list_t*, src_list, entry_list_create(src_ffs),
+ entry_list_delete);
+ if (src_list == NULL)
+ return -1;
+ if (entry_list_add_child(src_list, &src_parent) < 0)
+ return -1;
+
+ RAII(entry_list_t*, dst_list, entry_list_create(dst_ffs),
+ entry_list_delete);
+ if (dst_list == NULL)
+ return -1;
+ if (entry_list_add_child(dst_list, &dst_parent) < 0)
+ return -1;
+
+ while (list_empty(&src_list->list) == 0) {
+ RAII(entry_node_t*, src_node,
+ container_of(list_head(&src_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(src_list, src_node);
+ ffs_entry_t * src_entry = &src_node->entry;
+
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ free(src_node);
+ return -1;
+ }
+
+ if (list_empty(&dst_list->list)) {
+ UNEXPECTED("source entry '%s' not a child of "
+ "destination entry '%s'\n",
+ full_src_name, dst_name);
+ return -1;
+ }
+
+ RAII(entry_node_t*, dst_node,
+ container_of(list_head(&dst_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(dst_list, dst_node);
+ ffs_entry_t * dst_entry = &dst_node->entry;
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(dst_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0)
+ return -1;
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(src_list,
+ src_entry) < 0)
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(dst_list,
+ dst_entry) < 0)
+ return -1;
+ }
+
+ if (args->cmd == c_COPY) {
+ if (__copy_entry(args, src_ffs, src_entry,
+ dst_ffs, dst_entry,
+ done_list) < 0)
+ return -1;
+ } else if (args->cmd == c_COMPARE) {
+ if (__compare_entry(args, src_ffs, src_entry,
+ dst_ffs, dst_entry,
+ done_list) < 0) {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int command_copy_compare(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __copy_compare(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_erase.c b/fcp/src/cmd_erase.c
new file mode 100644
index 0000000..972f0be
--- /dev/null
+++ b/fcp/src/cmd_erase.c
@@ -0,0 +1,203 @@
+/*
+ * 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: cmd_erase.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: erase implementation
+ * 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/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"
+
+static int __erase(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ size_t fill;
+ if (args->opt_nr == 1) {
+ fill = 0xFF;
+ } else if (args->opt_nr == 2) {
+ if (parse_number(args->opt[1], &fill) < 0)
+ return -1;
+ }
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ ffs->path = basename(target);
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t parent;
+ if (__ffs_entry_find(ffs, name, &parent) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ RAII(entry_list_t*, entry_list, entry_list_create(ffs),
+ entry_list_delete);
+ if (entry_list == NULL)
+ return -1;
+ if (entry_list_add(entry_list, &parent) < 0)
+ return -1;
+
+ while (list_empty(&entry_list->list) == 0) {
+ RAII(entry_node_t*, entry_node,
+ container_of(list_head(&entry_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(entry_list, entry_node);
+ ffs_entry_t * entry = &entry_node->entry;
+
+ if (entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(entry_list, entry) < 0)
+ return -1;
+ continue;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (args->protected != f_PROTECTED) {
+ if (entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "(skip)\n", offset, full_name);
+ continue;
+ }
+ }
+
+ if (__ffs_entry_truncate(ffs, full_name, 0ULL) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n",
+ offset, full_name, 0);
+
+ if (entry_list_exists(done_list, entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: erase partition "
+ "(skip)\n", offset, full_name);
+ continue;
+ }
+
+ if (entry_list_add(done_list, entry) < 0)
+ return -1;
+
+ if (fcp_erase_entry(ffs, full_name, (char)fill) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: erase partition (done)\n",
+ offset, full_name);
+ }
+
+ return 0;
+}
+
+int command_erase(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __erase(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_list.c b/fcp/src/cmd_list.c
new file mode 100644
index 0000000..069407a
--- /dev/null
+++ b/fcp/src/cmd_list.c
@@ -0,0 +1,166 @@
+/*
+ * 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: cmd_list.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: list implementation
+ * 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/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"
+
+static int list(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ fprintf(stdout, "========================[ PARTITION TABLE"
+ " 0x%llx ]=======================\n", ffs->offset);
+ fprintf(stdout, "vers:%04x size:%04x * blk:%06x blk(s):"
+ "%06x * entsz:%06x ent(s):%06x\n",
+ ffs->hdr->version, ffs->hdr->size, ffs->hdr->block_size,
+ ffs->hdr->block_count, ffs->hdr->entry_size,
+ ffs->hdr->entry_count);
+ fprintf(stdout, "----------------------------------"
+ "----------------------------------"
+ "-------\n");
+
+ RAII(entry_list_t*, entry_list, entry_list_create_by_regex(ffs, name),
+ entry_list_delete);
+ if (entry_list == NULL)
+ return -1;
+
+ list_iter_t it;
+ entry_node_t * entry_node;
+
+ list_iter_init(&it, &entry_list->list, LI_FLAG_FWD);
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+ char full_name[page_size];
+
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ size_t offset = entry->base * ffs->hdr->block_size;
+ size_t size = entry->size * ffs->hdr->block_size;
+
+ char type;
+ if (entry->type == FFS_TYPE_LOGICAL)
+ type ='l';
+ else if (entry->type == FFS_TYPE_DATA)
+ type ='d';
+ else if (entry->type == FFS_TYPE_PARTITION)
+ type ='p';
+ fprintf(stdout, "%3d [%08x-%08x] [%8x:%8x] ",
+ entry->id, offset, offset+size-1, size, entry->actual);
+
+ fprintf(stdout, "[%c%c%c%c%c%c] %s\n",
+ type, '-', '-', '-',
+ entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-',
+ entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-',
+ full_name);
+
+ if (args->verbose == f_VERBOSE) {
+ for (int i=0; i<FFS_USER_WORDS; i++) {
+ fprintf(stdout, "[%2d] %8x ", i,
+ entry->user.data[i]);
+ if ((i+1) % 4 == 0)
+ fprintf(stdout, "\n");
+ }
+ }
+ }
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+int command_list(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = list(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_probe.c b/fcp/src/cmd_probe.c
new file mode 100644
index 0000000..b0e55a8
--- /dev/null
+++ b/fcp/src/cmd_probe.c
@@ -0,0 +1,179 @@
+/*
+ * 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: cmd_probe.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: probe implementation
+ * 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/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include <spinor/spinor.h>
+#include <spinor/aardvark.h>
+
+#include <dbgx/rwflash.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int aa_probe(int verbose)
+{
+ int size = 16;
+
+ uint16_t ports[size];
+ uint32_t unique_ids[size];
+
+ int rc = aa_find_devices_ext(size, ports, size, unique_ids);
+
+ fprintf(stdout, "%d aardvark(s) found:\n", rc);
+
+ for (int i = 0; i < min(rc, 16); i++) {
+ if (ports[i] & AA_PORT_NOT_FREE) {
+ fprintf(stdout, " %d: [%04d-%06d] ===> BUSY",
+ ports[i] & ~AA_PORT_NOT_FREE,
+ unique_ids[i] / 1000000,
+ unique_ids[i] % 1000000);
+
+ continue;
+ }
+
+ spinor_t * nor = spinor_open(ports[i]);
+ if (nor == NULL) {
+ debug("unable to open aaflash on port '%d'\n", ports[i]);
+ continue;
+ }
+
+ if (spinor_configure(nor) < 0) {
+ debug("unable to configure Aardvark on port '%d'\n",
+ ports[i]);
+ continue;
+ }
+
+ fprintf(stdout, " %d: [%04d-%06d] => ",
+ ports[i] & ~AA_PORT_NOT_FREE,
+ unique_ids[i] / 1000000, unique_ids[i] % 1000000);
+
+ spinor_id_t id;
+ if (spinor_read_id(nor, id) < 0) {
+ debug("unable to read device id\n");
+ continue;
+ }
+
+ fprintf(stdout, "%s %s %s",
+ spinor_id_mfg(id), spinor_id_type(id),
+ spinor_id_capacity(id));
+
+ fprintf(stdout, " [%2.2x %2.2x %2.2x]\n",
+ id[0], id[1], id[2]);
+
+ if (spinor_close(nor) < 0) {
+ debug("unable to read device id\n");
+ continue;
+ }
+
+ nor = NULL;
+ }
+
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+static int rw_probe(const char * host, int verbose)
+{
+ assert(host != NULL);
+
+ rwflash_cookie_t * cookie = rwflash_cookie_open(host, verbose);
+ if (cookie == NULL) {
+ debug("unable to open rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ if (rwflash_cookie_init(cookie) < 0) {
+ debug("unable to init rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ if (rwflash_cookie_probe(cookie) < 0) {
+ debug("unable to probe rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ uint8_t * id = spinor_id(rwflash_cookie_spinor(cookie));
+ if (id == NULL) {
+ debug("invalid id rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ fprintf(stdout, " %s ===> %s %s %s",
+ host,
+ spinor_id_mfg(id), spinor_id_type(id),
+ spinor_id_capacity(id));
+
+ fprintf(stdout, " [%2.2x %2.2x %2.2x]\n",
+ id[0], id[1], id[2]);
+
+ return 0;
+}
+
+int command_probe(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+
+ int verbose = args->verbose == f_VERBOSE;
+
+ if (strcasecmp(type, TYPE_AA) == 0) {
+ rc = aa_probe(verbose);
+ } else if (strcasecmp(type, TYPE_RW) == 0) {
+ rc = rw_probe(target, verbose);
+ } else {
+ UNEXPECTED("NOT IMPLEMENTED YET!");
+ rc = -1;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_read.c b/fcp/src/cmd_read.c
new file mode 100644
index 0000000..7923b46
--- /dev/null
+++ b/fcp/src/cmd_read.c
@@ -0,0 +1,187 @@
+/*
+ * 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: cmd_read.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: read implementation
+ * 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 <clib/attribute.h>
+#include <clib/assert.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"
+
+static int __read(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * type = args->src_type;
+ char * target = args->src_target;
+ char * name = args->src_name;
+
+ char * out_path = args->dst_target;
+
+ RAII(FILE*, file, __fopen(type, target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ if (entry.actual == 0) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: is empty (skip)\n",
+ offset, name);
+ return 0;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, name);
+ return 0;
+ }
+
+ if (entry_list_exists(done_list, &entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: write to '%s' (skip)\n",
+ offset, full_name, out_path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, &entry) < 0)
+ return -1;
+
+ if (strcmp(out_path, "-") == 0) {
+ if (fcp_read_entry(ffs, full_name, stdout) < 0)
+ return -1;
+ } else {
+ if (access(out_path, F_OK) == 0 && args->force != f_FORCE) {
+ UNEXPECTED("output file '%s' already exists, "
+ "use --force to overwrite\n", out_path);
+ return -1;
+ }
+
+ RAII(FILE*, out, fopen(out_path, "w+"), fclose);
+ if (out == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fcp_read_entry(ffs, full_name, out) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: write to '%s' (done)\n",
+ offset, full_name, out_path);
+ }
+
+ return 0;
+}
+
+int command_read(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __read(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_trunc.c b/fcp/src/cmd_trunc.c
new file mode 100644
index 0000000..cf65788
--- /dev/null
+++ b/fcp/src/cmd_trunc.c
@@ -0,0 +1,163 @@
+/*
+ * 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: cmd_trunc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: trunc implementation
+ * 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/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"
+
+static int __trunc(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags && FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ size_t size = 0;
+ if (args->opt_nr == 1) {
+ size = entry.size * block_size;
+ } else if (args->opt_nr == 2) {
+ if (parse_number(args->opt[1], &size) < 0)
+ return -1;
+ }
+
+ if (__ffs_entry_truncate(ffs, full_name, size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: truncate '%x' (done)\n",
+ offset, full_name, size);
+
+ return 0;
+}
+
+int command_trunc(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __trunc(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_user.c b/fcp/src/cmd_user.c
new file mode 100644
index 0000000..46637ce
--- /dev/null
+++ b/fcp/src/cmd_user.c
@@ -0,0 +1,201 @@
+/*
+ * 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: cmd_user.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: user implementation
+ * 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/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 DELIM '='
+
+static int __user(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags && FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ // parse <word>[=<value>]
+
+ if (args->opt_nr <= 1) {
+ for (uint32_t word=0; word<FFS_USER_WORDS; word++) {
+ uint32_t value = 0;
+
+ if (__ffs_entry_user_get(ffs, name, word, &value) < 0)
+ return -1;
+
+ fprintf(stdout, "%8llx: %s: [%02d] = %08x\n",
+ offset, full_name, word, value);
+ }
+ fprintf(stdout, "\n");
+ } else {
+ for (int i=1; i<args->opt_nr; i++) {
+ if (args->opt[i] == '\0') {
+ UNEXPECTED("invalid user '%s', use form "
+ "'<word>[=<value>]'\n",
+ args->opt[i]);
+ return -1;
+ }
+
+ char * __value = strrchr(args->opt[i], DELIM);
+ if (__value != NULL)
+ *__value = '\0';
+
+ size_t word = 0;
+ if (parse_number(args->opt[i], &word) < 0)
+ return -1;
+
+ uint32_t value = 0;
+ if (__value != NULL) { // write
+ if (parse_number(__value+1, &value) < 0)
+ return -1;
+
+ *__value = DELIM;
+
+ if (__ffs_entry_user_put(ffs, full_name,
+ word, value) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: [%02d] = "
+ "%08x\n", offset, full_name,
+ word, value);
+ } else { // read
+ if (__ffs_entry_user_get(ffs, full_name,
+ word, &value) < 0)
+ return -1;
+
+ if (isatty(fileno(stdout)))
+ fprintf(stdout, "%8llx: %s: [%02d] = "
+ "%08x\n", offset, full_name,
+ word, value);
+ else
+ fprintf(stdout, "%x", value);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int command_user(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __user(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_write.c b/fcp/src/cmd_write.c
new file mode 100644
index 0000000..5152532
--- /dev/null
+++ b/fcp/src/cmd_write.c
@@ -0,0 +1,202 @@
+/*
+ * 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: cmd_write.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: write implementation
+ * 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/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"
+
+static int __write(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * in_path = args->src_target;
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ ffs->path = basename(target);
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ struct stat st;
+ if (stat(in_path, &st) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (entry.actual < st.st_size) {
+ if (__ffs_entry_truncate(ffs, full_name,
+ st.st_size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%llx' (done)\n",
+ offset, full_name, st.st_size);
+ }
+
+ if (entry_list_exists(done_list, &entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: read from '%s' (skip)\n",
+ offset, full_name, in_path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, &entry) < 0)
+ return -1;
+
+ if (strcmp(in_path, "-") == 0) {
+ if (fcp_write_entry(ffs, full_name, stdin) < 0)
+ return -1;
+ } else {
+ RAII(FILE*, in, fopen(in_path, "r"), fclose);
+ if (in == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fcp_write_entry(ffs, full_name, in) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: read from '%s' (done)\n",
+ offset, full_name, in_path);
+ }
+
+ return 0;
+}
+
+int command_write(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __write(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/main.c b/fcp/src/main.c
new file mode 100644
index 0000000..c122c3f
--- /dev/null
+++ b/fcp/src/main.c
@@ -0,0 +1,709 @@
+/*
+ * 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_main.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cp for FFS files
+ * Date: 04/25/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/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"
+
+args_t args;
+size_t page_size;
+
+int verbose;
+int debug;
+
+static const char copyright[] __unused__ =
+ "Licensed Internal Code - Property of IBM\n"
+ "IBM Support Processor Licensed Internal Code\n"
+ "(c) Copyright IBM Corp 2013 All Rights Reserved\n"
+ "US Government Users Restricted Rights - Use, duplication\n"
+ "or disclosure restricted by GSA ADP Schedule Contract\n"
+ "with IBM Corp.";
+
+static void usage(bool verbose)
+{
+ FILE *e = stderr;
+
+ fprintf(e,
+ "fcp - FFS copy v%d.%d.%d -- Authors: "
+ "<shaun@us.ibm.com>\n (c) Copyright IBM Corp 2013 All Rights "
+ "Reserved\n", FCP_MAJOR, FCP_MINOR, FCP_PATCH);
+
+ fprintf(e, "\n");
+ fprintf(e, "Usage:\n");
+ fprintf(e," fcp [<src_type>:]<src_target>[:<src_name>] -PLETU"
+ "\n [-b <size>] [-o <offset,...>] [-fpvdh]\n");
+ fprintf(e," fcp [<src_type>:]<src_target>[:<src_name>] "
+ "[<dst_type>:]<dst_target>[:<dst_name>] -RWCM"
+ "\n [-b <size>] [-o <offset,...>] [-fpvdh]\n");
+ fprintf(e, "\n");
+ fprintf(e, " <type>\n");
+ fprintf(e, " 'aa' : Aardvark USB probe\n");
+ fprintf(e, " 'rw' : RISCWatch Ethernet probe\n");
+ fprintf(e, " 'sfc' : FSP SFC character device\n");
+ fprintf(e, " 'file' : UNIX regular file\n");
+ fprintf(e, " <target>\n");
+ fprintf(e, " 'aa' : <number> USB device number [0..9]\n");
+ fprintf(e, " 'rw' : <hostname>@<port> RISCwatch probe\n");
+ fprintf(e, " 'sfc' : <path> to SFC char device\n");
+ fprintf(e, " 'file' : <path> to UNIX regular file\n");
+ fprintf(e, " <name>\n");
+ fprintf(e, " : FFS name, e.g. bank0/bootenv/card\n");
+ fprintf(e, "\n");
+
+ if (verbose) {
+ fprintf(e, "Examples:\n");
+ fprintf(e, " fcp -P aa:0\n");
+ fprintf(e, " fcp -P rw:riscwatch.ibm.com\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -L aa:0\n");
+ fprintf(e, " fcp -L rw:riscwatch.ibm.com\n");
+ fprintf(e, " fcp -L nor.mif\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -E 0 nor.mif:bank0\n");
+ fprintf(e, " fcp -E 0xff nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -W ipl.bin file:nor:bank0/ipl\n");
+ fprintf(e, " fcp -R file:nor:bank0/ipl ipl.bin\n");
+ fprintf(e, "\n");
+ fprintf(e, " cat ipl.bin | fcp -R file:nor:bank0/ipl -\n");
+ fprintf(e, " fcp -W file:nor:bank0/ipl - | cat > ipl.bin\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -C file:nor:bank0 rw:host.ibm.com:bank0\n");
+ fprintf(e, " fcp -C file:nor rw:host.ibm.com@6470\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -M file:nor:bank0 rw:host.ibm.com:bank0\n");
+ fprintf(e, " fcp -M file:nor:* rw:host.ibm.com@6470:*\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -C nor.mif:bank0 nor.mif:bank1\n");
+ fprintf(e, " fcp -C rw:host:bank0/ipl aa:0:bank1/ipl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -M nor.mif:bank0 nor.mif:bank1\n");
+ fprintf(e, " fcp -M rw:host:bank0/ipl aa:0:bank1/ipl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -T nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -T 0 nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -U nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -U 0 1 2 nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -U 0=0xffffffff 1=0 nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ }
+
+ /* =============================== */
+
+ fprintf(e, "Commands:\n");
+
+ fprintf(e, " -P, --probe\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the SPI NOR device Id\n\n");
+
+ fprintf(e, " -L, --list\n");
+ if (verbose)
+ fprintf(e,
+ "\n List the contents of a partition table\n\n");
+
+ fprintf(e, " -R, --read\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the contents of a partition and write "
+ "the data to an output file (use\n '-' for stdout)."
+ "\n\n");
+
+ fprintf(e, " -W, --write\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the contents of an input file (use '-' for "
+ "stdin) and write the data\n to a partition.\n\n");
+
+ fprintf(e, " -E, --erase <value>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Fill the contents of a partition with <value>. "
+ "<value> is a decimal (or hex)\n number, default is "
+ "0xFF.\n\n");
+
+ fprintf(e, " -C, --copy\n");
+ if (verbose)
+ fprintf(e,
+ "\n Copy source partition(s) to destination "
+ "partition(s). Both source and\n destination name(s) " "can specify either 'data' or 'logical' partitions."
+ "\n\n");
+
+ fprintf(e, " -T, --trunc <size>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Truncate the actual size of partition(s) to "
+ "<size> size bytes. <size> is\n a decimal (or hex) "
+ "number, default is the partition entry size\n\n");
+
+ fprintf(e, " -M, --compare\n");
+ if (verbose)
+ fprintf(e,
+ "\n Compare source partition(s) to destination "
+ "partition(s). Both source and\n destination name(s) "
+ "can specify either 'data' or 'logical' partitions."
+ "\n\n");
+
+ fprintf(e, " -U, --user [<word>[=<value>] ...]\n");
+ if (verbose)
+ fprintf(e,
+ "\n Get or set a user word. <word> and <value> are "
+ "decimal (or hex) numbers.\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e, "Options:\n");
+ fprintf(e, " -o, --offset <offset[,offset]>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies a comma (,) separated list of"
+ " partition table offsets, in bytes\n from the start"
+ " of the target file (or device).\n\n");
+
+ fprintf(e, " -b, --buffer <value>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Specifies the buffer size used by --read, --write,"
+ " --copy and --compare\n commands <value> is a "
+ "decimal (or hex) number, default is buffer size.\n\n");
+ fprintf(e, "\n");
+
+ /* =============================== */
+
+ fprintf(e, "Flags:\n");
+
+ fprintf(e, " -f, --force\n");
+ if (verbose)
+ fprintf(e, "\n Override command safe guards\n\n");
+
+ fprintf(e, " -p, --protected\n");
+ if (verbose)
+ fprintf(e, "\n Do not ignore protected partition "
+ "entries\n\n");
+
+ fprintf(e, " -v, --verbose\n");
+ if (verbose)
+ fprintf(e, "\n Write progress messages to stdout\n\n");
+
+ fprintf(e, " -d, --debug\n");
+ if (verbose)
+ fprintf(e, "\n Write debug messages to stdout\n\n");
+
+ fprintf(e, " -h, --help\n");
+ if (verbose)
+ fprintf(e, "\n Write this help text to stdout and exit\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e, "Report bugs to <https://bugzilla.linux.ibm.com/>"
+ " (vendor='MCP for FSP*')\n");
+}
+
+static int process_argument(args_t * args, int opt, const char *optarg)
+{
+ assert(args != NULL);
+
+ switch (opt) {
+ case c_PROBE: /* probe */
+ case c_LIST: /* list */
+ case c_READ: /* read */
+ case c_WRITE: /* write */
+ case c_ERASE: /* erase */
+ case c_COPY: /* copy */
+ case c_TRUNC: /* trunc */
+ case c_COMPARE: /* compare */
+ case c_USER: /* user */
+ if (args->cmd != c_ERROR) {
+ UNEXPECTED("commands '%c' and '%c' are mutually "
+ "exclusive", args->cmd, opt);
+ return -1;
+ }
+ args->cmd = (cmd_t) opt;
+ break;
+ case o_OFFSET: /* offset */
+ args->offset = strdup(optarg);
+ break;
+ case o_BUFFER: /* buffer */
+ args->buffer = strdup(optarg);
+ break;
+ case f_FORCE: /* force */
+ args->force = (flag_t) opt;
+ break;
+ case f_PROTECTED: /* protected */
+ args->protected = (flag_t) opt;
+ break;
+ case f_VERBOSE: /* verbose */
+ verbose = 1;
+ args->verbose = (flag_t) opt;
+ break;
+ case f_DEBUG: /* debug */
+ debug = 1;
+ args->debug = (flag_t) opt;
+ break;
+ case f_HELP: /* help */
+ usage(true);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?': /* error */
+ default:
+ UNEXPECTED("unknown option '%c', please see --help for "
+ "details\n", opt);
+ return -1;
+ }
+ return 0;
+}
+
+static int process_option(args_t * args, const char *opt)
+{
+ assert(args != NULL);
+ assert(opt != NULL);
+
+ if (args->opt_sz <= args->opt_nr) {
+ size_t size = sizeof(*args->opt);
+
+ args->opt_sz += 5;
+ args->opt = (const char **)realloc(args->opt,
+ size * args->opt_sz);
+ if (args->opt == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memset(args->opt + args->opt_nr, 0,
+ size * (args->opt_sz - args->opt_nr));
+ }
+
+ args->opt[args->opt_nr] = strdup(opt);
+ args->opt_nr++;
+
+ return 0;
+}
+
+/*
+ * probe:
+ * fcp [<type>:]<target>[:<path>] -P
+ * list:
+ * fcp <path> -L
+ * fcp [<type>:]<target>[:<path>] -L
+ * erase:
+ * fcp [<type>:]<target>[:<path>] -E <pad>
+ * trunc:
+ * fcp [<type>:]<target>[:<path>] -T <size>
+ * user:
+ * fcp [<type>:]<target>[:<path>] -U <word>[=<value>] ...
+ * write:
+ * fcp <path> [<type>:]<target>:<path> -W
+ * read:
+ * fcp [<type>:]<target>:<path> <path> -R
+ * copy:
+ * fcp <type>:]<target>:<source> [<type>:]<target>:<dest> -C
+ * fcp [<type>:]<source>:<path> [<type>:]<destination>:<path> -C
+ * compare:
+ * fcp <type>:]<target>:<source> [<type>:]<target>:<dest> -M
+ * fcp [<type>:]<source>:<path> [<type>:]<destination>:<path> -M
+ */
+static int validate_args(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->offset == NULL) {
+ UNEXPECTED("--offset is required for all '%s' "
+ "commands", args->short_name);
+ return -1;
+ }
+
+ switch (args->cmd) {
+ case c_PROBE:
+ case c_LIST:
+ case c_ERASE:
+ case c_TRUNC:
+ case c_USER:
+ if (args->opt_nr < 1) {
+ UNEXPECTED("invalid options, please see --help for "
+ "details");
+ return -1;
+ }
+
+ if (parse_path(args->opt[0], &args->dst_type,
+ &args->dst_target, &args->dst_name) < 0)
+ return -1;
+
+ break;
+ case c_READ:
+ case c_WRITE:
+ case c_COPY:
+ case c_COMPARE:
+ if (args->opt_nr < 2) {
+ UNEXPECTED("invalid options, please see --help for "
+ "details");
+ return -1;
+ }
+
+ if (parse_path(args->opt[0], &args->src_type,
+ &args->src_target, &args->src_name) < 0)
+ return -1;
+ if (parse_path(args->opt[1], &args->dst_type,
+ &args->dst_target, &args->dst_name) < 0)
+ return -1;
+
+ break;
+ case 0:
+ UNEXPECTED("specify a command, or please see --help "
+ "for details");
+ return -1;
+ default:
+ UNEXPECTED("invalid command '%c', please see --help for "
+ "details", args->cmd);
+ return -1;
+
+ }
+
+ debug("cmd[%c]\n", args->cmd);
+
+ debug(" src_type: '%s'\n", args->src_type);
+ debug("src_target: '%s'\n", args->src_target);
+ debug(" src_name: '%s'\n", args->src_name);
+
+ debug(" dst_type: '%s'\n", args->dst_type);
+ debug("dst_target: '%s'\n", args->dst_target);
+ debug(" dst_name: '%s'\n", args->dst_name);
+
+ #define REQ_OPT(name,cmd) ({ \
+ if (args->name == 0) { \
+ UNEXPECTED("--%s is required for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ #define REQ_FIELD(name,cmd) ({ \
+ if (args->name == 0) { \
+ UNEXPECTED("<%s> is required for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ #define UNSUP_OPT(name,cmd) ({ \
+ if (args->name != 0) { \
+ UNEXPECTED("--%s is unsupported for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ if (args->cmd == c_PROBE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --probe [--verbose]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 1) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ UNSUP_OPT(buffer, probe);
+ } else if (args->cmd == c_LIST) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --list [--verbose]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 1) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ UNSUP_OPT(buffer, list);
+ } else if (args->cmd == c_READ) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_source>"
+ ":<src_name> <path> --read [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(src_name, read);
+ } else if (args->cmd == c_WRITE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s <path> [<dst_type>:]"
+ "<dst_target>:<dst_name> --write [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+
+ REQ_FIELD(dst_name, write);
+ } else if (args->cmd == c_ERASE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --erase <value> [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(dst_name, erase);
+ } else if (args->cmd == c_TRUNC) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --trunc <value> [--verbose] "
+ "[--force] [--protected]\n", args->short_name);
+ }
+ if (2 < args->opt_nr) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(dst_name, trunc);
+
+ UNSUP_OPT(buffer, trunc);
+ } else if (args->cmd == c_USER) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --user <value>[=<value] "
+ "[--verbose] [--force] [--protected]\n",
+ args->short_name);
+ }
+
+ REQ_FIELD(dst_name, user);
+
+ UNSUP_OPT(buffer, user);
+ } else if (args->cmd == c_COPY) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_target>"
+ "[:<src_name>] [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --copy [--verbose] [--force] "
+ "[--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+ } else if (args->cmd == c_COMPARE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_target>"
+ "[:<src_name>] [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --compare [--verbose] [--force] "
+ "[--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+ } else {
+ UNEXPECTED("invalid command '%c'", args->cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_args(args_t * args)
+{
+ assert(args != NULL);
+ int rc = 0;
+
+ switch (args->cmd) {
+ case c_PROBE:
+ //rc = command_probe(args);
+ break;
+ case c_LIST:
+ rc = command_list(args);
+ break;
+ case c_READ:
+ rc = command_read(args);
+ break;
+ case c_WRITE:
+ rc = command_write(args);
+ break;
+ case c_ERASE:
+ rc = command_erase(args);
+ break;
+ case c_TRUNC:
+ rc = command_trunc(args);
+ break;
+ case c_USER:
+ rc = command_user(args);
+ break;
+ case c_COPY:
+ case c_COMPARE:
+ rc = command_copy_compare(args);
+ break;
+ default:
+ UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void args_dump(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->short_name != NULL)
+ printf("short_name[%s]\n", args->short_name);
+ printf("cmd[%c]\n", args->cmd);
+ if (args->offset != NULL)
+ printf("offset[%s]\n", args->offset);
+ if (args->buffer != NULL)
+ printf("buffer[%s]\n", args->buffer);
+ if (args->force != 0)
+ printf("force[%c]\n", args->force);
+ if (args->protected != 0)
+ printf("protected[%c]\n", args->protected);
+ if (args->verbose != 0)
+ printf("verbose[%c]\n", args->verbose);
+ if (args->debug != 0)
+ printf("debug[%c]\n", args->debug);
+
+ for (int i = 0; i < args->opt_nr; i++) {
+ if (args->opt[i] != NULL)
+ printf("opt%d[%s]\n", i, args->opt[i]);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_opt[] = {
+ /* commands */
+ {"probe", no_argument, NULL, c_PROBE},
+ {"list", no_argument, NULL, c_LIST},
+ {"read", no_argument, NULL, c_READ},
+ {"write", no_argument, NULL, c_WRITE},
+ {"erase", no_argument, NULL, c_ERASE},
+ {"copy", no_argument, NULL, c_COPY},
+ {"trunc", no_argument, NULL, c_TRUNC},
+ {"compare", no_argument, NULL, c_COMPARE},
+ {"user", no_argument, NULL, c_USER},
+ /* options */
+ {"offset", required_argument, NULL, o_OFFSET},
+ {"buffer", required_argument, NULL, o_BUFFER},
+ /* flags */
+ {"force", no_argument, NULL, f_FORCE},
+ {"protected", no_argument, NULL, f_PROTECTED},
+ {"verbose", no_argument, NULL, f_VERBOSE},
+ {"debug", no_argument, NULL, f_DEBUG},
+ {"help", no_argument, NULL, f_HELP},
+ {0, 0, 0, 0}
+ };
+
+ static const char *short_opt;
+ short_opt = "PLRWECTMUo:b:fpvdh";
+
+ int rc = EXIT_FAILURE;
+
+ setlinebuf(stdout);
+
+ if (argc == 1)
+ usage(false), exit(rc);
+
+ int opt = 0, idx = 0;
+ while ((opt = getopt_long(argc, argv, short_opt, long_opt, &idx)) != -1)
+ if (process_argument(&args, opt, optarg) < 0)
+ goto error;
+
+ /* getopt_long doesn't know what to do with orphans, */
+ /* so we'll scoop them up here, and deal with them later */
+
+ while (optind < argc)
+ if (process_option(&args, argv[optind++]) < 0)
+ goto error;
+
+ if (args.debug == f_DEBUG)
+ args_dump(&args);
+
+ if (validate_args(&args) < 0)
+ goto error;
+ if (process_args(&args) < 0)
+ goto error;
+
+ rc = EXIT_SUCCESS;
+
+ if (false) {
+error:
+ dump_errors(args.short_name, stderr);
+ }
+
+ return rc;
+}
+
+static void __ctor__(void) __constructor;
+static void __ctor__(void)
+{
+ page_size = sysconf(_SC_PAGESIZE);
+
+ args.short_name = program_invocation_short_name;
+ args.offset = "0x3F0000,0x7F0000";
+}
diff --git a/fcp/src/main.h b/fcp/src/main.h
new file mode 100644
index 0000000..93d8f59
--- /dev/null
+++ b/fcp/src/main.h
@@ -0,0 +1,120 @@
+/*
+ * 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: main.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cmdline tool for libffs.so
+ * Date: 05/12/2012
+ */
+
+#ifndef __FCP_H__
+#define __FCP_H__
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include <ffs/libffs.h>
+
+#define FCP_MAJOR 0x01
+#define FCP_MINOR 0x00
+#define FCP_PATCH 0x00
+
+#define TYPE_FILE "file"
+#define TYPE_RW "rw"
+#define TYPE_AA "aa"
+#define TYPE_SFC "sfc"
+
+#define verbose(fmt, args...) \
+ ({if (verbose) printf("%s: " fmt, __func__, ##args); })
+#define debug(fmt, args...) \
+ ({if (debug) printf("%s: " fmt, __func__, ##args); })
+
+typedef enum {
+ c_ERROR = 0,
+ c_PROBE = 'P',
+ c_LIST = 'L',
+ c_READ = 'R',
+ c_WRITE = 'W',
+ c_ERASE = 'E',
+ c_COPY = 'C',
+ c_TRUNC = 'T',
+ c_COMPARE = 'M',
+ c_USER = 'U',
+} cmd_t;
+
+typedef enum {
+ o_ERROR = 0,
+ o_OFFSET = 'o',
+ o_BUFFER = 'b',
+} option_t;
+
+typedef enum {
+ f_ERROR = 0,
+ f_FORCE = 'f',
+ f_PROTECTED = 'p',
+ f_VERBOSE = 'v',
+ f_DEBUG = 'd',
+ f_HELP = 'h',
+} flag_t;
+
+typedef struct {
+ const char *short_name;
+
+ cmd_t cmd;
+ char *src_type, *src_target, *src_name;
+ char *dst_type, *dst_target, *dst_name;
+
+ /* options */
+ const char *offset;
+ const char *buffer;
+
+ /* flags */
+ flag_t force;
+ flag_t protected;
+ flag_t verbose;
+ flag_t debug;
+
+ const char **opt;
+ int opt_sz, opt_nr;
+} args_t;
+
+extern args_t args;
+extern size_t page_size;
+
+extern int verbose;
+extern int debug;
+
+extern int fcp_read_entry(ffs_t *, const char *, FILE *);
+extern int fcp_write_entry(ffs_t *, const char *, FILE *);
+extern int fcp_erase_entry(ffs_t *, const char *, char);
+extern int fcp_copy_entry(ffs_t *, const char *, ffs_t *, const char *);
+extern int fcp_compare_entry(ffs_t *, const char *, ffs_t *, const char *);
+
+extern int command_probe(args_t *);
+extern int command_list(args_t *);
+extern int command_read(args_t *);
+extern int command_write(args_t *);
+extern int command_erase(args_t *);
+extern int command_copy_compare(args_t *);
+extern int command_trunc(args_t *);
+extern int command_compare(args_t *);
+extern int command_user(args_t *);
+
+#endif /* __FCP_H__ */
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;
+}
diff --git a/fcp/src/misc.h b/fcp/src/misc.h
new file mode 100644
index 0000000..c96eb11
--- /dev/null
+++ b/fcp/src/misc.h
@@ -0,0 +1,72 @@
+/*
+ * 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.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Misc. helpers
+ * Date: 05/12/2012
+ */
+
+#ifndef __MISC__H__
+#define __MISC__H__
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <regex.h>
+
+#include <clib/list.h>
+
+#include <ffs/libffs.h>
+
+typedef struct entry_list entry_list_t;
+struct entry_list {
+ list_t list;
+ ffs_t * ffs;
+};
+
+typedef struct entry_node entry_node_t;
+struct entry_node {
+ list_node_t node;
+ ffs_entry_t entry;
+};
+
+extern entry_list_t * entry_list_create(ffs_t *);
+extern entry_list_t * entry_list_create_by_regex(ffs_t *, const char *);
+extern int entry_list_add(entry_list_t *, ffs_entry_t *);
+extern int entry_list_add_child(entry_list_t *, ffs_entry_t *);
+extern int entry_list_remove(entry_list_t *, entry_node_t *);
+extern int entry_list_delete(entry_list_t *);
+extern int entry_list_exists(entry_list_t *, ffs_entry_t *);
+extern ffs_entry_t * entry_list_find(entry_list_t *, const char *);
+extern int entry_list_dump(entry_list_t *, FILE *);
+
+extern int parse_offset(const char *, off_t *);
+extern int parse_size(const char *, size_t *);
+extern int parse_number(const char *, size_t *);
+extern int parse_path(const char *, char **, char **, char **);
+
+extern int dump_errors(const char *, FILE *);
+extern int check_file(const char *, FILE *, off_t);
+extern int is_file(const char *, const char *, const char *);
+extern int valid_type(const char *);
+
+extern FILE *__fopen(const char *, const char *, const char *, int);
+
+#endif /* __MISC__H__ */
diff --git a/fcp/x86/Makefile b/fcp/x86/Makefile
new file mode 100644
index 0000000..2a5a240
--- /dev/null
+++ b/fcp/x86/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ..
+
+ARCH=x86
+FCP_INSTALL = $(INST_USR_X86)
+FCP_INSTALL_TEST = $(INST_TESTS_X86)
+
+include ../Rules.mk
OpenPOWER on IntegriCloud