summaryrefslogtreecommitdiffstats
path: root/ffs
diff options
context:
space:
mode:
Diffstat (limited to 'ffs')
-rw-r--r--ffs/.gitignore3
-rw-r--r--ffs/Makefile10
-rw-r--r--ffs/Rules.mk30
-rw-r--r--ffs/Rules.test.mk25
-rw-r--r--ffs/ffs.h148
-rw-r--r--ffs/libffs.h174
-rw-r--r--ffs/libffs2.h332
-rw-r--r--ffs/src/ffs-fsp.h59
-rw-r--r--ffs/src/libffs.c1520
-rw-r--r--ffs/src/libffs2.c489
-rw-r--r--ffs/test/Makefile8
-rwxr-xr-xffs/test/ffs_tool_test.sh244
-rwxr-xr-xffs/test/libffs_test.sh248
-rw-r--r--ffs/test/test_libffs.c654
-rw-r--r--ffs/test/test_libffs.h45
-rw-r--r--ffs/x86/Makefile7
16 files changed, 3996 insertions, 0 deletions
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 <linux/types.h>
+#else
+#include <stdint.h>
+#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 <stdbool.h>
+#include <stdarg.h>
+
+#include <clib/exception.h>
+
+#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 <shaun@us.ibm.com>
+ * @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 <shaun@us.ibm.com>
+ * Descr: FFS IO interface
+ * Note:
+ * Date: 05/07/12
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <endian.h>
+#include <libgen.h>
+#include <regex.h>
+
+#include "libffs.h"
+
+#include <clib/assert.h>
+#include <clib/builtin.h>
+#include <clib/checksum.h>
+#include <clib/misc.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#ifndef be32toh
+#include <byteswap.h>
+#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; 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);
+ }
+ }
+
+ __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; i<hdr->entry_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; 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 -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; i<FFS_USER_WORDS; i++) {
+ fprintf(stdout, "[%2d] %8x ", i,
+ entry->user.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 <shaun@us.ibm.com>
+ * Descr: FFS IO interface
+ * Note:
+ * Date: 05/07/12
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <endian.h>
+#include <libgen.h>
+
+#include "libffs2.h"
+
+#include <clib/assert.h>
+#include <clib/builtin.h>
+#include <clib/checksum.h>
+#include <clib/misc.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#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 <shekbabu@in.ibm.com>
+#
+
+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 <shekbabu@in.ibm.com>
+#
+
+
+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 <shekbabu@in.ibm.com>
+ * Descr: unit test tool for api's in libffs.so
+ * Date: 06/26/2012
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <clib/exception.h>
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/min.h>
+#include <sys/xattr.h>
+
+#include <clib/bb_trace.h>
+
+#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; i<entry.size; i++) {
+ size_t rc = __ffs_entry_read(ffs, ffs_ops->part_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; j<entry.size; j++) {
+ clearerr(fp);
+
+ size_t bytes_read = fread(block, 1, block_size, fp);
+ __ffs_entry_write(ffs, ffs_ops->part_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 <shekbabu@in.ibm.com>
+ * 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
OpenPOWER on IntegriCloud