From bf4630076762d9c20c16c80c1c791f352b046dd1 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Mon, 30 Jun 2014 22:10:16 -0500 Subject: Port FFS tools over from Building Block repository. --- fcp/.gitignore | 3 + fcp/Makefile | 10 + fcp/Rules.mk | 30 ++ fcp/fcp.sh | 313 ++++++++++++++++++ fcp/ppc/Makefile | 9 + fcp/src/cmd_copy.c | 582 ++++++++++++++++++++++++++++++++ fcp/src/cmd_erase.c | 203 ++++++++++++ fcp/src/cmd_list.c | 166 ++++++++++ fcp/src/cmd_probe.c | 179 ++++++++++ fcp/src/cmd_read.c | 187 +++++++++++ fcp/src/cmd_trunc.c | 163 +++++++++ fcp/src/cmd_user.c | 201 +++++++++++ fcp/src/cmd_write.c | 202 +++++++++++ fcp/src/main.c | 709 +++++++++++++++++++++++++++++++++++++++ fcp/src/main.h | 120 +++++++ fcp/src/misc.c | 937 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fcp/src/misc.h | 72 ++++ fcp/x86/Makefile | 7 + 18 files changed, 4093 insertions(+) create mode 100644 fcp/.gitignore create mode 100644 fcp/Makefile create mode 100644 fcp/Rules.mk create mode 100755 fcp/fcp.sh create mode 100644 fcp/ppc/Makefile create mode 100644 fcp/src/cmd_copy.c create mode 100644 fcp/src/cmd_erase.c create mode 100644 fcp/src/cmd_list.c create mode 100644 fcp/src/cmd_probe.c create mode 100644 fcp/src/cmd_read.c create mode 100644 fcp/src/cmd_trunc.c create mode 100644 fcp/src/cmd_user.c create mode 100644 fcp/src/cmd_write.c create mode 100644 fcp/src/main.c create mode 100644 fcp/src/main.h create mode 100644 fcp/src/misc.c create mode 100644 fcp/src/misc.h create mode 100644 fcp/x86/Makefile (limited to 'fcp') 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 +# 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 + * Descr: copy & compare implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iforce != 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 + * Descr: erase implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Descr: list implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iuser.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 + * Descr: probe implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 + * Descr: read implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Descr: trunc implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Descr: user implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 [=] + + if (args->opt_nr <= 1) { + for (uint32_t word=0; wordopt_nr; i++) { + if (args->opt[i] == '\0') { + UNEXPECTED("invalid user '%s', use form " + "'[=]'\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 + * Descr: write implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Descr: cp for FFS files + * Date: 04/25/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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: " + "\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 [:][:] -PLETU" + "\n [-b ] [-o ] [-fpvdh]\n"); + fprintf(e," fcp [:][:] " + "[:][:] -RWCM" + "\n [-b ] [-o ] [-fpvdh]\n"); + fprintf(e, "\n"); + fprintf(e, " \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, " \n"); + fprintf(e, " 'aa' : USB device number [0..9]\n"); + fprintf(e, " 'rw' : @ RISCwatch probe\n"); + fprintf(e, " 'sfc' : to SFC char device\n"); + fprintf(e, " 'file' : to UNIX regular file\n"); + fprintf(e, " \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 \n"); + if (verbose) + fprintf(e, + "\n Fill the contents of a partition with . " + " 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 \n"); + if (verbose) + fprintf(e, + "\n Truncate the actual size of partition(s) to " + " size bytes. 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 [[=] ...]\n"); + if (verbose) + fprintf(e, + "\n Get or set a user word. and are " + "decimal (or hex) numbers.\n"); + + fprintf(e, "\n"); + + fprintf(e, "Options:\n"); + fprintf(e, " -o, --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 \n"); + if (verbose) + fprintf(e, + "\n Specifies the buffer size used by --read, --write," + " --copy and --compare\n commands 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 " + " (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 [:][:] -P + * list: + * fcp -L + * fcp [:][:] -L + * erase: + * fcp [:][:] -E + * trunc: + * fcp [:][:] -T + * user: + * fcp [:][:] -U [=] ... + * write: + * fcp [:]: -W + * read: + * fcp [:]: -R + * copy: + * fcp :]: [:]: -C + * fcp [:]: [:]: -C + * compare: + * fcp :]: [:]: -M + * fcp [:]: [:]: -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 [:]" + "[:] --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 [:]" + "[:] --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 [:]" + ": --read [--verbose] " + "[--force] [--protected] [--buffer ]\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 [:]" + ": --write [--verbose] " + "[--force] [--protected] [--buffer ]\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 [:]" + ": --erase [--verbose] " + "[--force] [--protected] [--buffer ]\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 [:]" + ": --trunc [--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 [:]" + ": --user [=short_name); + } + + REQ_FIELD(dst_name, user); + + UNSUP_OPT(buffer, user); + } else if (args->cmd == c_COPY) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + "[:] [:]" + "[:] --copy [--verbose] [--force] " + "[--protected] [--buffer ]\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 [:]" + "[:] [:]" + "[:] --compare [--verbose] [--force] " + "[--protected] [--buffer ]\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 + * Descr: cmdline tool for libffs.so + * Date: 05/12/2012 + */ + +#ifndef __FCP_H__ +#define __FCP_H__ + +#include + +#include + +#include + +#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 + * Descr: misc helpers + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) { // + if (asprintf(target, "%s", path) < 0) { + ERRNO(errno); + return -1; + } + } else if (delim1 == delim2) { // : + 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) { // :: + 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 + * Descr: Misc. helpers + * Date: 05/12/2012 + */ + +#ifndef __MISC__H__ +#define __MISC__H__ + +#include + +#include +#include + +#include + +#include + +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 -- cgit v1.2.1