AUTHORS, util/: Drop individual copyright notices
[coreboot.git] / util / cbfstool / partitioned_file.c
blob917341c1f9b127508339f190c8f0a094fb32bdd3
1 /* read and write binary file "partitions" described by FMAP */
2 /*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; version 2 of the License.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
13 #include "partitioned_file.h"
15 #include "cbfs_sections.h"
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
21 struct partitioned_file {
22 struct fmap *fmap;
23 struct buffer buffer;
24 FILE *stream;
27 static bool fill_ones_through(struct partitioned_file *file)
29 assert(file);
31 memset(file->buffer.data, 0xff, file->buffer.size);
32 return partitioned_file_write_region(file, &file->buffer);
35 static unsigned count_selected_fmap_entries(const struct fmap *fmap,
36 partitioned_file_fmap_selector_t callback, const void *arg)
38 assert(fmap);
39 assert(callback);
41 unsigned count = 0;
42 for (unsigned i = 0; i < fmap->nareas; ++i) {
43 if (callback(fmap->areas + i, arg))
44 ++count;
46 return count;
49 static partitioned_file_t *reopen_flat_file(const char *filename,
50 bool write_access)
52 assert(filename);
53 struct partitioned_file *file = calloc(1, sizeof(*file));
54 const char *access_mode;
56 if (!file) {
57 ERROR("Failed to allocate partitioned file structure\n");
58 return NULL;
61 if (buffer_from_file(&file->buffer, filename)) {
62 free(file);
63 return NULL;
66 access_mode = write_access ? "rb+" : "rb";
67 file->stream = fopen(filename, access_mode);
69 if (!file->stream) {
70 perror(filename);
71 partitioned_file_close(file);
72 return NULL;
75 return file;
78 partitioned_file_t *partitioned_file_create_flat(const char *filename,
79 size_t image_size)
81 assert(filename);
83 struct partitioned_file *file = calloc(1, sizeof(*file));
84 if (!file) {
85 ERROR("Failed to allocate partitioned file structure\n");
86 return NULL;
89 file->stream = fopen(filename, "wb");
90 if (!file->stream) {
91 perror(filename);
92 free(file);
93 return NULL;
96 if (buffer_create(&file->buffer, image_size, filename)) {
97 partitioned_file_close(file);
98 return NULL;
101 if (!fill_ones_through(file)) {
102 partitioned_file_close(file);
103 return NULL;
106 return file;
109 partitioned_file_t *partitioned_file_create(const char *filename,
110 struct buffer *flashmap)
112 assert(filename);
113 assert(flashmap);
114 assert(flashmap->data);
116 if (fmap_find((const uint8_t *)flashmap->data, flashmap->size) != 0) {
117 ERROR("Attempted to create a partitioned image out of something that isn't an FMAP\n");
118 return NULL;
120 struct fmap *bootstrap_fmap = (struct fmap *)flashmap->data;
122 const struct fmap_area *fmap_area =
123 fmap_find_area(bootstrap_fmap, SECTION_NAME_FMAP);
124 if (!fmap_area) {
125 ERROR("Provided FMAP missing '%s' region\n", SECTION_NAME_FMAP);
126 return NULL;
129 if (count_selected_fmap_entries(bootstrap_fmap,
130 partitioned_file_fmap_select_children_of, fmap_area)) {
131 ERROR("Provided FMAP's '%s' region contains other regions\n",
132 SECTION_NAME_FMAP);
133 return NULL;
136 int fmap_len = fmap_size(bootstrap_fmap);
137 if (fmap_len < 0) {
138 ERROR("Unable to determine size of provided FMAP\n");
139 return NULL;
141 assert((size_t)fmap_len <= flashmap->size);
142 if ((uint32_t)fmap_len > fmap_area->size) {
143 ERROR("Provided FMAP's '%s' region needs to be at least %d bytes\n",
144 SECTION_NAME_FMAP, fmap_len);
145 return NULL;
148 partitioned_file_t *file = partitioned_file_create_flat(filename,
149 bootstrap_fmap->size);
150 if (!file)
151 return NULL;
153 struct buffer fmap_region;
154 buffer_splice(&fmap_region, &file->buffer, fmap_area->offset, fmap_area->size);
155 memcpy(fmap_region.data, bootstrap_fmap, fmap_len);
156 if (!partitioned_file_write_region(file, &fmap_region)) {
157 partitioned_file_close(file);
158 return NULL;
160 file->fmap = (struct fmap *)(file->buffer.data + fmap_area->offset);
162 return file;
165 partitioned_file_t *partitioned_file_reopen(const char *filename,
166 bool write_access)
168 assert(filename);
170 partitioned_file_t *file = reopen_flat_file(filename, write_access);
171 if (!file)
172 return NULL;
174 long fmap_region_offset = fmap_find((const uint8_t *)file->buffer.data,
175 file->buffer.size);
176 if (fmap_region_offset < 0) {
177 INFO("Opening image as a flat file because it doesn't contain any FMAP\n");
178 return file;
180 file->fmap = (struct fmap *)(file->buffer.data + fmap_region_offset);
182 if (file->fmap->size > file->buffer.size) {
183 int fmap_region_size = fmap_size(file->fmap);
184 ERROR("FMAP records image size as %u, but file is only %zu bytes%s\n",
185 file->fmap->size, file->buffer.size,
186 fmap_region_offset == 0 &&
187 (signed)file->buffer.size == fmap_region_size ?
188 " (is it really an image, or *just* an FMAP?)" :
189 " (did something truncate this file?)");
190 partitioned_file_close(file);
191 return NULL;
194 const struct fmap_area *fmap_fmap_entry =
195 fmap_find_area(file->fmap, SECTION_NAME_FMAP);
197 if (!fmap_fmap_entry) {
198 partitioned_file_close(file);
199 return NULL;
202 if ((long)fmap_fmap_entry->offset != fmap_region_offset) {
203 ERROR("FMAP's '%s' section doesn't point back to FMAP start (did something corrupt this file?)\n",
204 SECTION_NAME_FMAP);
205 partitioned_file_close(file);
206 return NULL;
209 return file;
212 bool partitioned_file_write_region(partitioned_file_t *file,
213 const struct buffer *buffer)
215 assert(file);
216 assert(file->stream);
217 assert(buffer);
218 assert(buffer->data);
220 if (buffer->data - buffer->offset != file->buffer.data) {
221 ERROR("Attempted to write a partition buffer back to a different file than it came from\n");
222 return false;
224 if (buffer->offset + buffer->size > file->buffer.size) {
225 ERROR("Attempted to write data off the end of image file\n");
226 return false;
229 if (fseek(file->stream, buffer->offset, SEEK_SET)) {
230 ERROR("Failed to seek within image file\n");
231 return false;
233 if (!fwrite(buffer->data, buffer->size, 1, file->stream)) {
234 ERROR("Failed to write to image file\n");
235 return false;
237 return true;
240 bool partitioned_file_read_region(struct buffer *dest,
241 const partitioned_file_t *file, const char *region)
243 assert(dest);
244 assert(file);
245 assert(file->buffer.data);
246 assert(region);
248 if (file->fmap) {
249 const struct fmap_area *area = fmap_find_area(file->fmap,
250 region);
251 if (!area) {
252 ERROR("Image is missing '%s' region\n", region);
253 return false;
255 if (area->offset + area->size > file->buffer.size) {
256 ERROR("Region '%s' runs off the end of the image file\n",
257 region);
258 return false;
260 buffer_splice(dest, &file->buffer, area->offset, area->size);
261 } else {
262 if (strcmp(region, SECTION_NAME_PRIMARY_CBFS) != 0) {
263 ERROR("This is a legacy image that contains only a CBFS\n");
264 return false;
266 buffer_clone(dest, &file->buffer);
269 return true;
272 void partitioned_file_close(partitioned_file_t *file)
274 if (!file)
275 return;
277 file->fmap = NULL;
278 buffer_delete(&file->buffer);
279 if (file->stream) {
280 fclose(file->stream);
281 file->stream = NULL;
283 free(file);
286 bool partitioned_file_is_partitioned(const partitioned_file_t *file)
288 return partitioned_file_get_fmap(file) != NULL;
291 size_t partitioned_file_total_size(const partitioned_file_t *file)
293 assert(file);
295 return file->buffer.size;
298 bool partitioned_file_region_check_magic(const partitioned_file_t *file,
299 const char *region, const char *magic, size_t magic_len)
301 struct buffer area;
302 return partitioned_file_read_region(&area, file, region) &&
303 buffer_check_magic(&area, magic, magic_len);
306 bool partitioned_file_region_contains_nested(const partitioned_file_t *file,
307 const char *region)
309 assert(file);
310 assert(region);
312 if (!file->fmap)
313 return false;
314 const struct fmap_area *area = fmap_find_area(file->fmap, region);
315 return area && partitioned_file_fmap_count(file,
316 partitioned_file_fmap_select_children_of, area);
319 const struct fmap *partitioned_file_get_fmap(const partitioned_file_t *file)
321 assert(file);
323 return file->fmap;
326 unsigned partitioned_file_fmap_count(const partitioned_file_t *file,
327 partitioned_file_fmap_selector_t callback, const void *arg)
329 assert(file);
330 assert(callback);
332 if (!file->fmap)
333 return 0;
334 return count_selected_fmap_entries(file->fmap, callback, arg);
337 static bool select_all(unused const struct fmap_area *area,
338 unused const void *arg)
340 return true;
342 const partitioned_file_fmap_selector_t partitioned_file_fmap_select_all =
343 select_all;
345 static bool select_children_of(const struct fmap_area *child, const void *arg)
347 assert(child);
348 assert(arg);
350 const struct fmap_area *parent = (const struct fmap_area *)arg;
351 if (child == arg || (child->offset == parent->offset &&
352 child->size == parent->size))
353 return false;
354 return child->offset >= parent->offset &&
355 child->offset + child->size <= parent->offset + parent->size;
357 const partitioned_file_fmap_selector_t
358 partitioned_file_fmap_select_children_of = select_children_of;
360 static bool select_parents_of(const struct fmap_area *parent, const void *arg)
362 return select_children_of((const struct fmap_area *)arg, parent);
364 const partitioned_file_fmap_selector_t partitioned_file_fmap_select_parents_of =
365 select_parents_of;