diff options
author | Cyril Bur <cyril.bur@au1.ibm.com> | 2018-03-15 16:58:23 +1100 |
---|---|---|
committer | Stewart Smith <stewart@linux.ibm.com> | 2018-04-09 03:45:23 -0500 |
commit | 79316cb6aca6fc79585f4f47c18a37f2eb177780 (patch) | |
tree | c228abad78786db2ac503444a56eec0467b089e8 /libflash/libffs.c | |
parent | 3d47dbb4fb8dc010c1905a878794731e812a05ac (diff) | |
download | skiboot-79316cb6aca6fc79585f4f47c18a37f2eb177780.zip skiboot-79316cb6aca6fc79585f4f47c18a37f2eb177780.tar.gz skiboot-79316cb6aca6fc79585f4f47c18a37f2eb177780.tar.bz2 |
libflash/libffs: Refcount ffs entries
Currently consumers can add an new ffs entry to multiple headers, this
is fine but freeing any of the headers will cause the entry to be freed,
this causes double free problems.
Even if only one header is uses, the consumer of the library still has a
reference to the entry, which they may well reuse at some other point.
libffs will now refcount entries and only free when there are no more
references.
This patch also removes the pointless return value of ffs_hdr_free()
Signed-off-by: Cyril Bur <cyril.bur@au1.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
Diffstat (limited to 'libflash/libffs.c')
-rw-r--r-- | libflash/libffs.c | 38 |
1 files changed, 31 insertions, 7 deletions
diff --git a/libflash/libffs.c b/libflash/libffs.c index d295280..b4a8116 100644 --- a/libflash/libffs.c +++ b/libflash/libffs.c @@ -280,13 +280,35 @@ bool has_flag(struct ffs_entry *ent, uint16_t flag) return ((ent->user.miscflags & flag) != 0); } -struct ffs_entry *ffs_entry_get(struct ffs_handle *ffs, uint32_t index) +static struct ffs_entry *__ffs_entry_get(struct ffs_handle *ffs, uint32_t index) { - if (!ffs || index >= ffs->hdr.count) + if (index >= ffs->hdr.count) return NULL; return ffs->hdr.entries[index]; } +struct ffs_entry *ffs_entry_get(struct ffs_handle *ffs, uint32_t index) +{ + struct ffs_entry *ret = __ffs_entry_get(ffs, index); + if (ret) + ret->ref++; + return ret; +} + +struct ffs_entry *ffs_entry_put(struct ffs_entry *ent) +{ + if (!ent) + return NULL; + + ent->ref--; + if (ent->ref == 0) { + free(ent); + ent = NULL; + } + + return ent; +} + bool has_ecc(struct ffs_entry *ent) { return ((ent->user.datainteg & FFS_ENRY_INTEG_ECC) != 0); @@ -402,6 +424,7 @@ int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl, } f->hdr.entries[f->hdr.count++] = ent; + ent->ref = 1; rc = ffs_entry_to_cpu(&f->hdr, ent, &f->cache->entries[i]); if (rc) { FL_DBG("FFS: Failed checksum for partition %s\n", @@ -436,15 +459,14 @@ static void __hdr_free(struct ffs_hdr *hdr) return; for (i = 0; i < hdr->count; i++) - free(hdr->entries[i]); + ffs_entry_put(hdr->entries[i]); free(hdr->entries); } -int ffs_hdr_free(struct ffs_hdr *hdr) +void ffs_hdr_free(struct ffs_hdr *hdr) { __hdr_free(hdr); free(hdr); - return 0; } void ffs_close(struct ffs_handle *ffs) @@ -483,7 +505,7 @@ int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx, struct ffs_entry *ent; char *n; - ent = ffs_entry_get(ffs, part_idx); + ent = __ffs_entry_get(ffs, part_idx); if (!ent) return FFS_ERR_PART_NOT_FOUND; @@ -609,6 +631,7 @@ int ffs_entry_add(struct ffs_hdr *hdr, struct ffs_entry *entry) } hdr->entries_size += HDR_ENTRIES_NUM; } + entry->ref++; hdr->entries[hdr->count++] = entry; return 0; @@ -728,6 +751,7 @@ int ffs_entry_new(const char *name, uint32_t base, uint32_t size, struct ffs_ent ret->actual = size; ret->pid = FFS_PID_TOPLEVEL; ret->type = FFS_TYPE_DATA; + ret->ref = 1; *r = ret; return 0; @@ -790,7 +814,7 @@ int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, uint32_t offset; int rc; - ent = ffs_entry_get(ffs, part_idx); + ent = __ffs_entry_get(ffs, part_idx); if (!ent) { FL_DBG("FFS: Entry not found\n"); return FFS_ERR_PART_NOT_FOUND; |