1 /* read and write binary file "partitions" described by FMAP */
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"
21 struct partitioned_file
{
27 static bool fill_ones_through(struct partitioned_file
*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
)
42 for (unsigned i
= 0; i
< fmap
->nareas
; ++i
) {
43 if (callback(fmap
->areas
+ i
, arg
))
49 static partitioned_file_t
*reopen_flat_file(const char *filename
,
53 struct partitioned_file
*file
= calloc(1, sizeof(*file
));
54 const char *access_mode
;
57 ERROR("Failed to allocate partitioned file structure\n");
61 if (buffer_from_file(&file
->buffer
, filename
)) {
66 access_mode
= write_access
? "rb+" : "rb";
67 file
->stream
= fopen(filename
, access_mode
);
71 partitioned_file_close(file
);
78 partitioned_file_t
*partitioned_file_create_flat(const char *filename
,
83 struct partitioned_file
*file
= calloc(1, sizeof(*file
));
85 ERROR("Failed to allocate partitioned file structure\n");
89 file
->stream
= fopen(filename
, "wb");
96 if (buffer_create(&file
->buffer
, image_size
, filename
)) {
97 partitioned_file_close(file
);
101 if (!fill_ones_through(file
)) {
102 partitioned_file_close(file
);
109 partitioned_file_t
*partitioned_file_create(const char *filename
,
110 struct buffer
*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");
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
);
125 ERROR("Provided FMAP missing '%s' region\n", SECTION_NAME_FMAP
);
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",
136 int fmap_len
= fmap_size(bootstrap_fmap
);
138 ERROR("Unable to determine size of provided FMAP\n");
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
);
148 partitioned_file_t
*file
= partitioned_file_create_flat(filename
,
149 bootstrap_fmap
->size
);
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
);
160 file
->fmap
= (struct fmap
*)(file
->buffer
.data
+ fmap_area
->offset
);
165 partitioned_file_t
*partitioned_file_reopen(const char *filename
,
170 partitioned_file_t
*file
= reopen_flat_file(filename
, write_access
);
174 long fmap_region_offset
= fmap_find((const uint8_t *)file
->buffer
.data
,
176 if (fmap_region_offset
< 0) {
177 INFO("Opening image as a flat file because it doesn't contain any FMAP\n");
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
);
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
);
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",
205 partitioned_file_close(file
);
212 bool partitioned_file_write_region(partitioned_file_t
*file
,
213 const struct buffer
*buffer
)
216 assert(file
->stream
);
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");
224 if (buffer
->offset
+ buffer
->size
> file
->buffer
.size
) {
225 ERROR("Attempted to write data off the end of image file\n");
229 if (fseek(file
->stream
, buffer
->offset
, SEEK_SET
)) {
230 ERROR("Failed to seek within image file\n");
233 if (!fwrite(buffer
->data
, buffer
->size
, 1, file
->stream
)) {
234 ERROR("Failed to write to image file\n");
240 bool partitioned_file_read_region(struct buffer
*dest
,
241 const partitioned_file_t
*file
, const char *region
)
245 assert(file
->buffer
.data
);
249 const struct fmap_area
*area
= fmap_find_area(file
->fmap
,
252 ERROR("Image is missing '%s' region\n", region
);
255 if (area
->offset
+ area
->size
> file
->buffer
.size
) {
256 ERROR("Region '%s' runs off the end of the image file\n",
260 buffer_splice(dest
, &file
->buffer
, area
->offset
, area
->size
);
262 if (strcmp(region
, SECTION_NAME_PRIMARY_CBFS
) != 0) {
263 ERROR("This is a legacy image that contains only a CBFS\n");
266 buffer_clone(dest
, &file
->buffer
);
272 void partitioned_file_close(partitioned_file_t
*file
)
278 buffer_delete(&file
->buffer
);
280 fclose(file
->stream
);
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
)
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
)
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
,
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
)
326 unsigned partitioned_file_fmap_count(const partitioned_file_t
*file
,
327 partitioned_file_fmap_selector_t callback
, const void *arg
)
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
)
342 const partitioned_file_fmap_selector_t partitioned_file_fmap_select_all
=
345 static bool select_children_of(const struct fmap_area
*child
, const void *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
))
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
=