diff mbox

[RFC,v2,1/3] libflash/libffs: Rework libffs

Message ID 20161129003845.23051-2-cyril.bur@au1.ibm.com
State RFC
Headers show

Commit Message

Cyril Bur Nov. 29, 2016, 12:38 a.m. UTC
This patch attempts a rework of libffs to prepare it for future changes.

Firstly the types are split in two:
  1. Packed, big endian structures used to map exactly how the data is on flash.
  2. CPU endian, sane valued, not packed structures used to manipulate FFS data.

Secondly:
The packed struct can use BE types so that in future tools like sparse can be
run over the code to check for endian conversion bugs.

Thirdly:
defines of sizeof(struct ...) were removed for clarity.

Finally:
For ease of manipulation, the in memory FFS structures contain a linked list of
entries as this will make addition and removal operations much easier.

This patch should be invisible to consumers of libffs.

Signed-off-by: Cyril Bur <cyril.bur@au1.ibm.com>
---
 libflash/ffs.h    | 152 +++++++++++++++++++++--------
 libflash/libffs.c | 283 ++++++++++++++++++++++++++++++------------------------
 libflash/libffs.h |  11 +++
 3 files changed, 277 insertions(+), 169 deletions(-)
diff mbox

Patch

diff --git a/libflash/ffs.h b/libflash/ffs.h
index 5cb6778..09d9307 100644
--- a/libflash/ffs.h
+++ b/libflash/ffs.h
@@ -28,8 +28,11 @@ 
 #if defined(__KERNEL__)
 #include <linux/types.h>
 #else
+#define CCAN_SHORT_TYPES_H
+#include <ccan/endian/endian.h>
 #include <stdint.h>
 #endif
+#include <ccan/list/list.h>
 
 /* The version of this partition implementation */
 #define FFS_VERSION_1	1
@@ -40,25 +43,13 @@ 
 /* 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 {
+enum ffs_type {
 	FFS_TYPE_DATA      = 1,
 	FFS_TYPE_LOGICAL   = 2,
 	FFS_TYPE_PARTITION = 3,
@@ -74,7 +65,9 @@  enum type {
 #define FFS_ENRY_INTEG_ECC 0x8000
 
 /**
- * struct ffs_entry_user - User data enties
+ * struct __ffs_entry_user - On flash user data entries
+ *
+ * Represents the on flash layout of FFS structures
  *
  *  @chip:		Chip Select (0,1)
  *  @compressType:	Compression Indication/alg (0=not compressed)
@@ -84,18 +77,41 @@  enum type {
  *  @freeMisc[2]:	Unused Miscellaneious Info
  *  @freeUser[14]:	Unused User Data
  */
+struct __ffs_entry_user {
+	uint8_t chip;
+	uint8_t compresstype;
+	be16 datainteg;
+	uint8_t vercheck;
+	uint8_t miscflags;
+	uint8_t freemisc[2];
+	be32 reserved[14];
+} __attribute__ ((packed));
+
+/**
+ * struct ffs_entry_user - User data entries
+ *
+ * Usable in memory representation of a struct __ffs_entry_user
+ *
+ *  @chip:		Chip Select (0,1)
+ *  @compressType:	Compression Indication/alg (0=not compressed)
+ *  @dataInteg:		Indicates Data Integrity mechanism
+ *  @verCheck:		Indicates Version check type
+ *  @miscFlags:		Misc Partition related Flags
+ */
 struct ffs_entry_user {
-	uint8_t  chip;
-	uint8_t  compresstype;
+	uint8_t chip;
+	uint8_t compresstype;
 	uint16_t datainteg;
-	uint8_t  vercheck;
-	uint8_t  miscflags;
-	uint8_t  freemisc[2];
-	uint32_t reserved[14];
+	uint8_t vercheck;
+	uint8_t miscflags;
 };
 
 /**
- * struct ffs_entry - Partition entry
+ * struct __ffs_entry - On flash partition entry
+ *
+ * Represents the on flash layout of FFS structures
+ * Note: Unlike the in memory structures base and size of the entry are in
+ * units of block_size and the actual size is in bytes
  *
  * @name:	Opaque null terminated string
  * @base:	Starting offset of partition in flash (in hdr.block_size)
@@ -109,22 +125,54 @@  struct ffs_entry_user {
  * @user:	User data (optional)
  * @checksum:	Partition entry checksum (includes all above)
  */
+struct __ffs_entry {
+	char name[PART_NAME_MAX + 1];
+	be32 base;
+	be32 size;
+	be32 pid;
+	be32 id;
+	be32 type;
+	be32 flags;
+	be32 actual;
+	be32 resvd[4];
+	struct __ffs_entry_user user;
+	/* The checksum is actually endian agnostic */
+	uint32_t checksum;
+} __attribute__ ((packed));
+
+/**
+ * struct ffs_entry - Partition entry
+ *
+ * Useable in memory representation of a struct __ffs_entry
+ * Note: Unlike the on flash structure, all sizes here are in bytes!
+ *
+ * @name:	Opaque null terminated string
+ * @base:	Starting offset of partition in flash (in bytes)
+ * @size:	Partition size (in bytes)
+ * @actual:	Actual partition size (in bytes)
+ * @pid:	Parent partition entry (FFS_PID_TOPLEVEL for toplevel)
+ * @type:	Describe type of partition
+ * @flags:	Partition attributes (optional)
+ * @user:	User data (optional)
+ */
 struct ffs_entry {
-	char     name[PART_NAME_MAX + 1];
+	char name[PART_NAME_MAX + 1];
 	uint32_t base;
 	uint32_t size;
+	uint32_t actual;
 	uint32_t pid;
-	uint32_t id;
-	uint32_t type;
+	enum ffs_type type;
 	uint32_t flags;
-	uint32_t actual;
-	uint32_t resvd[4];
 	struct ffs_entry_user user;
-	uint32_t checksum;
-} __attribute__ ((packed));
+	struct list_node list;
+};
+
 
 /**
- * struct ffs_hdr - FSP Flash Structure header
+ * struct __ffs_hdr - On flash FSP Flash Structure header
+ *
+ * Represents the on flash layout of FFS structures
+ * Note: Beware that the size of the partition table is in units of block_size
  *
  * @magic:		Eye catcher/corruption detector
  * @version:		Version of the structure
@@ -133,22 +181,44 @@  struct ffs_entry {
  * @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
+ * @resvd[4]:		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[];
+struct __ffs_hdr {
+	be32 magic;
+	be32 version;
+	be32 size;
+	be32 entry_size;
+	be32 entry_count;
+	be32 block_size;
+	be32 block_count;
+	be32 resvd[4];
+	/* The checksum is actually endian agnostic */
+	uint32_t checksum;
+	struct __ffs_entry entries[];
 } __attribute__ ((packed));
 
+/**
+ * struct ffs_hdr - FSP Flash Structure header
+ *
+ * Useable in memory representation of a struct __ffs_hdr
+ * Note: All sizes here are in bytes
+ *
+ * @version:		Version of the structure
+ * @size:		Size of partition table (in bytes)
+ * @block_size:		Size of block on device (in bytes)
+ * @block_count:	Number of blocks on device. Not strictly nessesary here,
+ *                  can be calculated from size / block_size but makes a good
+ *                  sanity check and avoids having to do the division so often.
+ * @entries:		List of partition entries
+ */
+struct ffs_hdr {
+	uint32_t version;
+	uint32_t size;
+	uint32_t block_size;
+	uint32_t block_count;
+	struct list_head entries;
+};
 
 #endif /* __FFS_H__ */
diff --git a/libflash/libffs.c b/libflash/libffs.c
index 4d57992..94c83af 100644
--- a/libflash/libffs.c
+++ b/libflash/libffs.c
@@ -25,22 +25,14 @@ 
 #include <unistd.h>
 #endif
 
-#include <ccan/endian/endian.h>
-
 #include "libffs.h"
 
-enum ffs_type {
-	ffs_type_flash,
-	ffs_type_image,
-};
-
 struct ffs_handle {
 	struct ffs_hdr		hdr;	/* Converted header */
-	enum ffs_type		type;
 	uint32_t		toc_offset;
 	uint32_t		max_size;
-	void			*cache;
-	uint32_t		cached_size;
+	/* The converted header knows how big this is */
+	struct __ffs_hdr *cache;
 	struct blocklevel_device *bl;
 };
 
@@ -53,32 +45,105 @@  static uint32_t ffs_checksum(void* data, size_t size)
 	return csum;
 }
 
-static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *src)
+/* Helper functions for typesafety and size safety */
+static uint32_t ffs_hdr_checksum(struct __ffs_hdr *hdr)
+{
+	return ffs_checksum(hdr, sizeof(struct __ffs_hdr));
+}
+
+static uint32_t ffs_entry_checksum(struct __ffs_entry *ent)
 {
-	dst->magic = be32_to_cpu(src->magic);
-	if (dst->magic != FFS_MAGIC)
+	return ffs_checksum(ent, sizeof(struct __ffs_entry));
+}
+
+static int ffs_check_convert_header(struct ffs_hdr *dst, struct __ffs_hdr *src)
+{
+	if (be32_to_cpu(src->magic) != FFS_MAGIC)
 		return FFS_ERR_BAD_MAGIC;
 	dst->version = be32_to_cpu(src->version);
 	if (dst->version != FFS_VERSION_1)
 		return FFS_ERR_BAD_VERSION;
-	if (ffs_checksum(src, FFS_HDR_SIZE) != 0)
+	if (ffs_hdr_checksum(src) != 0)
 		return FFS_ERR_BAD_CKSUM;
-	dst->size = be32_to_cpu(src->size);
-	dst->entry_size = be32_to_cpu(src->entry_size);
-	dst->entry_count = be32_to_cpu(src->entry_count);
+	if (be32_to_cpu(src->entry_size) != sizeof(struct __ffs_entry))
+		return FFS_ERR_BAD_SIZE;
 	dst->block_size = be32_to_cpu(src->block_size);
+	dst->size = be32_to_cpu(src->size) * dst->block_size;
 	dst->block_count = be32_to_cpu(src->block_count);
 
 	return 0;
 }
 
+static int ffs_entry_to_flash(struct ffs_hdr *hdr,
+		struct __ffs_entry *dst, struct ffs_entry *src)
+{
+	int index = 1; /* On flash indexes start at 1 */
+	struct ffs_entry *ent = NULL;
+
+	if (!hdr || !dst || !src)
+		return -1;
+
+	list_for_each(&hdr->entries, ent, list) {
+		if (ent == src)
+			break;
+		index++;
+	}
+
+	if (!ent)
+		return FFS_ERR_PART_NOT_FOUND;
+
+	memcpy(dst->name, src->name, sizeof(dst->name));
+	dst->name[PART_NAME_MAX] = '\0';
+	dst->base = cpu_to_be32(src->base / hdr->block_size);
+	dst->size = cpu_to_be32(src->size / hdr->block_size);
+	dst->pid = cpu_to_be32(src->pid);
+	dst->id = cpu_to_be32(index);
+	dst->type = cpu_to_be32(src->type); /* TODO: Check that it is valid? */
+	dst->flags = cpu_to_be32(src->flags);
+	dst->actual = cpu_to_be32(src->actual);
+	dst->user.datainteg = cpu_to_be16(src->user.datainteg);
+	dst->checksum = ffs_entry_checksum(dst);
+
+	return 0;
+}
+
+static int ffs_entry_to_cpu(struct ffs_hdr *hdr,
+		struct ffs_entry *dst, struct __ffs_entry *src)
+{
+	if (ffs_entry_checksum(src) != 0)
+		return FFS_ERR_BAD_CKSUM;
+	memcpy(dst->name, src->name, sizeof(dst->name));
+	dst->name[PART_NAME_MAX] = '\0';
+	dst->base = be32_to_cpu(src->base) * hdr->block_size;
+	dst->size = be32_to_cpu(src->size) * hdr->block_size;
+	dst->actual = be32_to_cpu(src->actual);
+	dst->pid = be32_to_cpu(src->pid);
+	dst->type = be32_to_cpu(src->type); /* TODO: Check that it is valid? */
+	dst->flags = be32_to_cpu(src->flags);
+	dst->user.datainteg = be16_to_cpu(src->user.datainteg);
+	return 0;
+}
+
+static struct ffs_entry *ffs_get_part(struct ffs_handle *ffs, uint32_t index)
+{
+	int i = 0;
+	struct ffs_entry *ent = NULL;
+
+	list_for_each(&ffs->hdr.entries, ent, list)
+		if (i++ == index)
+			break;
+
+	return ent;
+}
+
 int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
 		struct ffs_handle **ffs, bool mark_ecc)
 {
-	struct ffs_hdr hdr;
-	struct ffs_hdr blank_hdr;
+	struct __ffs_hdr blank_hdr;
+	struct __ffs_hdr raw_hdr;
 	struct ffs_handle *f;
 	uint32_t total_size;
+	struct ffs_entry *ent;
 	int rc, i;
 
 	if (!ffs || !bl)
@@ -97,7 +162,7 @@  int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
 		return FLASH_ERR_PARM_ERROR;
 
 	/* Read flash header */
-	rc = blocklevel_read(bl, offset, &hdr, sizeof(hdr));
+	rc = blocklevel_read(bl, offset, &raw_hdr, sizeof(raw_hdr));
 	if (rc) {
 		FL_ERR("FFS: Error %d reading flash header\n", rc);
 		return rc;
@@ -106,17 +171,17 @@  int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
 	/*
 	 * Flash controllers can get deconfigured or otherwise upset, when this
 	 * happens they return all 0xFF bytes.
-	 * An ffs_hdr consisting of all 0xFF cannot be valid and it would be
+	 * An __ffs_hdr consisting of all 0xFF cannot be valid and it would be
 	 * nice to drop a hint to the user to help with debugging. This will
 	 * help quickly differentiate between flash corruption and standard
 	 * type 'reading from the wrong place' errors vs controller errors or
 	 * reading erased data.
 	 */
-	memset(&blank_hdr, UINT_MAX, sizeof(struct ffs_hdr));
-	if (memcmp(&blank_hdr, &hdr, sizeof(struct ffs_hdr)) == 0) {
+	memset(&blank_hdr, UINT_MAX, sizeof(struct __ffs_hdr));
+	if (memcmp(&blank_hdr, &raw_hdr, sizeof(struct __ffs_hdr)) == 0) {
 		FL_ERR("FFS: Reading the flash has returned all 0xFF.\n");
-		FL_ERR("Are you reading erased flash?\n");
-		FL_ERR("Is something else using the flash controller?\n");
+		FL_ERR("     Are you reading erased flash?\n");
+		FL_ERR("     Is something else using the flash controller?\n");
 		return FLASH_ERR_BAD_READ;
 	}
 
@@ -131,160 +196,125 @@  int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
 	f->bl = bl;
 
 	/* Convert and check flash header */
-	rc = ffs_check_convert_header(&f->hdr, &hdr);
+	rc = ffs_check_convert_header(&f->hdr, &raw_hdr);
 	if (rc) {
 		FL_ERR("FFS: Error %d checking flash header\n", rc);
 		goto out;
 	}
 
 	/*
-	 * Decide how much of the image to grab to get the whole
-	 * partition map.
+	 * Grab the entire partition header
 	 */
-	f->cached_size = f->hdr.block_size * f->hdr.size;
-	FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size);
+	FL_DBG("FFS: Partition map size: 0x%x\n", f->hdr.size);
+	if (offset + f->hdr.size > max_size) {
+		FL_ERR("FFS: Partition table is bigger than max_size\n");
+		goto out;
+	}
 
 	/* Allocate cache */
-	f->cache = malloc(f->cached_size);
+	f->cache = malloc(f->hdr.size);
 	if (!f->cache) {
 		rc = FLASH_ERR_MALLOC_FAILED;
 		goto out;
 	}
 
 	/* Read the cached map */
-	rc = blocklevel_read(bl, offset, f->cache, f->cached_size);
+	rc = blocklevel_read(bl, offset, f->cache, f->hdr.size);
 	if (rc) {
 		FL_ERR("FFS: Error %d reading flash partition map\n", rc);
 		goto out;
 	}
 
-	if (mark_ecc) {
-		uint32_t start, total_size;
-		bool ecc;
-		for (i = 0; i < f->hdr.entry_count; i++) {
-			rc = ffs_part_info(f, i, NULL, &start, &total_size,
-					NULL, &ecc);
+	list_head_init(&f->hdr.entries);
+	for (i = 0; i < be32_to_cpu(raw_hdr.entry_count); i++) {
+		ent = calloc(1, sizeof(struct ffs_entry));
+		if (!ent) {
+			rc = FLASH_ERR_MALLOC_FAILED;
+			goto out;
+		}
+
+		list_add_tail(&f->hdr.entries, &ent->list);
+		rc = ffs_entry_to_cpu(&f->hdr, ent, &f->cache->entries[i]);
+		if (rc)
+			goto out;
+
+		if (mark_ecc && has_ecc(ent)) {
+			rc = blocklevel_ecc_protect(bl, ent->base, ent->size);
 			if (rc) {
-				FL_ERR("FFS: Failed to read ffs partition %d\n",
-						i);
+				FL_ERR("Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n",
+				       ent->base, ent->size);
 				goto out;
 			}
-			if (ecc) {
-				rc = blocklevel_ecc_protect(bl, start, total_size);
-				if (rc) {
-					FL_ERR("Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n",
-					       start, total_size);
-					goto out;
-				}
-			}  /* ecc */
-		} /* for */
+		}
 	}
 
 out:
 	if (rc == 0)
 		*ffs = f;
 	else
-		free(f);
+		ffs_close(f);
 
 	return rc;
 }
 
 void ffs_close(struct ffs_handle *ffs)
 {
-	if (ffs->cache)
-		free(ffs->cache);
-	free(ffs);
-}
+	struct ffs_entry *ent, *next;
 
-static struct ffs_entry *ffs_get_part(struct ffs_handle *ffs, uint32_t index,
-				      uint32_t *out_offset)
-{
-	uint32_t esize = ffs->hdr.entry_size;
-	uint32_t offset = FFS_HDR_SIZE + index * esize;
-
-	if (index > ffs->hdr.entry_count)
-		return NULL;
-	if (out_offset)
-		*out_offset = ffs->toc_offset + offset;
-	return (struct ffs_entry *)(ffs->cache + offset);
-}
+	list_for_each_safe(&ffs->hdr.entries, ent, next, list) {
+		list_del(&ent->list);
+		free(ent);
+	}
 
-static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry *src)
-{
-	if (ffs_checksum(src, FFS_ENTRY_SIZE) != 0)
-		return FFS_ERR_BAD_CKSUM;
-	memcpy(dst->name, src->name, sizeof(dst->name));
-	dst->base = be32_to_cpu(src->base);
-	dst->size = be32_to_cpu(src->size);
-	dst->pid = be32_to_cpu(src->pid);
-	dst->id = be32_to_cpu(src->id);
-	dst->type = be32_to_cpu(src->type);
-	dst->flags = be32_to_cpu(src->flags);
-	dst->actual = be32_to_cpu(src->actual);
-	dst->user.datainteg = be16_to_cpu(src->user.datainteg);
+	if (ffs->cache)
+		free(ffs->cache);
 
-	return 0;
+	free(ffs);
 }
 
 int ffs_lookup_part(struct ffs_handle *ffs, const char *name,
 		    uint32_t *part_idx)
 {
-	struct ffs_entry ent;
-	uint32_t i;
-	int rc;
+	int i = 0;
+	struct ffs_entry *ent = NULL;
 
-	/* Lookup the requested partition */
-	for (i = 0; i < ffs->hdr.entry_count; i++) {
-		struct ffs_entry *src_ent  = ffs_get_part(ffs, i, NULL);
-		rc = ffs_check_convert_entry(&ent, src_ent);
-		if (rc) {
-			FL_ERR("FFS: Bad entry %d in partition map\n", i);
-			continue;
-		}
-		if (!strncmp(name, ent.name, sizeof(ent.name)))
+	list_for_each(&ffs->hdr.entries, ent, list) {
+		if (!strncmp(name, ent->name, sizeof(ent->name)))
 			break;
+		i++;
 	}
-	if (i >= ffs->hdr.entry_count)
-		return FFS_ERR_PART_NOT_FOUND;
+
 	if (part_idx)
 		*part_idx = i;
-	return 0;
+	return ent ? 0 : FFS_ERR_PART_NOT_FOUND;
 }
 
 int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx,
 		  char **name, uint32_t *start,
 		  uint32_t *total_size, uint32_t *act_size, bool *ecc)
 {
-	struct ffs_entry *raw_ent;
-	struct ffs_entry ent;
+	struct ffs_entry *ent;
 	char *n;
-	int rc;
-
-	if (part_idx >= ffs->hdr.entry_count)
-		return FFS_ERR_PART_NOT_FOUND;
 
-	raw_ent = ffs_get_part(ffs, part_idx, NULL);
-	if (!raw_ent)
+	ent = ffs_get_part(ffs, part_idx);
+	if (!ent)
 		return FFS_ERR_PART_NOT_FOUND;
 
-	rc = ffs_check_convert_entry(&ent, raw_ent);
-	if (rc) {
-		FL_ERR("FFS: Bad entry %d in partition map\n", part_idx);
-		return rc;
-	}
 	if (start)
-		*start = ent.base * ffs->hdr.block_size;
+		*start = ent->base;
 	if (total_size)
-		*total_size = ent.size * ffs->hdr.block_size;
+		*total_size = ent->size;
 	if (act_size)
-		*act_size = ent.actual;
+		*act_size = ent->actual;
 	if (ecc)
-		*ecc = ((ent.user.datainteg & FFS_ENRY_INTEG_ECC) != 0);
+		*ecc = has_ecc(ent);
 
 	if (name) {
 		n = malloc(PART_NAME_MAX + 1);
+		if (!n)
+			return FLASH_ERR_MALLOC_FAILED;
 		memset(n, 0, PART_NAME_MAX + 1);
-		strncpy(n, ent.name, PART_NAME_MAX);
+		strncpy(n, ent->name, PART_NAME_MAX);
 		*name = n;
 	}
 	return 0;
@@ -334,33 +364,30 @@  int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx,
 			uint32_t act_size)
 {
 	struct ffs_entry *ent;
+	struct __ffs_entry raw_ent;
 	uint32_t offset;
+	int rc;
 
-	if (part_idx >= ffs->hdr.entry_count) {
-		FL_DBG("FFS: Entry out of bound\n");
-		return FFS_ERR_PART_NOT_FOUND;
-	}
-
-	ent = ffs_get_part(ffs, part_idx, &offset);
+	ent = ffs_get_part(ffs, part_idx);
 	if (!ent) {
 		FL_DBG("FFS: Entry not found\n");
 		return FFS_ERR_PART_NOT_FOUND;
 	}
+	offset = ent->base;
 	FL_DBG("FFS: part index %d at offset 0x%08x\n",
 	       part_idx, offset);
 
-	/*
-	 * NOTE: We are accessing the unconverted ffs_entry from the PNOR here
-	 * (since we are going to write it back) so we need to be endian safe.
-	 */
-	if (ent->actual == cpu_to_be32(act_size)) {
+	if (ent->actual == act_size) {
 		FL_DBG("FFS: ent->actual alrady matches: 0x%08x==0x%08x\n",
-		       cpu_to_be32(act_size), ent->actual);
+		       act_size, ent->actual);
 		return 0;
 	}
-	ent->actual = cpu_to_be32(act_size);
-	ent->checksum = ffs_checksum(ent, FFS_ENTRY_SIZE_CSUM);
+	ent->actual = act_size;
+
+	rc = ffs_entry_to_flash(&ffs->hdr, &raw_ent, ent);
+	if (rc)
+		return rc;
 
-	return blocklevel_write(ffs->bl, offset, ent, FFS_ENTRY_SIZE);
+	return blocklevel_write(ffs->bl, offset, &raw_ent, sizeof(struct __ffs_entry));
 }
 
diff --git a/libflash/libffs.h b/libflash/libffs.h
index a9ff574..23ccb8f 100644
--- a/libflash/libffs.h
+++ b/libflash/libffs.h
@@ -22,6 +22,7 @@ 
 
 /* FFS handle, opaque */
 struct ffs_handle;
+struct ffs_entry;
 
 /* Error codes:
  *
@@ -34,6 +35,16 @@  struct ffs_handle;
 #define FFS_ERR_BAD_CKSUM	102
 #define FFS_ERR_PART_NOT_FOUND	103
 #define FFS_ERR_BAD_ECC		104
+#define FFS_ERR_BAD_SIZE	105
+#define FFS_ERR_BAD_PART_NAME	106
+#define FFS_ERR_BAD_PART_BASE	107
+#define FFS_ERR_BAD_PART_SIZE	108
+#define FFS_ERR_BAD_PART_PID	109
+
+static inline bool has_ecc(struct ffs_entry *ent)
+{
+	return ((ent->user.datainteg & FFS_ENRY_INTEG_ECC) != 0);
+}
 
 /* Init */