summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/cbfstool/cbfs_image.c121
-rw-r--r--util/cbfstool/cbfs_image.h4
-rw-r--r--util/cbfstool/cbfstool.c13
3 files changed, 138 insertions, 0 deletions
diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c
index 64266b0b5a..314ea5741d 100644
--- a/util/cbfstool/cbfs_image.c
+++ b/util/cbfstool/cbfs_image.c
@@ -441,6 +441,127 @@ int cbfs_copy_instance(struct cbfs_image *image, struct buffer *dst)
return 0;
}
+static size_t cbfs_file_entry_metadata_size(const struct cbfs_file *f)
+{
+ return ntohl(f->offset);
+}
+
+static size_t cbfs_file_entry_data_size(const struct cbfs_file *f)
+{
+ return ntohl(f->len);
+}
+
+static size_t cbfs_file_entry_size(const struct cbfs_file *f)
+{
+ return cbfs_file_entry_metadata_size(f) + cbfs_file_entry_data_size(f);
+}
+
+int cbfs_compact_instance(struct cbfs_image *image)
+{
+ assert(image);
+
+ struct cbfs_file *prev;
+ struct cbfs_file *cur;
+
+ /* The prev entry will always be an empty entry. */
+ prev = NULL;
+
+ /*
+ * Note: this function does not honor alignment or fixed location files.
+ * It's behavior is akin to cbfs_copy_instance() in that it expects
+ * the caller to understand the ramifications of compacting a
+ * fragmented CBFS image.
+ */
+
+ for (cur = cbfs_find_first_entry(image);
+ cur && cbfs_is_valid_entry(image, cur);
+ cur = cbfs_find_next_entry(image, cur)) {
+ size_t prev_size;
+ size_t cur_size;
+ size_t empty_metadata_size;
+ size_t spill_size;
+ uint32_t type = htonl(cur->type);
+
+ /* Current entry is empty. Kepp track of it. */
+ if ((type == htonl(CBFS_COMPONENT_NULL)) ||
+ (type == htonl(CBFS_COMPONENT_DELETED))) {
+ prev = cur;
+ continue;
+ }
+
+ /* Need to ensure the previous entry is an empty one. */
+ if (prev == NULL)
+ continue;
+
+ /* At this point prev is an empty entry. Put the non-empty
+ * file in prev's location. Then add a new emptry entry. This
+ * essentialy bubbles empty entries towards the end. */
+
+ prev_size = cbfs_file_entry_size(prev);
+ cur_size = cbfs_file_entry_size(cur);
+
+ /*
+ * Adjust the empty file size by the actual space occupied
+ * bewtween the beginning of the empty file and the non-empty
+ * file.
+ */
+ prev_size += (cbfs_get_entry_addr(image, cur) -
+ cbfs_get_entry_addr(image, prev)) - prev_size;
+
+ /* Move the non-empty file over the empty file. */
+ memmove(prev, cur, cur_size);
+
+ /*
+ * Get location of the empty file. Note that since prev was
+ * overwritten with the non-empty file the previously moved
+ * file needs to be used to calculate the empty file's location.
+ */
+ cur = cbfs_find_next_entry(image, prev);
+
+ /*
+ * The total space to work with for swapping the 2 entries
+ * consists of the 2 files' sizes combined. However, the
+ * cbfs_file entries start on CBFS_ALIGNMENT boundaries.
+ * Because of this the empty file size may end up smaller
+ * because of the non-empty file's metadata and data length.
+ *
+ * Calculate the spill size which is the amount of data lost
+ * due to the alignment constraints after moving the non-empty
+ * file.
+ */
+ spill_size = (cbfs_get_entry_addr(image, cur) -
+ cbfs_get_entry_addr(image, prev)) - cur_size;
+
+ empty_metadata_size = cbfs_calculate_file_header_size("");
+
+ /* Check if new empty size can contain the metadata. */
+ if (empty_metadata_size + spill_size > prev_size) {
+ ERROR("Unable to swap '%s' with prev empty entry.\n",
+ prev->filename);
+ return 1;
+ }
+
+ /* Update the empty file's size. */
+ prev_size -= spill_size + empty_metadata_size;
+
+ /* Create new empty file. */
+ cbfs_create_empty_entry(cur, CBFS_COMPONENT_NULL,
+ prev_size, "");
+
+ /* Merge any potential empty entries together. */
+ cbfs_walk(image, cbfs_merge_empty_entry, NULL);
+
+ /*
+ * Since current switched to an empty file keep track of it.
+ * Even if any empty files were merged the empty entry still
+ * starts at previously calculated location.
+ */
+ prev = cur;
+ }
+
+ return 0;
+}
+
int cbfs_image_delete(struct cbfs_image *image)
{
if (image == NULL)
diff --git a/util/cbfstool/cbfs_image.h b/util/cbfstool/cbfs_image.h
index 38510d2cfc..0d7877a7cc 100644
--- a/util/cbfstool/cbfs_image.h
+++ b/util/cbfstool/cbfs_image.h
@@ -76,6 +76,10 @@ int cbfs_image_from_buffer(struct cbfs_image *out, struct buffer *in,
* Will not succeed on new-style images without a master header. */
int cbfs_copy_instance(struct cbfs_image *image, struct buffer *dst);
+/* Compact a fragmented CBFS image by placing all the non-empty files at the
+ * beginning of the image. Returns 0 on success, otherwise non-zero. */
+int cbfs_compact_instance(struct cbfs_image *image);
+
/* Releases the CBFS image. Returns 0 on success, otherwise non-zero. */
int cbfs_image_delete(struct cbfs_image *image);
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index 13a9956045..5be2d6549c 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -1054,6 +1054,16 @@ static int cbfs_copy(void)
return cbfs_copy_instance(&src_image, param.image_region);
}
+static int cbfs_compact(void)
+{
+ struct cbfs_image image;
+ if (cbfs_image_from_buffer(&image, param.image_region,
+ param.headeroffset))
+ return 1;
+ WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
+ return cbfs_compact_instance(&image);
+}
+
static const struct command commands[] = {
{"add", "H:r:f:n:t:c:b:a:vA:gh?", cbfs_add, true, true},
{"add-flat-binary", "H:r:f:n:l:e:c:b:vA:gh?", cbfs_add_flat_binary,
@@ -1064,6 +1074,7 @@ static const struct command commands[] = {
true, true},
{"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
{"add-master-header", "H:r:vh?", cbfs_add_master_header, true, true},
+ {"compact", "r:h?", cbfs_compact, true, true},
{"copy", "r:R:h?", cbfs_copy, true, true},
{"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
{"hashcbfs", "r:R:A:vh?", cbfs_hash, true, true},
@@ -1198,6 +1209,8 @@ static void usage(char *name)
"Add a legacy CBFS master header\n"
" remove [-r image,regions] -n NAME "
"Remove a component\n"
+ " compact -r image,regions "
+ "Defragment CBFS image.\n"
" copy -r image,regions -R source-region "
"Create a copy (duplicate) cbfs instance in fmap\n"
" create -m ARCH -s size [-b bootblock offset] \\\n"