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. --- ffs/.gitignore | 3 + ffs/Makefile | 10 + ffs/Rules.mk | 30 + ffs/Rules.test.mk | 25 + ffs/ffs.h | 148 +++++ ffs/libffs.h | 174 ++++++ ffs/libffs2.h | 332 ++++++++++ ffs/src/ffs-fsp.h | 59 ++ ffs/src/libffs.c | 1520 +++++++++++++++++++++++++++++++++++++++++++++ ffs/src/libffs2.c | 489 +++++++++++++++ ffs/test/Makefile | 8 + ffs/test/ffs_tool_test.sh | 244 ++++++++ ffs/test/libffs_test.sh | 248 ++++++++ ffs/test/test_libffs.c | 654 +++++++++++++++++++ ffs/test/test_libffs.h | 45 ++ ffs/x86/Makefile | 7 + 16 files changed, 3996 insertions(+) create mode 100644 ffs/.gitignore create mode 100644 ffs/Makefile create mode 100644 ffs/Rules.mk create mode 100644 ffs/Rules.test.mk create mode 100644 ffs/ffs.h create mode 100644 ffs/libffs.h create mode 100644 ffs/libffs2.h create mode 100644 ffs/src/ffs-fsp.h create mode 100644 ffs/src/libffs.c create mode 100644 ffs/src/libffs2.c create mode 100644 ffs/test/Makefile create mode 100755 ffs/test/ffs_tool_test.sh create mode 100755 ffs/test/libffs_test.sh create mode 100644 ffs/test/test_libffs.c create mode 100644 ffs/test/test_libffs.h create mode 100644 ffs/x86/Makefile (limited to 'ffs') diff --git a/ffs/.gitignore b/ffs/.gitignore new file mode 100644 index 0000000..3a1f0a1 --- /dev/null +++ b/ffs/.gitignore @@ -0,0 +1,3 @@ +*/ffs +*/fcp +test/test_libffs diff --git a/ffs/Makefile b/ffs/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/ffs/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/ffs/Rules.mk b/ffs/Rules.mk new file mode 100644 index 0000000..eb8b736 --- /dev/null +++ b/ffs/Rules.mk @@ -0,0 +1,30 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 +CFLAGS += -I$(DEPTH)/.. -I$(DEPTH)/boot/fsp2_ipl -iquote.. + +LDFLAGS += -L. -m32 + +NAME=libffs + +CLIB=$(DEPTH)/../clib/x86/libclib.a + +OBJS=$(NAME).o $(NAME)2.o + +TARGETS=$(NAME).so $(NAME).a + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +$(NAME).so: $(OBJS) + $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $^ + +$(NAME).a: $(OBJS) + $(AR) -r $@ $^ + +install: $(TARGETS) + $(INSTALL) libffs.so libffs.a $(FFS_INSTALL)/lib + $(INSTALL) ../ffs.h ../libffs.h ../libffs2.h $(FFS_INSTALL)/include + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/ffs/Rules.test.mk b/ffs/Rules.test.mk new file mode 100644 index 0000000..9b63533 --- /dev/null +++ b/ffs/Rules.test.mk @@ -0,0 +1,25 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/apps -iquote.. +LDFLAGS += -L. -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR) + +OBJS=test_libffs.o + +TARGETS=test_libffs + +vpath %.c ../test +vpath %.h .. + +all: $(TARGETS) + +#libffs.a: $(OBJS) +# $(AR) -r $@ $^ + +test_libffs: test_libffs.o ../ppc/libffs.a $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a + $(CC) $(LDFLAGS) -o $@ $^ -lpthread + +install: $(TARGETS) + $(INSTALL) ../test/test_libffs $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/libffs_test.sh $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/ffs_tool_test.sh $(TEST_LIBFFS_INSTALL) + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/ffs/ffs.h b/ffs/ffs.h new file mode 100644 index 0000000..3c113bc --- /dev/null +++ b/ffs/ffs.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * 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 + */ + +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure + * + * This header defines the layout for the FSP Flash Structure. + */ + +#ifndef __FFS_H__ +#define __FFS_H__ + +/* Pull in the correct header depending on what is being built */ +#if defined(__KERNEL__) +#include +#else +#include +#endif + +/* The version of this partition implementation */ +#define FFS_VERSION_1 1 + +/* Magic number for the partition header (ASCII 'PART') */ +#define FFS_MAGIC 0x50415254 + +/* The maximum length of the partition name */ +#define PART_NAME_MAX 15 + +/* + * Sizes of the data structures + */ +#define FFS_HDR_SIZE sizeof(struct ffs_hdr) +#define FFS_ENTRY_SIZE sizeof(struct ffs_entry) + +/* + * Sizes of the data structures w/o checksum + */ +#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t)) +#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t)) + +/* pid of logical partitions/containers */ +#define FFS_PID_TOPLEVEL 0xFFFFFFFF + +/* + * Type of image contained w/in partition + */ +enum type { + FFS_TYPE_DATA = 1, + FFS_TYPE_LOGICAL = 2, + FFS_TYPE_PARTITION = 3, +}; + +/* + * Flag bit definitions + */ +#define FFS_FLAGS_PROTECTED 0x0001 +#define FFS_FLAGS_U_BOOT_ENV 0x0002 + +/* + * Number of user data words + */ +#define FFS_USER_WORDS 16 + +/* + * Define layout of user.data in struct ffs_entry + */ +enum user_data { + USER_DATA_VOL = 0, + USER_DATA_SIZE = 1, + USER_DATA_CRC = 2, +}; + +/** + * struct ffs_entry - Partition entry + * + * @name: Opaque null terminated string + * @base: Starting offset of partition in flash (in hdr.block_size) + * @size: Partition size (in hdr.block_size) + * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel) + * @id: Partition entry ID [1..65536] + * @type: Describe type of partition + * @flags: Partition attributes (optional) + * @actual: Actual partition size (in bytes) + * @resvd: Reserved words for future use + * @user: User data (optional) + * @checksum: Partition entry checksum (includes all above) + */ +struct ffs_entry { + char name[PART_NAME_MAX + 1]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct { + uint32_t data[FFS_USER_WORDS]; + } user; + uint32_t checksum; +} __attribute__ ((packed)); + +/** + * struct ffs_hdr - FSP Flash Structure header + * + * @magic: Eye catcher/corruption detector + * @version: Version of the structure + * @size: Size of partition table (in block_size) + * @entry_size: Size of struct ffs_entry element (in bytes) + * @entry_count: Number of struct ffs_entry elements in @entries array + * @block_size: Size of block on device (in bytes) + * @block_count: Number of blocks on device + * @resvd: Reserved words for future use + * @checksum: Header checksum + * @entries: Pointer to array of partition entries + */ +struct ffs_hdr { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + uint32_t checksum; + struct ffs_entry entries[]; +} __attribute__ ((packed)); + +#endif /* __FFS_H__ */ diff --git a/ffs/libffs.h b/ffs/libffs.h new file mode 100644 index 0000000..9772cac --- /dev/null +++ b/ffs/libffs.h @@ -0,0 +1,174 @@ +/* + * 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 + */ + +#ifndef __LIBFFS_H__ +#define __LIBFFS_H__ + +#include +#include + +#include + +#include "ffs.h" + +typedef struct ffs_entry ffs_entry_t; +typedef struct ffs_hdr ffs_hdr_t; +typedef enum type ffs_type_t; + +#define FFS_EXCEPTION_DATA 1024 + +/* ============================================================ */ + +/*! + * @brief ffs I/O interface + */ +struct ffs { + ffs_hdr_t * hdr; + + char * path; + off_t offset; + + FILE * file; + uint32_t count; + + void * buf; // FILE* buffer + uint32_t buf_count; // multiple of hdr->block_size + + bool dirty; +}; + +typedef struct ffs ffs_t; + +struct ffs_exception { + int rc; + char data[FFS_EXCEPTION_DATA]; +}; + +typedef struct ffs_exception ffs_exception_t; + +#define FFS_PARTITION_NAME "part" + +#define FFS_INFO_ERROR 0 +#define FFS_INFO_MAGIC 1 +#define FFS_INFO_VERSION 2 +#define FFS_INFO_ENTRY_SIZE 3 +#define FFS_INFO_ENTRY_COUNT 4 +#define FFS_INFO_BLOCK_SIZE 5 +#define FFS_INFO_BLOCK_COUNT 6 +#define FFS_INFO_BUFFER_COUNT 7 +#define FFS_INFO_OFFSET 8 + +#define FFS_CHECK_PATH -3 +#define FFS_CHECK_HEADER_MAGIC -4 +#define FFS_CHECK_HEADER_CHECKSUM -5 +#define FFS_CHECK_ENTRY_CHECKSUM -6 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __ffs_fcheck(FILE *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_check(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_fcreate(FILE *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_create(const char *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_fopen(FILE *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_open(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_info(ffs_t *, int, uint32_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_buffer(ffs_t *, size_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_close(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_fclose(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_fsync(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_list_entries(ffs_t *, const char *, bool, FILE *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*)) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_find(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_name(ffs_t *, ffs_entry_t *, char *, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern int __ffs_entry_add(ffs_t *, const char *, off_t, + size_t, ffs_type_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_delete(ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *) +/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ; + +extern int __ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_hexdump(ffs_t *, const char *, FILE *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_truncate(ffs_t *, const char *, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_write(ffs_t *, const char *, const void *, + off_t, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_copy(ffs_t *, ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_compare(ffs_t *, ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern int __ffs_entry_list(ffs_t *, ffs_entry_t ** list) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +#ifdef __cplusplus +} +#endif + +/* ============================================================ */ + +#endif /* __LIBFFS_H__ */ diff --git a/ffs/libffs2.h b/ffs/libffs2.h new file mode 100644 index 0000000..79bc856 --- /dev/null +++ b/ffs/libffs2.h @@ -0,0 +1,332 @@ +/* + * 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 libffs2.h + * @brief FSP Flash File Structure API + * @details This library provides an API to the FFS partitioning scheme. + * @author Shaun Wetzstein + * @date 2010-2012 + */ + +#ifndef __LIBFFS2_H__ +#define __LIBFFS2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libffs.h" + +/* FFS_OPEN_* are deprecated, use FFS_CHECK_* instead */ +#define FFS_OPEN_INVALID_PATH FFS_CHECK_PATH +#define FFS_OPEN_FOPEN_FAILURE -1 +#define FFS_OPEN_MAGIC_CHECK FFS_CHECK_HEADER_MAGIC +#define FFS_OPEN_CRC_CHECK FFS_CHECK_HEADER_CHECKSUM + +/*! + * @brief Clears the (global) FFS error indicator + * @memberof ffs + * @return None + */ +extern void ffs_errclr(void); + +/*! + * @brief Return the error number of an FFS error + * @memberof ffs + * @return non-0 on success, '0' if no pending error + */ +extern int ffs_errnum(void); + +/*! + * @brief Return the error string of an FFS error + * @memberof ffs + * @return non-NULL on success, 'NULL' if no pending error + */ +extern const char * ffs_errstr(void); + +/*! + * @brief Open the file named 'path' and check the @em FFS partition table + * at 'offset' bytes from the beginning of the file (or device). + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from the beginning of the file (or device), + * of the ffs_hdr_t structure + * @return 0 on success, non-0 otherwise + * FFS_CHECK_PATH if path is NULL or "" + * FFS_CHECK_HEADER_MAGIC for header magic corruption + * FFS_CHECK_HEADER_CHECKSUM for header data corruption + * FFS_CHECK_ENTRY_CHECKSUM for [an] entry data corruption + */ +extern int ffs_check(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Create an empty @em FFS partition table and store it at 'offset' + * bytes from the beginning of the file (or device) named 'path' + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from beginning of file (or device), + * of the ffs_hdr_t structure + * @param block_size [in] Block size in bytes + * @param block_count [in] Number of blocks in the device size + * @return Pointer to ffs_t (allocated on the heap) on success, + * NULL otherwise + */ +extern ffs_t * ffs_create(const char *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Open the file name 'path' and read the @em FFS partition table + * at 'offset' bytes from the beggining of the file (or device). + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from beginning of file (or device), + * of the ffs_hdr_t structure + * @return Pointer to ffs_t (allocated on the heap) on success, + * NULL otherwise + */ +extern ffs_t * ffs_open(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Query a @em FFS object for header metadata. + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param name [in] Field number, see ffs.h for details + * FFS_INFO_MAGIC - ffs_hdr::magic + * FFS_INFO_VERSION - ffs_hdr::version + * FFS_INFO_ENTRY_SIZE - ffs_hdr::entry_size + * FFS_INFO_ENTRY_COUNT - ffs_hdr::entry_count + * FFS_INFO_BLOCK_SIZE - ffs_hdr::block_size + * FFS_INFO_BLOCK_COUNT - ffs_hdr::block_count + * @param value [out] Pointer to output data + * @return '0' on success, non-0 otherwise + */ +extern int ffs_info(ffs_t *, int, uint32_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Close a @em FFS object, after writting any modified in-core + * data to the file (or device). + * @memberof ffs + * @param self [in] Pointer to ffs object + * @return '0' on success, non-0 otherwise + */ +extern int ffs_close(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Flush all modified in-core metadata of a @em FFS object, + * to the underlying file (or device). + * @memberof ffs + * @param self [in] Pointer to ffs object + * @return '0' on success, non-0 otherwise + */ +extern int ffs_fsync(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Pretty print the entries of a @em FFS partition table to + * stream 'out' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param out [in] Pointer the output stream + * @return '0' on success, non-0 otherwise + */ +extern int ffs_list_entries(ffs_t *, FILE *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Iterate over the entries of a @em FFS partition table and + * call a callback function 'func' + * @note If the callback function returns non-0, the iteration function + * will return immediately. + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param func [in] Pointer the callback function + * @return '0' on success, non-0 otherwise + */ +extern int ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*)) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Find an entry in a @em FFS partition table and return + * a copy of the in 'entry' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param entry [out] Target entry object + * @return '1' == found, '0' == not-found, error otherwise + */ +extern int ffs_entry_find(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Find the parent entry in a @em FFS partition table and + * return a copy of the in 'entry' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param parent [out] Target entry object + * @return '1' == found, '0' == not-found, error otherwise + */ +extern int ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Add a partition entry to a @em FFS partition table + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param offset [in] Offset, in blocks, of the partition entry, from + * the beginning of the file (or device) + * @param size [in] Size, in blocks, of the partition entry + * @param type [in] Partition type. FFS_TYPE_LOGICAL can be used to + * to create a container for a set of partitions. A logical partition + * can be thought of as a directory. Use FFS_TYPE_DATA for data + * partitions. + * @param flags [in] Partition flags. FFS_FLAG_PROTECTED can be used to + * protect a partition from inadvertant updates. The ffs related tools + * should *not* overwrite protected partitions unless a --protected + * flag is specified. + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_add(ffs_t *, const char *, off_t, size_t, ffs_type_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Delete a partition entry from the @em FFS partition table + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param path [in] Name of a partition entry + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_delete(ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Get the value of a meta-data user word + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param user [in] User word number, in the range [0..FFS_USER_WORDS] + * @param word [in] User word value, in the range [0..UINT32_MAX] (optional) + * @note 'word' is optional, if omitted, the current value is returned + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *) +/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ; + +/*! + * @brief Set the value of a meta-data user word + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param user [in] User word number, in the range [0..FFS_USER_WORDS] + * @param word [in] User word value, in the range [0..UINT32_MAX] (optional) + * @note 'word' is optional, if omitted, the current value is returned + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Hexdump the data contents of a partition entry to output stream + * 'out' + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param out [in] Output stream + * @return Negative on success, number of bytes written otherwise + */ +extern ssize_t ffs_entry_hexdump(ffs_t *, const char *, FILE *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +/*! + * @brief Change the actual size of partition entry 'name' + * to 'offset' bytes from the beginning of the entry. + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param offset [in] Offset from the beginning of the partition (in bytes) + * @return Negative on failure, zero otherwise + */ +extern ssize_t ffs_entry_truncate_no_pad(ffs_t *, const char *, off_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Change the actual size of partition entry 'name' + * to 'offset' bytes from the beginning of the entry. + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param offset [in] Offset from the beginning of the partition (in bytes) + * @param pad [in] Pad character + * @return Negative on failure, zero otherwise + */ +extern ssize_t ffs_entry_truncate(ffs_t *, const char *, off_t, uint8_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Read 'count' data bytes of partition entry 'name' to into 'buf' at + * offset 'offset' bytes from the beginning of the entry + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param buf [out] Output data buffer + * @param offset [in] Offset from the beginning of the partition + * @param count [in] Number of bytes to read + * @return Negative on failure, number of bytes read otherwise + */ +extern ssize_t ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Write 'count' data bytes to partition entry 'name' at offset + * 'offset' bytes from the beginning of the entry, from input + * buffer 'buf' + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param buf [in] Input data buffer + * @param offset [in] Offset from the beginning of the partition + * @param count [in] Number of bytes to write + * @return Negative on failure, else number of bytes written otherwise + */ +extern ssize_t ffs_entry_write(ffs_t *, const char *, const void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Return an array of entry_t structures, one each partition that + * exists in the partition table + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param list [out] Array of entry_t structures allocated on the heap + * @return Negative on failure, else number of entry_t's in the output + * array with a call to free() + * @note Caller is responsible for freeing array with a call to free() + */ +extern ssize_t ffs_entry_list(ffs_t *, ffs_entry_t **) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBFFS2_H__ */ diff --git a/ffs/src/ffs-fsp.h b/ffs/src/ffs-fsp.h new file mode 100644 index 0000000..f1b3374 --- /dev/null +++ b/ffs/src/ffs-fsp.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * 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 + */ + +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure, Boot specific adaptations + */ + +#ifndef __FFS_BOOT_H__ +#define __FFS_BOOT_H__ + +#include "ffs.h" + +/* + * Values to use in USER_DATA_VOL + * + * @note: Side 0/1 must be defined as even/odd values. Code in the IPL depends + * on this to be able to use the appropriate volume based on the boot bank. + */ +enum vol { + FFS_VOL_IPL0 = 0, + FFS_VOL_IPL1 = 1, + FFS_VOL_SPL0 = 2, + FFS_VOL_SPL1 = 3, + FFS_VOL_BOOTENV0 = 4, + FFS_VOL_BOOTENV1 = 5, + FFS_VOL_KERNEL0 = 6, + FFS_VOL_KERNEL1 = 7, + FFS_VOL_INITRAMFS0 = 8, + FFS_VOL_INITRAMFS1 = 9, + FFS_VOL_DTB0 = 10, + FFS_VOL_DTB1 = 11, + FFS_VOL_SERIES0 = 12, + FFS_VOL_SERIES1 = 13, + FFS_VOL_CARD0 = 14, + FFS_VOL_CARD1 = 15, + FFS_VOL_DUMP0 = 16, + FFS_VOL_DUMP1 = 17, + FFS_VOL_DUMP2 = 18, + FFS_VOL_DUMP3 = 19, +}; + +#endif /* __FFS_BOOT_H__ */ diff --git a/ffs/src/libffs.c b/ffs/src/libffs.c new file mode 100644 index 0000000..b912ede --- /dev/null +++ b/ffs/src/libffs.c @@ -0,0 +1,1520 @@ +/* + * 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: libffs.c + * Author: Shaun Wetzstein + * Descr: FFS IO interface + * Note: + * Date: 05/07/12 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libffs.h" + +#include +#include +#include +#include +#include +#include + +#ifndef be32toh +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define be32toh(x) __bswap_32(x) +#define htobe32(x) __bswap_32(x) +#else +#define be32toh(x) (x) +#define htobe32(x) (x) +#endif +#endif + +#define FFS_ENTRY_EXTENT 10UL + +/* ============================================================ */ + +static void __hdr_be32toh(ffs_hdr_t * hdr) +{ + assert(hdr != NULL); + + hdr->magic = be32toh(hdr->magic); + hdr->version = be32toh(hdr->version); + hdr->size = be32toh(hdr->size); + hdr->entry_size = be32toh(hdr->entry_size); + hdr->entry_count = be32toh(hdr->entry_count); + hdr->block_size = be32toh(hdr->block_size); + hdr->block_count = be32toh(hdr->block_count); + hdr->checksum = be32toh(hdr->checksum); +} + +static void __hdr_htobe32(ffs_hdr_t * hdr) +{ + assert(hdr != NULL); + + hdr->magic = htobe32(hdr->magic); + hdr->version = htobe32(hdr->version); + hdr->size = htobe32(hdr->size); + hdr->entry_size = htobe32(hdr->entry_size); + hdr->entry_count = htobe32(hdr->entry_count); + hdr->block_size = htobe32(hdr->block_size); + hdr->block_count = htobe32(hdr->block_count); + hdr->checksum = htobe32(hdr->checksum); +} + +static void __entry_be32toh(ffs_entry_t * entry) +{ + assert(entry != NULL); + + entry->base = be32toh(entry->base); + entry->size = be32toh(entry->size); + entry->pid = be32toh(entry->pid); + entry->id = be32toh(entry->id); + entry->type = be32toh(entry->type); + entry->flags = be32toh(entry->flags); + entry->actual = be32toh(entry->actual); + entry->checksum = be32toh(entry->checksum); + + entry->resvd[0] = be32toh(entry->resvd[0]); + entry->resvd[1] = be32toh(entry->resvd[1]); + entry->resvd[2] = be32toh(entry->resvd[2]); + entry->resvd[3] = be32toh(entry->resvd[3]); + + for (int j = 0; j < FFS_USER_WORDS; j++) + entry->user.data[j] = be32toh(entry->user.data[j]); +} + +static void __entry_htobe32(ffs_entry_t * entry) +{ + assert(entry != NULL); + + entry->base = htobe32(entry->base); + entry->size = htobe32(entry->size); + entry->pid = htobe32(entry->pid); + entry->id = htobe32(entry->id); + entry->type = htobe32(entry->type); + entry->flags = htobe32(entry->flags); + entry->actual = htobe32(entry->actual); + entry->checksum = htobe32(entry->checksum); + + entry->resvd[0] = htobe32(entry->resvd[0]); + entry->resvd[1] = htobe32(entry->resvd[1]); + entry->resvd[2] = htobe32(entry->resvd[2]); + entry->resvd[3] = htobe32(entry->resvd[3]); + + for (int j = 0; j < FFS_USER_WORDS; j++) + entry->user.data[j] = htobe32(entry->user.data[j]); +} + +static int __hdr_read(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t rc = fread(hdr, 1, sizeof(*hdr), file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + uint32_t ck = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + + __hdr_be32toh(hdr); + + if (hdr->magic != FFS_MAGIC) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC, + "magic number mismatch '%x' != '%x'", + hdr->magic, FFS_MAGIC); + return -1; + } + + if (hdr->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM, + "header checksum mismatch '%x' != '%x'", + hdr->checksum, ck); + return -1; + } + + return 0; +} + +static int __hdr_write(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + assert(hdr->magic == FFS_MAGIC); + + hdr->checksum = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + hdr->checksum = htobe32(hdr->checksum); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t size = sizeof(*hdr); + + if (0 < hdr->entry_count) { + size += hdr->entry_count * hdr->entry_size; + + for (size_t i=0; ientry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + __entry_htobe32(e); + + e->checksum = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + e->checksum = htobe32(e->checksum); + } + } + + __hdr_htobe32(hdr); + + size_t rc = fwrite(hdr, 1, size, file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + __hdr_be32toh(hdr); + if (0 < hdr->entry_count) + for (size_t i=0; ientry_count; i++) + __entry_be32toh(hdr->entries + i); + + return 0; +} + +static int __entries_read(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + assert(hdr->magic == FFS_MAGIC); + + if (0 < hdr->entry_count) { + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t size = hdr->entry_count * hdr->entry_size; + + size_t rc = fread(hdr->entries, 1, size, file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + for (size_t i=0; ientry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + uint32_t ck = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + __entry_be32toh(e); + + if (e->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM, + "'%s' entry checksum mismatch '%x' != " + "'%x'", e->name, e->checksum, ck); + return -1; + } + } + } + + return 0; +} + +#if 0 +static void __entries_write(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + if (unlikely(hdr == NULL)) + ffs_throw(UNEX, 10400, "NULL hdr pointer"); + if (hdr->magic != FFS_MAGIC) + ffs_throw(UNEX, 10401, "magic number mismatch '%x' != " + "'%x'", hdr->magic, FFS_MAGIC); + + if (0 < hdr->entry_count) { + size_t size = hdr->entry_count * hdr->entry_size; + + for (size_t i = 0; i < hdr->entry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + __entry_htobe32(e); + e->checksum = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + e->checksum = htobe32(e->checksum); + } + + if (fseeko(file, offset, SEEK_SET) != 0) + ffs_throw(ERR, 10402, "%s (errno=%d)", + strerror(errno), errno); + + size_t rc = fwrite(hdr->entries, 1, size, file); + if (rc <= 0 && ferror(file)) + ffs_throw(ERR, 10403, "%s (errno=%d)", + strerror(errno), errno); + + fflush(file); + + for (size_t i = 0; i < hdr->entry_count; i++) + __entry_be32toh(hdr->entries + i); + } +} +#endif + +static ffs_entry_t *__iterate_entries(ffs_hdr_t * self, + int (*func) (ffs_entry_t *)) +{ + assert(self != NULL); + assert(func != NULL); + + for (uint32_t i = 0; i < self->entry_count; i++) { + if (func(self->entries + i) != 0) + return self->entries + i; + } + + return NULL; +} + +static ffs_entry_t *__find_entry(ffs_hdr_t * self, const char *path) +{ + assert(self != NULL); + + if (path == NULL || *path == '\0') + return NULL; + + if (*path == '/') + path++; + + char __path[strlen(path) + 1]; + strcpy(__path, path); + path = __path; + + ffs_entry_t root = {.id = FFS_PID_TOPLEVEL }, *parent = &root; + + char *name; + while (parent != NULL && (name = strtok((char *)path, "/")) != NULL) { + path = NULL; + + int find_entry(ffs_entry_t * child) { + return (parent->id == child->pid && + strncmp(name, child->name, + sizeof(child->name)) == 0); + } + + parent = __iterate_entries(self, find_entry); + } + + return parent; +} + +/* ============================================================ */ + +int __ffs_fcheck(FILE *file, off_t offset) +{ + assert(file != NULL); + + RAII(ffs_hdr_t*, hdr, malloc(sizeof(*hdr)), free); + if (hdr == NULL) { + ERRNO(errno); + return -1; + } + memset(hdr, 0, sizeof(*hdr)); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fread(hdr, 1, sizeof(*hdr), file) <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + uint32_t ck = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + __hdr_be32toh(hdr); + + if (hdr->magic != FFS_MAGIC) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC, + "header magic mismatch '%x' != '%x'", + hdr->magic, FFS_MAGIC); + return FFS_CHECK_HEADER_MAGIC; + } + if (hdr->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM, + "header checksum mismatch '%x' != '%x'", + hdr->checksum, ck); + return FFS_CHECK_HEADER_CHECKSUM; + } + + size_t size = hdr->entry_count * hdr->entry_size; + + hdr = (ffs_hdr_t *)realloc(hdr, sizeof(*hdr) + size); + if (hdr == NULL) { + ERRNO(errno); + return -1; + } + memset(hdr->entries, 0, size); + + if (0 < hdr->entry_count) { + if (fread(hdr->entries, 1, size, file) <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + for (size_t i = 0; i < hdr->entry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + uint32_t ck = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + + __entry_be32toh(e); + + if (e->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM, + "'%s' entry checksum mismatch '%x' != " + "'%x'", e->name, e->checksum, ck); + return FFS_CHECK_ENTRY_CHECKSUM; + } + } + } + + return 0; +} + +int __ffs_check(const char *path, off_t offset) +{ + if (path == NULL || *path == '\0') { + UNEXPECTED("invalid path '%s'\n", path); + return -1; + } + + RAII(FILE*, file, fopen(path, "r"), fclose); + if (file == NULL) { + ERRNO(errno); + return -1; + } + + return __ffs_fcheck(file, offset); +} + +ffs_t *__ffs_fcreate(FILE *file, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + assert(file != NULL); + + if (!is_pow2(block_size)) { + UNEXPECTED("'%d' invalid block size (must be non-0 and a " + "power of 2)", block_size); + return NULL; + } + if (!is_pow2(block_count)) { + UNEXPECTED("'%d' invalid block count (must be non-0 and a " + "power of 2)", block_count); + return NULL; + } + if (offset & (block_size - 1)) { + UNEXPECTED("'%lld' invalid offset (must be 'block size' " + "aligned)", offset); + return NULL; + } + + ffs_t *self = (ffs_t *) malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + goto error; + } + + memset(self, 0, sizeof(*self)); + self->file = file; + self->offset = offset; + self->count = FFS_ENTRY_EXTENT; + self->dirty = true; + + self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr)); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + + self->hdr->magic = FFS_MAGIC; + self->hdr->version = FFS_VERSION_1; + self->hdr->size = 1; + self->hdr->entry_size = sizeof(*self->hdr->entries); + self->hdr->entry_count = 0; + self->hdr->block_size = block_size; + self->hdr->block_count = block_count; + self->hdr->checksum = 0; + + size_t size = self->count * self->hdr->entry_size; + + self->hdr = (ffs_hdr_t *) realloc(self->hdr, sizeof(*self->hdr) + size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr->entries, 0, size); + + if (__ffs_entry_add(self, FFS_PARTITION_NAME, offset, block_size, + FFS_TYPE_PARTITION, FFS_FLAGS_PROTECTED) < 0) + goto error; + if (__ffs_entry_truncate(self, FFS_PARTITION_NAME, block_size) < 0) + goto error; + + if (false) { + error: + if (self != NULL) { + if (self->file != NULL) + fclose(self->file), self->file = NULL; + if (self->path != NULL) + free(self->path), self->path = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + free(self), self = NULL; + } + } + + return self; +} + +ffs_t *__ffs_create(const char *path, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + assert(path != NULL); + + FILE * file = fopen(path, "r+"); + if (file == NULL) { + ERRNO(errno); + return NULL; + } + + ffs_t * self = __ffs_fcreate(file, offset, block_size, block_count); + if (self != NULL) + self->path = strdup(path); + + return self; +} + +ffs_t *__ffs_fopen(FILE * file, off_t offset) +{ + assert(file != NULL); + + ffs_t *self = (ffs_t *) malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + goto error; + } + + memset(self, 0, sizeof(*self)); + self->file = file; + self->count = 0; + self->offset = offset; + self->dirty = false; + + self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr)); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr, 0, sizeof(*self->hdr)); + + if (__hdr_read(self->hdr, self->file, self->offset) < 0) + goto error; + + self->count = max(self->hdr->entry_count, FFS_ENTRY_EXTENT); + size_t size = self->count * self->hdr->entry_size; + + self->hdr = (ffs_hdr_t *)realloc(self->hdr, sizeof(*self->hdr) + size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr->entries, 0, size); + + if (0 < self->hdr->entry_count) { + if (__entries_read(self->hdr, self->file, + self->offset + sizeof(*self->hdr)) < 0) + goto error; + } + + self->buf_count = 1; // default to 1 + + self->buf = malloc(self->buf_count * self->hdr->block_size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + + if (setvbuf(self->file, self->buf, _IOFBF, + self->buf_count * self->hdr->block_size) != 0) { + ERRNO(errno); + goto error; + } + + if (false) { + error: + if (self != NULL) { + if (self->buf != NULL) + free(self->buf), self->buf = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + + free(self), self = NULL; + } + } + + return self; +} + +ffs_t *__ffs_open(const char *path, off_t offset) +{ + assert(path != NULL); + + FILE *file = fopen(path, "r+"); + if (file == NULL) { + ERRNO(errno); + return NULL; + } + + ffs_t *self = __ffs_fopen(file, offset); + if (self != NULL) + self->path = strdup(path); + + return self; +} + +static int ffs_flush(ffs_t * self) +{ + assert(self != NULL); + + if (__hdr_write(self->hdr, self->file, self->offset) < 0) + return -1; + + if (fflush(self->file) != 0) { + ERRNO(errno); + return -1; + } + + if (fileno(self->file) != -1) { + if (fsync(fileno(self->file)) < 0) { + ERRNO(errno); + return -1; + } + } + + + self->dirty = false; + + return 0; +} + +int __ffs_info(ffs_t * self, int name, uint32_t *value) +{ + assert(self != NULL); + assert(value != NULL); + + switch (name) { + case FFS_INFO_MAGIC: + *value = self->hdr->magic; + break; + case FFS_INFO_VERSION: + *value = self->hdr->version; + break; + case FFS_INFO_ENTRY_SIZE: + *value = self->hdr->entry_size; + break; + case FFS_INFO_ENTRY_COUNT: + *value = self->hdr->entry_count; + break; + case FFS_INFO_BLOCK_SIZE: + *value = self->hdr->block_size; + break; + case FFS_INFO_BLOCK_COUNT: + *value = self->hdr->block_count; + break; + case FFS_INFO_BUFFER_COUNT: + *value = self->buf_count; + break; + case FFS_INFO_OFFSET: + *value = self->offset; + break; + default: + UNEXPECTED("'%d' invalid info field", name); + return -1; + } + + return 0; +} + +int __ffs_buffer(ffs_t * self, size_t size) +{ + assert(self != NULL); + + if (size == 0) + size = self->hdr->block_size; + + if (self->buf != NULL) { + free(self->buf); + self->buf_count = 0; + } + + self->buf_count = size / self->hdr->block_size; + size = self->buf_count * self->hdr->block_size; + + self->buf = malloc(size); + if (self->buf == NULL) { + ERRNO(errno); + return -1; + } + + if (setvbuf(self->file, self->buf, _IOFBF, size) < 0) { + ERRNO(errno); + return -1; + } + + return 0; +} + +int __ffs_fclose(ffs_t * self) +{ + if (self == NULL) + return 0; + + if (self->dirty == true) + if (ffs_flush(self) < 0) + return -1; + + if (self->buf != NULL) + free(self->buf), self->buf = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + + memset(self, 0, sizeof(*self)); + free(self); + + return 0; +} + +int __ffs_close(ffs_t * self) +{ + if (unlikely(self == NULL)) + return 0; + + if (self->dirty == true) + if (ffs_flush(self) < 0) + return -1; + + if (self->path != NULL) + free(self->path), self->path = NULL; + if (self->file != NULL) + fclose(self->file), self->file = NULL; + + return __ffs_fclose(self); +} + +int __ffs_fsync(ffs_t * self) +{ + assert(self != NULL); + + if (fflush(self->file) < 0) { + ERRNO(errno); + return -1; + } + + if (fileno(self->file) != -1) + if (fsync(fileno(self->file)) < 0) + return -1; + + return 0; +} + +static ffs_entry_t *__add_entry_check(ffs_hdr_t * self, off_t offset, + size_t size) +{ + assert(self != NULL); + + int find_overlap(ffs_entry_t * entry) { + if (entry->type == FFS_TYPE_LOGICAL) + return 0; + + off_t entry_start = entry->base; + off_t entry_end = entry_start + entry->size - 1; + + off_t new_start = offset / self->block_size; + off_t new_end = new_start + (size / self->block_size) - 1; + + return !(new_start < entry_start && new_end < entry_start) && + !(entry_end < new_start && entry_end < new_end); + } + + return __iterate_entries(self, find_overlap); +} + +int __ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *)) +{ + return __iterate_entries(self->hdr, func) != NULL; +} + +int __ffs_list_entries(ffs_t * self, const char * name, bool user, FILE * out) +{ + assert(self != NULL); + + if (out == NULL) + out = stdout; + + char full_name[4096]; + regex_t rx; + + int print_entry(ffs_entry_t * entry) + { + size_t offset = entry->base * self->hdr->block_size; + size_t size = entry->size * self->hdr->block_size; + + if (__ffs_entry_name(self, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + fprintf(stdout, "%3d [%08x-%08x:%8x] " + "[%c%c%c%c%c%c%c%c%c%c] %s\n", + entry->id, offset, offset+size-1, entry->actual, + entry->type == FFS_TYPE_LOGICAL ? 'l' : 'd', + /* reserved */ '-', '-', '-', '-', '-', '-', '-', + entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-', + entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-', + full_name); + + if (user == true) { + for (int i=0; iuser.data[i]); + if ((i+1) % 4 == 0) + fprintf(stdout, "\n"); + } + } + + return 0; + } + + if (0 < self->count) { + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return-1; + } + + fprintf(out, "========================[ PARTITION TABLE 0x%llx " + "]=======================\n", self->offset); + fprintf(out, "vers:%04x size:%04x * blk:%06x blk(s):%06x * " + "entsz:%06x ent(s):%06x\n", + self->hdr->version, self->hdr->size, + self->hdr->block_size, self->hdr->block_count, + self->hdr->entry_size, self->hdr->entry_count); + fprintf(out, "------------------------------------------------" + "---------------------------\n"); + + (void)__iterate_entries(self->hdr, print_entry); + + fprintf(stdout, "\n"); + + regfree(&rx); + } + + return 0; +} + +int __ffs_entry_find(ffs_t *self, const char *path, ffs_entry_t *entry) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t *__entry = __find_entry(self->hdr, path); + if (__entry != NULL && entry != NULL) + *entry = *__entry; + + return __entry != NULL; +} + +int __ffs_entry_find_parent(ffs_t *self, const char *path, ffs_entry_t *entry) +{ + assert(self != NULL); + assert(path != NULL); + + if (*path == '/') + path++; + + char __path[strlen(path) + 1]; + strcpy(__path, path); + char *parent_path = dirname(__path); + + int found = 0; + + if (strcmp(parent_path, ".") != 0) { + ffs_entry_t parent; + + found = __ffs_entry_find(self, parent_path, &parent); + + if (found && entry != NULL) + *entry = parent; + } + + return found; +} + +int __ffs_entry_name(ffs_t *self, ffs_entry_t *entry, char *name, size_t size) +{ + assert(self != NULL); + assert(entry != NULL); + + ffs_hdr_t *hdr = self->hdr; + + int __entry_name(ffs_entry_t *parent, char *name, size_t size) { + assert(parent != NULL); + assert(name != NULL); + + if (parent->pid != FFS_PID_TOPLEVEL) { + for (uint32_t i = 0; i < hdr->entry_count; i++) { + if (hdr->entries[i].id == parent->pid) { + __entry_name(hdr->entries + i, name, + size); + break; + } + } + } + + if (strlen(name) + strlen(parent->name) < size) + strcat(name, parent->name); + + if (parent->id != entry->id) { + if (strlen(name) + strlen("/") < size) + strcat(name, "/"); + } + + return 0; + } + + memset(name, 0, size); + + return __entry_name(entry, name, size); +} + +int __ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size, + ffs_type_t type, uint32_t flags) +{ + assert(self != NULL); + assert(path != NULL); + + if (__ffs_entry_find(self, path, NULL) == true) { + UNEXPECTED("'%s' entry already exists", path); + return -1; + } + + ffs_entry_t parent = {.id = FFS_PID_TOPLEVEL }; + (void)__ffs_entry_find_parent(self, path, &parent); + + ffs_hdr_t *hdr = self->hdr; + + if (type != FFS_TYPE_LOGICAL) { + ffs_entry_t *overlap = __add_entry_check(hdr, offset, size); + if (overlap != NULL) { + UNEXPECTED("'%s' at offset %lld and size %d overlaps " + "'%s' at offset %d and size %d", + path, offset, size, overlap->name, + overlap->base * hdr->block_size, + overlap->size * hdr->block_size); + return -1; + } + } + + int find_empty(ffs_entry_t * empty) { + return empty->type == 0; + } + + ffs_entry_t *entry = __iterate_entries(hdr, find_empty); + if (entry == NULL) { + if (self->count <= hdr->entry_count) { + size_t new_size; + new_size = hdr->entry_size * + (self->count + FFS_ENTRY_EXTENT); + + self->hdr = (ffs_hdr_t *) realloc(self->hdr, + sizeof(*self->hdr) + + new_size); + assert(self->hdr != NULL); + + if (hdr != self->hdr) + hdr = self->hdr; + + memset(hdr->entries + self->count, 0, + FFS_ENTRY_EXTENT * hdr->entry_size); + + self->count += FFS_ENTRY_EXTENT; + } + + entry = hdr->entries + hdr->entry_count; + } + + uint32_t max_id = 0; + + int find_max_id(ffs_entry_t * max) { + if (max_id < max->id) + max_id = max->id; + return 0; + } + + (void)__iterate_entries(hdr, find_max_id); + + char name[strlen(path) + 1]; + strcpy(name, path); + strncpy(entry->name, basename(name), sizeof(entry->name)); + entry->id = max_id + 1; + entry->pid = parent.id; + entry->base = offset / hdr->block_size; + entry->size = size / hdr->block_size; + entry->type = type; + entry->flags = flags; + entry->checksum = 0; + + hdr->entry_count++; + + self->dirty = true; + + return 0; +} + +int __ffs_entry_delete(ffs_t * self, const char *path) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (entry.type == FFS_TYPE_PARTITION) { + UNEXPECTED("'%s' cannot --delete partition type entries", path); + return -1; + } + + uint32_t children = 0; + + int find_children(ffs_entry_t * child) { + if (entry.id == child->pid) + children++; + return 0; + } + + ffs_hdr_t *hdr = self->hdr; + + (void)__iterate_entries(hdr, find_children); + + if (0 < children) { + UNEXPECTED("'%s' has '%d' children, --delete those first", + path, children); + return -1; + } + + int find_entry_id(ffs_entry_t * __entry) { + return entry.id == __entry->id; + } + + ffs_entry_t *entry_p = __iterate_entries(hdr, find_entry_id); + assert(entry_p != NULL); + + int start = entry_p - hdr->entries; + int count = hdr->entry_count - start; + + memmove(entry_p, entry_p + 1, hdr->entry_size * count); + + hdr->entry_count = max(0UL, hdr->entry_count - 1); + memset(hdr->entries + hdr->entry_count, 0, hdr->entry_size); + + self->dirty = true; + + return 0; +} + +int __ffs_entry_user_get(ffs_t *self, const char *path, uint32_t word, + uint32_t *value) +{ + assert(self != NULL); + assert(path != NULL); + assert(value != NULL); + + if (FFS_USER_WORDS <= word) { + UNEXPECTED("word '%d' outside range [0..%d]", + word, FFS_USER_WORDS - 1); + return -1; + } + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + *value = entry->user.data[word]; + + return 0; +} + +int __ffs_entry_user_put(ffs_t *self, const char *path, uint32_t word, + uint32_t value) +{ + assert(self != NULL); + assert(path != NULL); + + if (FFS_USER_WORDS <= word) { + UNEXPECTED("word '%d' outside range [0..%d]", + word, FFS_USER_WORDS - 1); + return -1; + } + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + entry->user.data[word] = value; + self->dirty = true; + + return 0; +} + +ssize_t __ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + size_t size = entry.size * self->hdr->block_size; + if (entry.actual < size) + size = entry.actual; + + off_t offset = entry.base * self->hdr->block_size; + + if (fseeko(self->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + + size_t block_size = self->hdr->block_size; + char block[block_size]; + while (0 < size) { + size_t rc = fread(block, 1, min(block_size, size), + self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + + dump_memory(out, offset + total, block, rc); + + total += rc; + size -= rc; + } + + return total; +} + +ssize_t __ffs_entry_truncate(ffs_t * self, const char *path, size_t size) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t * entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + if ((entry->size * self->hdr->block_size) < size) { + errno = EFBIG; + ERRNO(errno); + return -1; + } else { + entry->actual = size; + self->dirty = true; + } + + return 0; +} + +ssize_t __ffs_entry_read(ffs_t * self, const char *path, void *buf, + off_t offset, size_t count) +{ + assert(self != NULL); + assert(path != NULL); + assert(buf != NULL); + + if (count == 0) + return 0; + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + size_t entry_size = entry.size * self->hdr->block_size; + if (entry.actual < entry_size) + entry_size = entry.actual; + off_t entry_offset = entry.base * self->hdr->block_size; + + if (entry_size <= offset) + return 0; + else + count = min(count, (entry_offset + entry_size) - offset); + + ssize_t total = 0; + + if (fseeko(self->file, entry_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_CUR) != 0) { + ERRNO(errno); + return -1; + } + + while (0 < count) { + size_t rc = fread(buf + total, 1, count, self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + + total += rc; + count -= rc; + } + + return total; +} + +ssize_t __ffs_entry_write(ffs_t * self, const char *path, const void *buf, + off_t offset, size_t count) +{ + assert(self != NULL); + assert(path != NULL); + assert(buf != NULL); + + if (count == 0) + return 0; + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + size_t entry_size = entry->size * self->hdr->block_size; + off_t entry_offset = entry->base * self->hdr->block_size; + + if (entry_size <= offset) + return 0; + else + count = min(count, (entry_offset + entry_size) - offset); + + ssize_t total = 0; + + if (fseeko(self->file, entry_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_CUR) != 0) { + ERRNO(errno); + return -1; + } + + while (0 < count) { + size_t rc = fwrite(buf + total, 1, count, self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + total += rc; + count -= rc; + } + + fflush(self->file); + + if (entry->actual < (uint32_t) total) { + entry->actual = (uint32_t) total; + self->dirty = true; + } + + return total; +} + +#if 0 +ssize_t __ffs_entry_copy(ffs_t *self, ffs_t *in, const char *path) +{ + assert(self != NULL); + assert(in != NULL); + assert(path != NULL); + + if (unlikely(*path == '\0')) + return 0; + + ffs_entry_t *src = __find_entry(in->hdr, path); + if (src == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, in->offset); + return -1; + } + + ffs_entry_t *dest = __find_entry(self->hdr, path); + if (dest == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (src->base != dest->base) { + UNEXPECTED("partition '%s' offsets differ '%x' != '%x'", + path, src->base, dest->base); + return -1; + } + + if (src->size != dest->size) { + UNEXPECTED("partition '%s' sizes differ '%x' != '%x'", + path, src->size, dest->size); + return -1; + } + + size_t block_size = self->hdr->block_size; + off_t src_offset = src->base * in->hdr->block_size; + size_t src_actual = src->actual; + off_t dest_offset = dest->base * self->hdr->block_size; + + if (fseeko(in->file, src_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, dest_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + char block[block_size]; + + while (0 < src_actual) { + size_t count = min(src_actual, block_size); + + size_t rc = fread(block, 1, count, in->file); + if (rc <= 0 && ferror(in->file)) { + ERRNO(errno); + return -1; + } + + rc = fwrite(block, 1, rc, self->file); + if (rc <= 0 && ferror(self->file)) { + ERRNO(errno); + return -1; + } + + total += rc; + src_actual -= rc; + } + + if (dest->actual != (uint32_t)total) { + dest->actual = (uint32_t) total; + self->dirty = true; + } + + return total; +} + +ssize_t __ffs_entry_compare(ffs_t * self, ffs_t * in, const char *path) +{ + assert(self != NULL); + assert(in != NULL); + assert(path != NULL); + + if (unlikely(*path == '\0')) + return 0; + + ffs_entry_t *src = __find_entry(in->hdr, path); + if (src == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, in->offset); + return -1; + } + + ffs_entry_t *dest = __find_entry(self->hdr, path); + if (dest == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (src->base != dest->base) { + UNEXPECTED("partition '%s' offsets differ '%x' != '%x'", + path, src->base, dest->base); + return -1; + } + + if (src->size != dest->size) { + UNEXPECTED("partition '%s' sizes differ '%x' != '%x'", + path, src->size, dest->size); + return -1; + } + + if (src->actual != dest->actual) { + UNEXPECTED("partition '%s' actual sizes differ '%x' != '%x'", + path, src->actual, dest->actual); + return -1; + } + + off_t offset = src->base * self->hdr->block_size; + size_t actual = src->actual; + + if (fseeko(in->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + size_t size = 256; + char __src[size], __dest[size]; + + while (0 < actual) { + size = min(actual, size); + + size_t rc = fread(__src, 1, size, in->file); + if (rc <= 0 && ferror(in->file)) { + ERRNO(errno); + return -1; + } + + rc = fread(__dest, 1, size, self->file); + if (rc <= 0 && ferror(self->file)) { + ERRNO(errno); + return -1; + } + + if (memcmp(__src, __dest, size) != 0) { + printf("==========> '%s'\n", self->path); + dump_memory(stdout, offset + total, __dest, size); + + printf("==========> '%s'\n", in->path); + dump_memory(stdout, offset + total, __src, size); + + break; + } + + actual -= size; + total += size; + } + + return total; +} +#endif + +int __ffs_entry_list(ffs_t * self, ffs_entry_t ** list) +{ + assert(self != NULL); + assert(list != NULL); + + size_t size = 0, count = 0; + *list = NULL; + + int name_list(ffs_entry_t * entry) { + if (size <= count) { + size += 10; + + *list = realloc(*list, size * sizeof(**list)); + if (*list == NULL) { + ERRNO(errno); + return -1; + } + } + + (*list)[count++] = *entry; + + return 0; + } + + if (__ffs_iterate_entries(self, name_list) != 0) { + if (*list != NULL) + free(*list), *list = NULL; + count = 0; + return -1; + } + + return count; +} + +/* ============================================================ */ diff --git a/ffs/src/libffs2.c b/ffs/src/libffs2.c new file mode 100644 index 0000000..0c83cf0 --- /dev/null +++ b/ffs/src/libffs2.c @@ -0,0 +1,489 @@ +/* + * 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: libffs2.c + * Author: Shaun Wetzstein + * Descr: FFS IO interface + * Note: + * Date: 05/07/12 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libffs2.h" + +#include +#include +#include +#include +#include +#include + +#define FFS_ERRSTR_MAX 1024 + +struct ffs_error { + int errnum; + char errstr[FFS_ERRSTR_MAX]; +}; + +typedef struct ffs_error ffs_error_t; + +static ffs_error_t __error; + +/* ============================================================ */ + +extern void ffs_errclr(void) +{ + __error.errnum = __error.errstr[0] = 0; +} + +extern int ffs_errnum(void) +{ + return __error.errnum; +} + +extern const char *ffs_errstr(void) +{ + return __error.errnum ? __error.errstr : NULL; +} + +int ffs_check(const char *path, off_t offset) +{ + RAII(FILE*, file, fopen(path, "r"), fclose); + if (file == NULL) { + __error.errnum = -1; + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : %s (errno=%d)\n", + program_invocation_short_name, "errno", + __FILE__, __LINE__, strerror(errno), errno); + + return -1; + } + + int rc = __ffs_fcheck(file, offset); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + // __ffs_check will return FFS_CHECK_* const's + } + + return rc; +} + +ffs_t *ffs_create(const char *path, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + ffs_t *self = __ffs_create(path, offset, block_size, block_count); + if (self == NULL) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return self; +} + +ffs_t *ffs_open(const char *path, off_t offset) +{ + FILE * file = fopen(path, "r+"); + if (file == NULL) { + __error.errnum = -1; + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : %s (errno=%d)\n", + program_invocation_short_name, "errno", + __FILE__, __LINE__, strerror(errno), errno); + + return NULL; + } + + ffs_t *self = __ffs_fopen(file, offset); + if (self == NULL) { + fclose(file); + + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return self; +} + +int ffs_info(ffs_t *self, int name, uint32_t *value) +{ + int rc = __ffs_info(self, name, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_close(ffs_t * self) +{ + int rc = __ffs_close(self); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_fsync(ffs_t * self) +{ + int rc = __ffs_fsync(self); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_list_entries(ffs_t * self, FILE * out) +{ + int rc = __ffs_list_entries(self, ".*", true, out); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *)) +{ + int rc = __ffs_iterate_entries(self, func); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_find(ffs_t * self, const char *path, ffs_entry_t * entry) +{ + int rc = __ffs_entry_find(self, path, entry); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_find_parent(ffs_t * self, const char *path, ffs_entry_t * entry) +{ + int rc = __ffs_entry_find_parent(self, path, entry); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size, + ffs_type_t type, uint32_t flags) +{ + int rc = __ffs_entry_add(self, path, offset, size, type, flags); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_delete(ffs_t * self, const char *path) +{ + int rc = __ffs_entry_delete(self, path); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_user_get(ffs_t * self, const char *path, uint32_t word, + uint32_t * value) +{ + int rc = __ffs_entry_user_get(self, path, word, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_user_put(ffs_t * self, const char *path, uint32_t word, + uint32_t value) +{ + int rc = __ffs_entry_user_put(self, path, word, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out) +{ + ssize_t rc = __ffs_entry_hexdump(self, path, out); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_truncate(ffs_t * self, const char *path, off_t offset, + uint8_t pad __unused__) +{ + return ffs_entry_truncate_no_pad(self, path, offset); +} + +ssize_t ffs_entry_truncate_no_pad(ffs_t * self, const char *path, off_t offset) +{ + ssize_t rc = __ffs_entry_truncate(self, path, offset); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_read(ffs_t * self, const char *path, void *buf, off_t offset, + size_t count) +{ + ssize_t rc = __ffs_entry_read(self, path, buf, offset, count); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_write(ffs_t * self, const char *path, const void *buf, + off_t offset, size_t count) +{ + ssize_t rc = __ffs_entry_write(self, path, buf, offset, count); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_list(ffs_t * self, ffs_entry_t ** list) +{ + ssize_t rc = __ffs_entry_list(self, list); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +/* ============================================================ */ diff --git a/ffs/test/Makefile b/ffs/test/Makefile new file mode 100644 index 0000000..37a4d0d --- /dev/null +++ b/ffs/test/Makefile @@ -0,0 +1,8 @@ +DEPTH = ../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.ppc.mk + +ARCH=ppc +TEST_LIBFFS_INSTALL = $(INST_TESTS) + +include ../Rules.test.mk diff --git a/ffs/test/ffs_tool_test.sh b/ffs/test/ffs_tool_test.sh new file mode 100755 index 0000000..37f5fe0 --- /dev/null +++ b/ffs/test/ffs_tool_test.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# ffs_tool_test.sh +# +# Test case to perform tests for supported options in ffs tool +# +# Author: Shekar Babu +# + +FFS_TOOL="ffs" +NOR_IMAGE="/tmp/pnor" +OFFSET="0x3F0000" +SIZE="8MiB" +BLOCK="64KiB" +LOGICAL="logical" +DATA="data" +PAD="0xFF" + +create_dummy_file() { + echo Creating a dummy file $1 of size $2 with sample data $3 + yes $3 | head -$2 > $1 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating dummy file $1 + exit $RC + fi + echo Success, creating $1 +} + +create_nor_image() { + + # Check if nor image already exist + if [ -f $1 ];then + rm $1 + fi + echo Creating nor image $1 + $FFS_TOOL --create $1 -p $2 -s $3 -b $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating $1 image + exit $RC + fi + echo Success, creating $1 image +} + +add_logical_partition() { + echo Adding logical partition $3 + echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4 + $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +add_data_partition() { + echo Adding data partition $3 + echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4 + $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +read_partition_entry() { + echo Reading partition entry $3 + echo $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force + $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force + RC=$? + if [ $RC -ne 0 ]; then + echo Error, reading partition entry $3 + exit $RC + fi + echo Success, reading partition entry $3 +} + +write_partition_entry() { + echo Writing to partition entry $3 + echo $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force + $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force + RC=$? + if [ $RC -ne 0 ]; then + echo Error, writing to partition entry $3 + exit $RC + fi + echo Success, writing to partition entry $3 +} + +list_partition_table_entries() { + echo Listing partition table entries in $1 + echo $FFS_TOOL --list $1 -O $2 + $FFS_TOOL --list $1 -O $2 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Listing partition table entries in $1 + exit $RC + fi + echo Success, Listing partition table entries in $1 +} +hexdump_partition_entry() { + echo Hexdump partition entry $3 into $4 + echo "$FFS_TOOL --hexdump $1 -O $2 --name $3 > $4" + $FFS_TOOL --hexdump $1 -O $2 --name $3 > $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, hexdump partition entry $3 into $4 + exit $RC + fi + echo Success, hexdump partition entry $3 into $4 +} + +delete_partition_entry() { + echo Delete partition entry $3 + echo $FFS_TOOL --delete $1 -O $2 --name $3 + $FFS_TOOL --delete $1 -O $2 --name $3 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, deleting partition entry $3 + exit $RC + fi + echo Success, deleting partition entry $3 +} + +get_partition_entry_user_word() { + echo Get user word from partition entry $3 + echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 + $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 > /tmp/GETUW + sed 's/^\(.\)\{7\}//g' /tmp/GETUW > /tmp/chop_GETUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Getting user word from partition entry $3 + exit $RC + fi + echo Success, Getting user word from partition entry $3 +} + +put_partition_entry_user_word() { + echo Put user word to partition entry $3 + echo $5 > /tmp/PUTUW + echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5 + $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Putting user word to partition entry $3 + exit $RC + fi + echo Success, Putting user word to partition entry $3 +} + +read_write_part_entry() { + write_partition_entry $1 $2 $3 $4 + read_partition_entry $1 $2 $3 $5 + cmp $4 $5 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, data read/write mismatch -- entry $3 + exit $RC + fi + echo PASS, data read/write matches -- entry $3 +} + +get_put_user_word() { + put_partition_entry_user_word $1 $2 $3 $4 $5 + get_partition_entry_user_word $1 $2 $3 $4 + cmp /tmp/chop_GETUW /tmp/PUTUW > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, user word get/put mismatch -- entry $3 + exit $RC + fi + echo PASS, user word get/put matches -- entry $3 + rm /tmp/GETUW /tmp/PUTUW /tmp/chop_GETUW +} + +compare_hexdump() { + hexdump_partition_entry $1 $2 $3 $4 + HEXFILE=/tmp/hex_sz0 + stat -c %s $4 > $HEXFILE + if [[ -s $HEXFILE ]] ; then + echo PASS, hexdump test on entry $3 + else + echo FAIL, hexdump test on entry $3 + exit $RC + fi + rm $4 $HEXFILE +} + +clean_data() { + rm $NOR_IMAGE /tmp/in_file /tmp/out_file + exit 0 +} + +# Main program starts + +# Create a dummy file as 'filename size data' +create_dummy_file /tmp/in_file 131072 WELCOME + +# Create nor image +create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK + +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL + +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1MiB 0M +add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1MiB 2M +add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 960K 3M +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1MiB 4M +add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1MiB 6M +add_data_partition $NOR_IMAGE $OFFSET boot1/bootfsp $DATA 960K 7M +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Perform read and write operations on all partition entries +read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file + +# Perform get and put user words on all partition entries +get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 0x0000000a +get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 0x0000000b +get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 0x0000000c +get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 0x0000000d +get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 0x0000000f + +# Hexdump partition entry +compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump + +# Delete a partition entry +delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Clean/remove all temporary files +clean_data diff --git a/ffs/test/libffs_test.sh b/ffs/test/libffs_test.sh new file mode 100755 index 0000000..a7e3adc --- /dev/null +++ b/ffs/test/libffs_test.sh @@ -0,0 +1,248 @@ +#!/bin/bash +# libffs_test.sh +# +# Test case to perform unit tests for all api's in libffs.so +# +# Author: Shekar Babu +# + + +NOR_IMAGE=/tmp/sunray2.nor +OFFSET=0 +#OFFSET=4128768 +SIZE=8388608 +#SIZE=67108864 #For 64MB nor +BLOCK=65536 +LOGICAL=logical +DATA=data + +create_dummy_file() { + echo Creating a dummy file $1 of size $2 with sample data $3 + yes $3 | head -$2 > $1 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating dummy file $1 + exit $RC + fi + echo Success, creating $1 +} + +create_nor_image() { + echo Creating nor image $1 + echo test_libffs -c $1 -O $2 -s $3 -b $4 + ./test_libffs -c $1 -O $2 -s $3 -b $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating $1 image + exit $RC + fi + echo Success, creating $1 image +} + +add_logical_partition() { + echo Adding logical partition $3 + echo test_libffs -a $1 -O $2 -n $3 -t $4 + ./test_libffs -a $1 -O $2 -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +add_data_partition() { + echo Adding data partition $3 + echo test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6 + ./test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +read_partition_entry() { + echo Reading partition entry $3 + echo test_libffs -r $1 -O $2 -n $3 -o $4 + ./test_libffs -r $1 -O $2 -n $3 -o $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, reading partition entry $3 + exit $RC + fi + echo Success, reading partition entry $3 +} + +write_partition_entry() { + echo Writing to partition entry $3 + echo test_libffs -w $1 -O $2 -n $3 -i $4 + ./test_libffs -w $1 -O $2 -n $3 -i $4 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo Error, writing to partition entry $3 + exit $RC + fi + echo Success, writing to partition entry $3 +} + +list_partition_table_entries() { + echo Listing partition table entries in $1 + echo test_libffs -l $1 -O $2 + ./test_libffs -l $1 -O $2 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Listing partition table entries in $1 + exit $RC + fi + echo Success, Listing partition table entries in $1 +} +hexdump_partition_entry() { + echo Hexdump partition entry $3 into $4 + echo "test_libffs -h $1 -O $2 -n $3 > $4" + ./test_libffs -h $1 -O $2 -n $3 > $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, hexdump partition entry $3 into $4 + exit $RC + fi + echo Success, hexdump partition entry $3 into $4 +} + +delete_partition_entry() { + echo Delete partition entry $3 + echo test_libffs -d $1 -O $2 -n $3 + ./test_libffs -d $1 -O $2 -n $3 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, deleting partition entry $3 + exit $RC + fi + echo Success, deleting partition entry $3 +} + +get_partition_entry_user_word() { + echo Get user word from partition entry $3 + echo test_libffs -m $1 -O $2 -n $3 -u $4 -g + ./test_libffs -m $1 -O $2 -n $3 -u $4 -g > /tmp/GETUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Getting user word from partition entry $3 + exit $RC + fi + echo Success, Getting user word from partition entry $3 +} + +put_partition_entry_user_word() { + echo Put user word to partition entry $3 + echo test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5 + ./test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5 > /tmp/PUTUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Putting user word to partition entry $3 + exit $RC + fi + echo Success, Putting user word to partition entry $3 +} + +read_write_part_entry() { + write_partition_entry $1 $2 $3 $4 + read_partition_entry $1 $2 $3 $5 + cmp $4 $5 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, data read/write mismatch -- entry $3 + exit $RC + fi + echo PASS, data read/write matches -- entry $3 +} + +get_put_user_word() { + put_partition_entry_user_word $1 $2 $3 $4 $5 + get_partition_entry_user_word $1 $2 $3 $4 + cmp /tmp/GETUW /tmp/PUTUW > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, user word get/put mismatch -- entry $3 + exit $RC + fi + echo PASS, user word get/put matches -- entry $3 + rm /tmp/GETUW /tmp/PUTUW +} + +compare_hexdump() { + hexdump_partition_entry $1 $2 $3 $4 + HEXFILE=/tmp/hex_sz0 + stat -c %s $4 > $HEXFILE + if [[ -s $HEXFILE ]] ; then + echo PASS, hexdump test on entry $3 + else + echo FAIL, hexdump test on entry $3 + exit $RC + fi + rm $4 $HEXFILE +} + +clean_data() { + rm $NOR_IMAGE /tmp/in_file /tmp/out_file + exit 0 +} + +# Main program starts + +# Create a dummy file as 'filename size data' +create_dummy_file /tmp/in_file 131072 WELCOME + +# Create nor image +create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK + +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1048576 65536 +add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1048576 2097152 +add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 1048576 3145728 +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1048576 4194304 +add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1048576 5242880 +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET linux0 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET linux0/vpd $DATA 1048576 6291456 +add_data_partition $NOR_IMAGE $OFFSET linux0/hostboot $DATA 1048576 7340032 + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Perform read and write operations on all partition entries +read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/spl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET linux0/vpd /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET linux0/hostboot /tmp/in_file /tmp/out_file + +# Perform get and put user words on all partition entries +get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 28 +get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 56 +get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 96 +get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 16 +get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 84 +get_put_user_word $NOR_IMAGE $OFFSET linux0/vpd 8 64 +get_put_user_word $NOR_IMAGE $OFFSET linux0/hostboot 15 42 + +# Hexdump partition entry +compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump + +# Delete a partition entry +delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Clean/remove all temporary files +clean_data diff --git a/ffs/test/test_libffs.c b/ffs/test/test_libffs.c new file mode 100644 index 0000000..f8c7966 --- /dev/null +++ b/ffs/test/test_libffs.c @@ -0,0 +1,654 @@ +/* + * 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: test_libffs.c + * Author: Shekar Babu S + * Descr: unit test tool for api's in libffs.so + * Date: 06/26/2012 + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "test_libffs.h" + +FILE* +log_open(void) { + + FILE *logfile = NULL; + logfile = fopen("test_libffs.log", "w"); + if (logfile == NULL) { + perror("logfile"); + exit(EXIT_FAILURE); + } + setvbuf(logfile, NULL, _IOLBF, 0); + return logfile; +} + +void +create_regular_file(ffs_ops_t *ffs_ops) { + int rc = 0; + size_t size = ffs_ops->device_size; + mode_t mode = (S_IRUSR | S_IWUSR) | (S_IRGRP | S_IRGRP) | S_IROTH; + + fprintf(ffs_ops->log, "%s: Creating regular file\n", __func__); + + int fd = open(ffs_ops->nor_image, O_RDWR | O_CREAT | O_TRUNC, mode); + if (fd == -1) { + fprintf(ffs_ops->log, "%s: Error creating regular file '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } + + if (ftruncate(fd, size) != 0) { + fprintf(ffs_ops->log, "%s: Error truncating '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } + + uint32_t page_size = sysconf(_SC_PAGESIZE); + char buf[page_size]; + memset(buf, 0xFF, page_size); + + while (0 < size) { + ssize_t rc = write(fd, buf, min(sizeof(buf), size)); + if (rc == -1) { + fprintf(ffs_ops->log, "%s: Error writing to '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } else if(rc == 0) { + break; + } + size -= rc; + } + + if(fd == 0) { + close(fd); + } +} + +int +create_partition(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + + create_regular_file(ffs_ops); + + ffs = __ffs_create(ffs_ops->nor_image, ffs_ops->part_off, + ffs_ops->blk_sz, + ffs_ops->device_size / ffs_ops->blk_sz); + + if(ffs != NULL) { + __ffs_close(ffs); + fprintf(ffs_ops->log, "%s: Creating pnor image '%s' success\n", + __func__, ffs_ops->nor_image); + } else { + fprintf(ffs_ops->log, "%s: Creating pnor image '%s' failed\n", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + } + + return rc; +} + +int +add_partition_entry(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs != NULL) { + __ffs_entry_add(ffs, ffs_ops->part_entry, ffs_ops->entry_off, + ffs_ops->entry_sz, ffs_ops->type, 0x00); + __ffs_close(ffs); + fprintf(ffs_ops->log, "%s: Adding partition entry '%s' " + "success\n", __func__, ffs_ops->part_entry); + } else { + fprintf(ffs_ops->log, "%s: Adding partition entry '%s' " + "failed\n", __func__, ffs_ops->part_entry); + } + return rc; +} + +uint32_t +read_partition(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + + FILE *fp = fopen(ffs_ops->o_file, "w+"); + if (fp == NULL) { + fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__, + ffs_ops->o_file); + rc = FFS_ERROR; + goto error; + } + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + goto error; + } + fprintf(ffs_ops->log, "%s: Successfully opened " + "nor image\n", __func__); + ffs_entry_t entry; + + if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) { + fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__, + ffs_ops->part_entry); + rc = FFS_ERROR; + goto error; + } + + if(false) { +error: + if(fp != NULL) + fclose(fp); + if(ffs != NULL) + __ffs_close(ffs); + return rc; + } + + fprintf(ffs_ops->log, "%s: Finding entry '%s' success\n", __func__, + ffs_ops->part_entry); + + uint32_t block_size = ffs->hdr->block_size; + char block[block_size]; + memset(block, 0, block_size); + + if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) { + fprintf(ffs_ops->log, "%s: Error, setvbuf failed " + "%s (errno=%d)", __func__, strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + + for (uint32_t i=0; ipart_entry, block, + i * block_size, block_size); + if(rc != block_size) { + fprintf(ffs_ops->log, "%s: Error, ffs_entry_read" + " '%s'\n", __func__, ffs_ops->part_entry); + rc = FFS_ERROR; + goto out; + } + + rc = fwrite(block, rc, 1, fp); + if (rc != 1) { + fprintf(ffs_ops->log, "%s: Error, fwrite " + "%s (errno=%d)\n", __func__, + strerror(ferror(fp)), ferror(fp)); + rc = FFS_ERROR; + goto out; + } + else if (rc == 0) + break; + } + + uint32_t bytes_read = entry.size * block_size; + + fprintf(ffs_ops->log, "%s: Read %d bytes from partition '%s' Success\n", + __func__, bytes_read, ffs_ops->part_entry); + + if (fclose(fp) == EOF) { + fprintf(ffs_ops->log, "%s: Error, flose '%s' " + "=> %s (errno=%d)", __func__, ffs_ops->o_file, + strerror(errno), errno); + fp = NULL; + rc = FFS_ERROR; + goto out; + } + + __ffs_close(ffs); + + fprintf(ffs_ops->log, "%s: Writing %d bytes from partition '%s' to " + "file '%s' Success\n", __func__, bytes_read, + ffs_ops->part_entry, ffs_ops->o_file); + + if(false) { +out: + if(fp != NULL) + fclose(fp); + if(ffs != NULL) + __ffs_close(ffs); + } + return rc; +} + +uint32_t +write_partition(ffs_ops_t * ffs_ops) { + + struct stat st; + uint32_t rc = 0; + ffs_t * ffs = NULL; + + + if (stat(ffs_ops->i_file, &st) != 0) { + fprintf(ffs_ops->log, "%s: '%s' => %s (errno=%d)", __func__, + ffs_ops->i_file, strerror(errno), errno); + rc = FFS_ERROR; + goto error; + } + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + goto error; + } + + fprintf(ffs_ops->log, "%s: Successfully opened nor image '%s' for " + "writing\n", __func__, ffs_ops->nor_image); + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) { + fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__, + ffs_ops->part_entry); + rc = FFS_ERROR; + goto error; + } + + size_t entry_size = entry.size * ffs->hdr->block_size; + + + fprintf(ffs_ops->log, "%s: Found entry '%s' with size=%d\n", __func__, + ffs_ops->part_entry, entry_size); + + if (entry_size < st.st_size) { + fprintf(ffs_ops->log, "%s: '%s' of size '%lld' too big for " + "partition '%s' of size '%d'\n", __func__, + ffs_ops->i_file, st.st_size, ffs_ops->nor_image, + entry_size); + rc = FFS_ERROR; + goto error; + } + + FILE * fp = fopen(ffs_ops->i_file, "r+"); + if (fp == NULL) { + fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__, + ffs_ops->i_file); + rc = FFS_ERROR; + goto error; + } + + if(false) { +error: + if(ffs != NULL) + __ffs_close(ffs); + return rc; + } + + uint32_t block_size = ffs->hdr->block_size; + char block[block_size]; + + if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) { + fprintf(ffs_ops->log, "%s: Error, setvbuf failed " + "%s (errno=%d)", __func__, strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + + fprintf(ffs_ops->log, "%s: Writing data file into partition\n", + __func__); + + for (uint32_t j=0; jpart_entry, block, + j * block_size, bytes_read); + if (bytes_read == 0) { + int err = ferror(fp); + if (err) { + fprintf(ffs_ops->log, "%s: Error, setvbuf " + "failed %s (errno=%d)", __func__, + strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + else { + break; + } + } + } + + if (fclose(fp) == EOF) { + fprintf(ffs_ops->log, "%s: Error, flose '%s' " + "=> %s (errno=%d)", __func__, ffs_ops->o_file, + strerror(errno), errno); + fp = NULL; + rc = FFS_ERROR; + goto out; + } + fprintf(ffs_ops->log, "%s: Writing to partition '%s' from data file " + "'%s' Success\n", __func__, ffs_ops->part_entry, + ffs_ops->i_file); + + if(false) { +out: + __ffs_close(ffs); + } + return rc; +} + +uint32_t +list_partition(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Listing partition entries in '%s'" + " image failed\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return rc; + } + //!< List all entries in the partition table + __ffs_list_entries(ffs, ".*", true, stdout); + fprintf(ffs_ops->log, "%s: Listing partition entries in '%s' image " + "success\n", __func__, ffs_ops->nor_image); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +hexdump_entry(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s'" + " image failed\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return rc; + } + //!< Hexdump the entry in the partition table + __ffs_entry_hexdump(ffs, ffs_ops->part_entry, stdout); + fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s' image " + "success\n", __func__, ffs_ops->nor_image); + __ffs_close(ffs); + + return rc; +} + +uint32_t +delete_entry(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Delete entry '%s' failed\n", + __func__, ffs_ops->part_entry); + rc = FFS_ERROR; + return rc; + } + //!< Delete the entry from the partition table + __ffs_entry_delete(ffs, ffs_ops->part_entry); + fprintf(ffs_ops->log, "%s: Delete entry '%s' success\n", __func__, + ffs_ops->part_entry); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +modify_entry_get(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + uint32_t value = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Get user word at '%d' failed\n", + __func__, ffs_ops->user); + rc = FFS_ERROR; + return rc; + } + //!< Get the user word at index + __ffs_entry_user_get(ffs, ffs_ops->part_entry, ffs_ops->user, &value); + fprintf(ffs_ops->log, "%s: Get user word at '%d' --> %x success\n", + __func__, ffs_ops->user, value); + //!< Required to check what is put + fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, value); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +modify_entry_put(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Put user word at '%d' failed\n", + __func__, ffs_ops->user); + rc = FFS_ERROR; + return rc; + } + //!< Put the user word at index + __ffs_entry_user_put(ffs, ffs_ops->part_entry, ffs_ops->user, + ffs_ops->value); + fprintf(ffs_ops->log, "%s: Put user word at '%d' --> %d success\n", + __func__, ffs_ops->user, ffs_ops->value); + //!< Required to check what is get + fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, ffs_ops->value); + + __ffs_close(ffs); + + return rc; +} + +void +usage(void) { + printf("This program is a unit test tool, its callers responsibility " + "to pass the correct parameters.\nNote: No usage errors are " + "displayed, any mistake in params may result in unexpected " + "results\n"); +} + +int +main(int argc, char * argv[]) { + + int32_t rc = 0; + ffs_ops_t ffs_ops; + + memset(&ffs_ops, 0, sizeof(ffs_ops_t)); + ffs_ops.part_off = PART_OFFSET; + ffs_ops.log = log_open(); + + while ((argc > 1) && (argv[1][0] == '-')) + { + switch (argv[1][1]) + { + +//test_libffs -c pnor -O part_offset -s dev_size -b block size +//test_libffs -c pnor -O 4128768 -s 67108864 -b 65536 + case 'c': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.device_size = atoi(argv[6]); + ffs_ops.blk_sz = atoi(argv[8]); + rc = create_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -a pnor -O part_offset -n part_name -t part_type +//test_libffs -a sunray.pnor -O 4128768 -n boot0 -t logical +//test_libffs -a pnor -O part_off -n part_name -t type -s size -o entry_off +//test_libffs -a sunray.pnor -O 4128768 -n boot0/bootenv -t data -s 1048576 -o 0 + case 'a': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; + if (!strcasecmp(argv[8], "logical")) { + ffs_ops.type = FFS_TYPE_LOGICAL; + ffs_ops.entry_sz = 0; + ffs_ops.entry_off = 0; + } + else if (!strcasecmp(argv[8], "data")) { + ffs_ops.type = FFS_TYPE_DATA; + ffs_ops.entry_sz = atol(argv[10]); + ffs_ops.entry_off = atoll(argv[12]); + } + rc = add_partition_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -r pnor -O part_off -n part_name -o out_file +//test_libffs -r sunray.pnor -O 4128768 -n boot0/bootenv -o out_file + case 'r': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.o_file = argv[8]; //!< out put file + ffs_ops.i_file = NULL; + rc = read_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -w pnor -O part_off -n part_name -i in_file +//test_libffs -w sunray.pnor -O 4128768 -n boot0/bootenv -i in_file + case 'w': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.i_file = argv[8]; //!< out put file + ffs_ops.o_file = NULL; + rc = write_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } +//test_libffs -l pnor -O part_off +//test_libffs -l sunray.pnor -O 4128768 + case 'l': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + rc = list_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -h pnor -O part_off -n part_entry +//test_libffs -h sunray.pnor -O 4128768 -n boot0/bootenv + case 'h': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + rc = hexdump_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -d pnor -O part_off -n part_entry +//test_libffs -d sunray.pnor -O 4128768 -n boot0/bootenv + case 'd': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + rc = delete_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -m pnor -O part_off -n part_name -u index -g +//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -g +//test_libffs -m pnor -O part_off -n part_name -u index -p -v some_value +//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -p -v 1024 + case 'm': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.user = atol(argv[8]); + if(!strcmp(argv[9], "-g")) { + rc = modify_entry_get(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + } else if(!strcmp(argv[9], "-p")) { + ffs_ops.value = atol(argv[11]); + rc = modify_entry_put(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + } + break; + default: + usage(); + break; + } + break; + } + +out: + if(ffs_ops.log != NULL) { + fclose(ffs_ops.log); + } + return rc; +} diff --git a/ffs/test/test_libffs.h b/ffs/test/test_libffs.h new file mode 100644 index 0000000..c9ec613 --- /dev/null +++ b/ffs/test/test_libffs.h @@ -0,0 +1,45 @@ +/* + * 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: test_libffs.c + * Author: Shekar Babu S + * Descr: unit test tool for api's in libffs.so + * Date: 06/26/2012 + */ + +#include "libffs.h" + +#define FFS_ERROR -1 +#define PART_OFFSET 0x3F0000 + +typedef struct ffs_operations{ + const char * nor_image; //!< Path to nor image special/regular file + const char * part_entry; //!< Logical partition/entry name + const char * i_file; //!< Input file + const char * o_file; //!< Output file + FILE * log; //!< Log file + size_t device_size;//!< Size of the nor flash + off_t part_off; //!< Offset of partition table + size_t blk_sz; //!< Block size + size_t entry_sz; //!< Partition entry size + off_t entry_off; //!< Offset of partition entry + uint32_t user; //!< Index to user word in any entry + uint32_t value; //!< User word at index in entry + ffs_type_t type; //!< Partition type +} ffs_ops_t; diff --git a/ffs/x86/Makefile b/ffs/x86/Makefile new file mode 100644 index 0000000..af907da --- /dev/null +++ b/ffs/x86/Makefile @@ -0,0 +1,7 @@ +DEPTH = .. + +ARCH=x86 +FFS_INSTALL = $(INST_USR_X86) +FFS_INSTALL_TEST = $(INST_TESTS_X86) + +include ../Rules.mk -- cgit v1.2.1