1 /* read and write binary file "partitions" described by FMAP */
2 /* SPDX-License-Identifier: GPL-2.0-only */
4 #define __BSD_VISIBLE 1
6 #include "partitioned_file.h"
8 #include "cbfs_sections.h"
15 struct partitioned_file
{
21 static bool fill_ones_through(struct partitioned_file
*file
)
25 memset(file
->buffer
.data
, 0xff, file
->buffer
.size
);
26 return partitioned_file_write_region(file
, &file
->buffer
);
29 static unsigned count_selected_fmap_entries(const struct fmap
*fmap
,
30 partitioned_file_fmap_selector_t callback
, const void *arg
)
36 for (unsigned i
= 0; i
< fmap
->nareas
; ++i
) {
37 if (callback(fmap
->areas
+ i
, arg
))
43 static partitioned_file_t
*reopen_flat_file(const char *filename
,
47 struct partitioned_file
*file
= calloc(1, sizeof(*file
));
48 const char *access_mode
;
51 ERROR("Failed to allocate partitioned file structure\n");
55 if (buffer_from_file(&file
->buffer
, filename
)) {
60 access_mode
= write_access
? "rb+" : "rb";
61 file
->stream
= fopen(filename
, access_mode
);
63 if (!file
->stream
|| flock(fileno(file
->stream
), LOCK_EX
)) {
65 partitioned_file_close(file
);
72 partitioned_file_t
*partitioned_file_create_flat(const char *filename
,
77 struct partitioned_file
*file
= calloc(1, sizeof(*file
));
79 ERROR("Failed to allocate partitioned file structure\n");
83 file
->stream
= fopen(filename
, "wb");
84 if (!file
->stream
|| flock(fileno(file
->stream
), LOCK_EX
)) {
90 if (buffer_create(&file
->buffer
, image_size
, filename
)) {
91 partitioned_file_close(file
);
95 if (!fill_ones_through(file
)) {
96 partitioned_file_close(file
);
103 partitioned_file_t
*partitioned_file_create(const char *filename
,
104 struct buffer
*flashmap
)
108 assert(flashmap
->data
);
110 if (fmap_find((const uint8_t *)flashmap
->data
, flashmap
->size
) != 0) {
111 ERROR("Attempted to create a partitioned image out of something that isn't an FMAP\n");
114 struct fmap
*bootstrap_fmap
= (struct fmap
*)flashmap
->data
;
116 const struct fmap_area
*fmap_area
=
117 fmap_find_area(bootstrap_fmap
, SECTION_NAME_FMAP
);
119 ERROR("Provided FMAP missing '%s' region\n", SECTION_NAME_FMAP
);
123 if (count_selected_fmap_entries(bootstrap_fmap
,
124 partitioned_file_fmap_select_children_of
, fmap_area
)) {
125 ERROR("Provided FMAP's '%s' region contains other regions\n",
130 int fmap_len
= fmap_size(bootstrap_fmap
);
132 ERROR("Unable to determine size of provided FMAP\n");
135 assert((size_t)fmap_len
<= flashmap
->size
);
136 if ((uint32_t)fmap_len
> fmap_area
->size
) {
137 ERROR("Provided FMAP's '%s' region needs to be at least %d bytes\n",
138 SECTION_NAME_FMAP
, fmap_len
);
142 partitioned_file_t
*file
= partitioned_file_create_flat(filename
,
143 bootstrap_fmap
->size
);
147 struct buffer fmap_region
;
148 buffer_splice(&fmap_region
, &file
->buffer
, fmap_area
->offset
, fmap_area
->size
);
149 memcpy(fmap_region
.data
, bootstrap_fmap
, fmap_len
);
150 if (!partitioned_file_write_region(file
, &fmap_region
)) {
151 partitioned_file_close(file
);
154 file
->fmap
= (struct fmap
*)(file
->buffer
.data
+ fmap_area
->offset
);
159 partitioned_file_t
*partitioned_file_reopen(const char *filename
,
164 partitioned_file_t
*file
= reopen_flat_file(filename
, write_access
);
168 long fmap_region_offset
= fmap_find((const uint8_t *)file
->buffer
.data
,
170 if (fmap_region_offset
< 0) {
171 INFO("Opening image as a flat file because it doesn't contain any FMAP\n");
174 file
->fmap
= (struct fmap
*)(file
->buffer
.data
+ fmap_region_offset
);
176 if (file
->fmap
->size
> file
->buffer
.size
) {
177 int fmap_region_size
= fmap_size(file
->fmap
);
178 ERROR("FMAP records image size as %u, but file is only %zu bytes%s\n",
179 file
->fmap
->size
, file
->buffer
.size
,
180 fmap_region_offset
== 0 &&
181 (signed)file
->buffer
.size
== fmap_region_size
?
182 " (is it really an image, or *just* an FMAP?)" :
183 " (did something truncate this file?)");
184 partitioned_file_close(file
);
188 const struct fmap_area
*fmap_fmap_entry
=
189 fmap_find_area(file
->fmap
, SECTION_NAME_FMAP
);
191 if (!fmap_fmap_entry
) {
192 partitioned_file_close(file
);
196 if ((long)fmap_fmap_entry
->offset
!= fmap_region_offset
) {
197 ERROR("FMAP's '%s' section doesn't point back to FMAP start (did something corrupt this file?)\n",
199 partitioned_file_close(file
);
206 bool partitioned_file_write_region(partitioned_file_t
*file
,
207 const struct buffer
*buffer
)
210 assert(file
->stream
);
212 assert(buffer
->data
);
214 if (buffer
->data
- buffer
->offset
!= file
->buffer
.data
) {
215 ERROR("Attempted to write a partition buffer back to a different file than it came from\n");
218 if (buffer
->offset
+ buffer
->size
> file
->buffer
.size
) {
219 ERROR("Attempted to write data off the end of image file\n");
223 if (fseek(file
->stream
, buffer
->offset
, SEEK_SET
)) {
224 ERROR("Failed to seek within image file\n");
227 if (!fwrite(buffer
->data
, buffer
->size
, 1, file
->stream
)) {
228 ERROR("Failed to write to image file\n");
234 bool partitioned_file_read_region(struct buffer
*dest
,
235 const partitioned_file_t
*file
, const char *region
)
239 assert(file
->buffer
.data
);
243 const struct fmap_area
*area
= fmap_find_area(file
->fmap
,
246 ERROR("Image is missing '%s' region\n", region
);
249 if (area
->offset
+ area
->size
> file
->buffer
.size
) {
250 ERROR("Region '%s' runs off the end of the image file\n",
254 buffer_splice(dest
, &file
->buffer
, area
->offset
, area
->size
);
256 if (strcmp(region
, SECTION_NAME_PRIMARY_CBFS
) != 0) {
257 ERROR("This is a legacy image that contains only a CBFS\n");
260 buffer_clone(dest
, &file
->buffer
);
266 void partitioned_file_close(partitioned_file_t
*file
)
272 buffer_delete(&file
->buffer
);
274 flock(fileno(file
->stream
), LOCK_UN
);
275 fclose(file
->stream
);
281 bool partitioned_file_is_partitioned(const partitioned_file_t
*file
)
283 return partitioned_file_get_fmap(file
) != NULL
;
286 size_t partitioned_file_total_size(const partitioned_file_t
*file
)
290 return file
->buffer
.size
;
293 bool partitioned_file_region_check_magic(const partitioned_file_t
*file
,
294 const char *region
, const char *magic
, size_t magic_len
)
297 return partitioned_file_read_region(&area
, file
, region
) &&
298 buffer_check_magic(&area
, magic
, magic_len
);
301 bool partitioned_file_region_contains_nested(const partitioned_file_t
*file
,
309 const struct fmap_area
*area
= fmap_find_area(file
->fmap
, region
);
310 return area
&& partitioned_file_fmap_count(file
,
311 partitioned_file_fmap_select_children_of
, area
);
314 const struct fmap
*partitioned_file_get_fmap(const partitioned_file_t
*file
)
321 unsigned partitioned_file_fmap_count(const partitioned_file_t
*file
,
322 partitioned_file_fmap_selector_t callback
, const void *arg
)
329 return count_selected_fmap_entries(file
->fmap
, callback
, arg
);
332 static bool select_all(unused
const struct fmap_area
*area
,
333 unused
const void *arg
)
337 const partitioned_file_fmap_selector_t partitioned_file_fmap_select_all
=
340 static bool select_children_of(const struct fmap_area
*child
, const void *arg
)
345 const struct fmap_area
*parent
= (const struct fmap_area
*)arg
;
346 if (child
== arg
|| (child
->offset
== parent
->offset
&&
347 child
->size
== parent
->size
))
349 return child
->offset
>= parent
->offset
&&
350 child
->offset
+ child
->size
<= parent
->offset
+ parent
->size
;
352 const partitioned_file_fmap_selector_t
353 partitioned_file_fmap_select_children_of
= select_children_of
;
355 static bool select_parents_of(const struct fmap_area
*parent
, const void *arg
)
357 return select_children_of((const struct fmap_area
*)arg
, parent
);
359 const partitioned_file_fmap_selector_t partitioned_file_fmap_select_parents_of
=