Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright 2014 Imagination Technologies Ltd. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License as |
| 8 | * published by the Free Software Foundation; version 2 of |
| 9 | * the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 15 | */ |
| 16 | |
| 17 | #include <errno.h> |
| 18 | #include <stdint.h> |
| 19 | #include <stdio.h> |
| 20 | #include <stdlib.h> |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 21 | #include <string.h> |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 22 | #include <sys/stat.h> |
| 23 | #include <sys/types.h> |
| 24 | |
| 25 | struct bimg_header { |
| 26 | uint32_t magic; |
| 27 | uint16_t ver_major; |
| 28 | uint16_t ver_minor; |
| 29 | uint32_t data_size; |
| 30 | uint32_t entry_addr; |
| 31 | uint32_t flags; |
| 32 | uint32_t data_crc; |
| 33 | uint32_t crc; |
| 34 | } __attribute__((packed)); |
| 35 | |
| 36 | struct bimg_data_header { |
| 37 | uint32_t size; |
| 38 | uint32_t dest_addr; |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 39 | uint16_t dummy; |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 40 | uint16_t crc; |
| 41 | } __attribute__((packed)); |
| 42 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 43 | struct crc_t { |
| 44 | uint16_t (*crc_f)(uint16_t crc, void *void_buf, size_t size); |
| 45 | uint32_t crc_init; |
| 46 | uint16_t ver_major; |
| 47 | uint16_t ver_minor; |
| 48 | }; |
| 49 | |
| 50 | |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 51 | #define BIMG_MAGIC /* y */ 0xabbadaba /* doo! */ |
| 52 | |
| 53 | #define BIMG_OP_MASK (0xf << 0) |
| 54 | #define BIMG_OP_EXEC_RETURN (0x1 << 0) |
| 55 | #define BIMG_OP_EXEC_NO_RETURN (0x2 << 0) |
| 56 | #define BIMG_DATA_CHECKSUM (0x1 << 4) |
| 57 | |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 58 | /* Typical use case for this utility. */ |
| 59 | #define BIMG_FLAGS (BIMG_OP_EXEC_NO_RETURN | BIMG_DATA_CHECKSUM) |
| 60 | |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 61 | #define MAX_RECORD_BYTES 0x8000 |
| 62 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 63 | #define CRC_16 |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 64 | |
| 65 | #define error(msg...) fprintf(stderr, "ERROR: " msg) |
| 66 | |
| 67 | #define error_ret(ret, msg...) { \ |
| 68 | error(msg); \ |
| 69 | return ret; \ |
| 70 | } |
| 71 | |
| 72 | static uint16_t crc_x25(uint16_t crc, void *void_buf, size_t size) |
| 73 | { |
| 74 | static const uint16_t crc_table[16] = { |
| 75 | 0x0000, 0x1021, 0x2042, 0x3063, |
| 76 | 0x4084, 0x50a5, 0x60c6, 0x70e7, |
| 77 | 0x8108, 0x9129, 0xa14a, 0xb16b, |
| 78 | 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, |
| 79 | }; |
| 80 | uint8_t *buf, data; |
| 81 | |
| 82 | for (buf = void_buf; size; size--) { |
| 83 | data = *buf++; |
| 84 | crc = (crc << 4) ^ crc_table[((crc >> 12) ^ (data >> 4)) & 0xf]; |
| 85 | crc = (crc << 4) ^ crc_table[((crc >> 12) ^ (data >> 0)) & 0xf]; |
| 86 | } |
| 87 | |
| 88 | return crc; |
| 89 | } |
| 90 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 91 | static uint16_t crc_16(uint16_t crc, void *void_buf, size_t size) |
| 92 | { |
| 93 | /* |
| 94 | * CRC table for the CRC-16. |
| 95 | * The poly is 0x8005 (x^16 + x^15 + x^2 + 1) |
| 96 | */ |
| 97 | static const uint16_t crc16_table[256] = { |
| 98 | 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, |
| 99 | 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, |
| 100 | 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, |
| 101 | 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, |
| 102 | 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, |
| 103 | 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, |
| 104 | 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, |
| 105 | 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, |
| 106 | 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, |
| 107 | 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, |
| 108 | 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, |
| 109 | 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, |
| 110 | 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, |
| 111 | 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, |
| 112 | 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, |
| 113 | 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, |
| 114 | 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, |
| 115 | 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, |
| 116 | 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, |
| 117 | 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, |
| 118 | 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, |
| 119 | 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, |
| 120 | 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, |
| 121 | 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, |
| 122 | 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, |
| 123 | 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, |
| 124 | 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, |
| 125 | 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, |
| 126 | 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, |
| 127 | 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, |
| 128 | 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, |
| 129 | 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 |
| 130 | }; |
| 131 | uint8_t *buf, data; |
| 132 | |
| 133 | for (buf = void_buf; size; size--) { |
| 134 | data = *buf++; |
| 135 | crc = (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; |
| 136 | } |
| 137 | |
| 138 | return crc; |
| 139 | |
| 140 | } |
| 141 | |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 142 | static const struct crc_t crc_type = { |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 143 | #if defined(CRC_16) |
| 144 | .crc_f = crc_16, |
| 145 | .crc_init = 0, |
| 146 | .ver_major = 2, |
| 147 | .ver_minor = 0 |
| 148 | #elif defined(CRC_X25) |
| 149 | .crc_f = crc_x25, |
| 150 | .crc_init = 0xffff, |
| 151 | .ver_major = 1, |
| 152 | .ver_minor = 0 |
| 153 | #endif |
| 154 | }; |
| 155 | |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 156 | static int write_binary(FILE *out, FILE *in, struct bimg_header *hdr) |
| 157 | { |
| 158 | static uint8_t file_buf[MAX_RECORD_BYTES]; |
Patrick Georgi | 2d119a3 | 2015-06-12 11:04:49 +0200 | [diff] [blame] | 159 | struct bimg_data_header data_hdr = { 0 }; |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 160 | size_t n_written; |
| 161 | |
| 162 | data_hdr.dest_addr = hdr->entry_addr; |
| 163 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 164 | /* |
| 165 | * The read binary data has to be split in chunks of max 64KiB - 1 byte |
| 166 | * (SPI controller limitation). Each chunk will have its own header in |
| 167 | * order to respect the BIMG format. |
| 168 | */ |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 169 | while ((data_hdr.size = fread(file_buf, 1, sizeof(file_buf), in))) { |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 170 | data_hdr.crc = crc_type.crc_f(crc_type.crc_init, &data_hdr, |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 171 | sizeof(data_hdr) - sizeof(data_hdr.crc)); |
| 172 | |
| 173 | if (fwrite(&data_hdr, sizeof(data_hdr), 1, out) != 1) |
| 174 | error_ret(-EIO, "Failed to write data header: %d\n", |
| 175 | errno); |
| 176 | |
| 177 | n_written = fwrite(file_buf, 1, data_hdr.size, out); |
| 178 | if (n_written != data_hdr.size) |
| 179 | error_ret(-EIO, "Failed to write to output file: %d\n", |
| 180 | errno); |
| 181 | |
| 182 | data_hdr.dest_addr += n_written; |
| 183 | hdr->data_size += sizeof(data_hdr) + n_written; |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 184 | hdr->data_crc = crc_type.crc_f(hdr->data_crc, |
| 185 | file_buf, n_written); |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | if (ferror(in)) |
| 189 | error_ret(-EIO, "Failed to read input file\n"); |
| 190 | |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | static int write_final(FILE *out, struct bimg_header *hdr) |
| 195 | { |
| 196 | struct bimg_data_header data_hdr = { |
| 197 | .size = 0, |
| 198 | .dest_addr = ~0, |
| 199 | }; |
| 200 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 201 | data_hdr.crc = crc_type.crc_f(crc_type.crc_init, &data_hdr, |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 202 | sizeof(data_hdr) - sizeof(data_hdr.crc)); |
| 203 | |
| 204 | if (fwrite(&data_hdr, sizeof(data_hdr), 1, out) != 1) |
| 205 | error_ret(-EIO, "Failed to write data header: %d\n", errno); |
| 206 | |
| 207 | hdr->data_size += sizeof(data_hdr); |
| 208 | |
| 209 | return 0; |
| 210 | } |
| 211 | |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 212 | static const char *help_message = |
| 213 | "Usage: bimgtool <input> [<output> <base-address>]\n" |
| 214 | "\n" |
| 215 | "This is a simple tool which generates and verifies boot images in\n" |
| 216 | "the BIMG format, used in systems designed by Imagination\n" |
| 217 | "Technologies, for example the Pistachio SoC. This version of the\n" |
| 218 | "tool works with BIMG images version %d.\n" |
| 219 | "\n" |
| 220 | " input: The binary file to be converted to a BIMG\n" |
| 221 | " or verified\n" |
| 222 | " output: The name of the output BIMG file\n" |
| 223 | " base-address: The address in memory at which you wish the " |
| 224 | " input binary to be loaded.\n"; |
| 225 | |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 226 | static void usage(FILE *f) |
| 227 | { |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 228 | fprintf(f, help_message, crc_type.ver_major); |
| 229 | } |
| 230 | |
| 231 | static int verify_file(FILE *f) |
| 232 | { |
| 233 | struct bimg_header file_header; |
| 234 | struct bimg_data_header data_header; |
| 235 | char *file_pointer; |
| 236 | char *file_data; |
| 237 | struct stat buf; |
| 238 | int data_size; |
| 239 | int fd = fileno(f); |
| 240 | uint32_t data_crc = crc_type.crc_init; |
| 241 | uint32_t crc_result; |
| 242 | |
| 243 | if (fread(&file_header, 1, sizeof(struct bimg_header), f) != |
| 244 | sizeof(struct bimg_header)) { |
| 245 | perror("Problems trying to read input file header\n"); |
| 246 | return -1; |
| 247 | } |
| 248 | |
| 249 | if (fstat(fd, &buf)) { |
| 250 | perror("Problems trying to stat input file\n"); |
| 251 | return -1; |
| 252 | } |
| 253 | |
| 254 | if (file_header.magic != BIMG_MAGIC) { |
| 255 | fprintf(stderr, "Wrong magic value %#x\n", file_header.magic); |
| 256 | return -1; |
| 257 | } |
| 258 | |
| 259 | crc_result = crc_type.crc_f(crc_type.crc_init, &file_header, |
| 260 | sizeof(file_header) - |
| 261 | sizeof(file_header.crc)); |
| 262 | if (file_header.crc != crc_result) { |
| 263 | fprintf(stderr, "File header CRC mismatch\n"); |
| 264 | return -1; |
| 265 | } |
| 266 | |
| 267 | if ((file_header.data_size + sizeof(struct bimg_header)) > |
| 268 | buf.st_size) { |
| 269 | fprintf(stderr, "Data size too big: %d > %d\n", |
| 270 | file_header.data_size, buf.st_size); |
| 271 | return -1; |
| 272 | } |
| 273 | |
| 274 | if (file_header.ver_major != crc_type.ver_major) { |
| 275 | fprintf(stderr, "Image version mismatch: %d\n", |
| 276 | file_header.ver_major); |
| 277 | return -1; |
| 278 | } |
| 279 | |
| 280 | if ((file_header.flags & BIMG_FLAGS) != BIMG_FLAGS) { |
| 281 | fprintf(stderr, "Unexpected file header flags: %#x\n", |
| 282 | file_header.flags); |
| 283 | return -1; |
| 284 | } |
| 285 | |
| 286 | if (file_header.ver_minor != crc_type.ver_minor) { |
| 287 | fprintf(stderr, |
| 288 | "Minor version mismatch: %d, will try anyways\n", |
| 289 | file_header.ver_minor); |
| 290 | } |
| 291 | |
| 292 | data_size = file_header.data_size; |
| 293 | file_pointer = malloc(data_size); |
| 294 | if (!file_pointer) { |
| 295 | fprintf(stderr, "Failed to allocate %d bytes\n", |
| 296 | file_header.data_size); |
| 297 | return -1; |
| 298 | } |
| 299 | |
| 300 | if (fread(file_pointer, 1, data_size, f) != data_size) { |
| 301 | fprintf(stderr, "Failed to read %d bytes\n", data_size); |
| 302 | free(file_pointer); |
| 303 | return -1; |
| 304 | } |
| 305 | |
| 306 | file_data = file_pointer; |
| 307 | while (data_size > 0) { |
| 308 | memcpy(&data_header, file_data, sizeof(data_header)); |
| 309 | |
| 310 | /* Check the data block header integrity. */ |
| 311 | crc_result = crc_type.crc_f(crc_type.crc_init, &data_header, |
| 312 | sizeof(data_header) - |
| 313 | sizeof(data_header.crc)); |
| 314 | if (data_header.crc != crc_result) { |
| 315 | fprintf(stderr, "Data header CRC mismatch at %d\n", |
| 316 | file_header.data_size - data_size); |
| 317 | free(file_pointer); |
| 318 | return -1; |
| 319 | } |
| 320 | |
| 321 | /* |
| 322 | * Add the block data to the CRC stream, the last block size |
| 323 | * will be zero. |
| 324 | */ |
| 325 | file_data += sizeof(data_header); |
| 326 | data_crc = crc_type.crc_f(data_crc, |
| 327 | file_data, data_header.size); |
| 328 | |
| 329 | data_size -= data_header.size + sizeof(data_header); |
| 330 | file_data += data_header.size; |
| 331 | } |
| 332 | |
| 333 | if (data_size) { |
| 334 | fprintf(stderr, "File size mismatch\n"); |
| 335 | free(file_pointer); |
| 336 | return -1; |
| 337 | } |
| 338 | |
| 339 | if (data_crc != file_header.data_crc) { |
| 340 | fprintf(stderr, "File data CRC mismatch\n"); |
| 341 | free(file_pointer); |
| 342 | return -1; |
| 343 | } |
| 344 | |
| 345 | free(file_pointer); |
| 346 | return 0; |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 347 | } |
| 348 | |
| 349 | int main(int argc, char *argv[]) |
| 350 | { |
| 351 | const char *in_filename, *out_filename; |
| 352 | FILE *in_file, *out_file; |
| 353 | int err; |
| 354 | struct bimg_header hdr = { |
| 355 | .magic = BIMG_MAGIC, |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 356 | .ver_major = crc_type.ver_major, |
| 357 | .ver_minor = crc_type.ver_minor, |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 358 | .flags = BIMG_FLAGS, |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 359 | .data_crc = crc_type.crc_init, |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 360 | }; |
| 361 | |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 362 | if ((argc != 4) && (argc != 2)) { |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 363 | usage(stderr); |
| 364 | goto out_err; |
| 365 | } |
| 366 | |
| 367 | in_filename = argv[1]; |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 368 | |
| 369 | in_file = fopen(in_filename, "r"); |
| 370 | if (!in_file) { |
| 371 | error("Failed to open input file '%s'\n", in_filename); |
| 372 | goto out_err; |
| 373 | } |
| 374 | |
Vadim Bendebury | f44319c | 2014-11-04 16:17:33 -0800 | [diff] [blame] | 375 | if (argc == 2) |
| 376 | return verify_file(in_file); |
| 377 | |
| 378 | out_filename = argv[2]; |
| 379 | hdr.entry_addr = strtoul(argv[3], NULL, 16); |
| 380 | |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 381 | out_file = fopen(out_filename, "w"); |
| 382 | if (!out_file) { |
| 383 | error("Failed to open output file '%s'\n", out_filename); |
| 384 | goto out_err_close_in; |
| 385 | } |
| 386 | |
| 387 | if (fseek(out_file, sizeof(hdr), SEEK_SET)) { |
| 388 | error("Failed to seek past header: %d\n", errno); |
| 389 | goto out_err_close_out; |
| 390 | } |
| 391 | |
| 392 | err = write_binary(out_file, in_file, &hdr); |
| 393 | if (err) { |
| 394 | error("Failed to write binary: %d\n", err); |
| 395 | goto out_err_close_out; |
| 396 | } |
| 397 | |
| 398 | err = write_final(out_file, &hdr); |
| 399 | if (err) { |
| 400 | error("Failed to write final record: %d\n", err); |
| 401 | goto out_err_close_out; |
| 402 | } |
| 403 | |
Ionela Voinescu | a545778 | 2014-10-31 17:21:41 +0000 | [diff] [blame] | 404 | hdr.crc = crc_type.crc_f(crc_type.crc_init, &hdr, |
| 405 | sizeof(hdr) - sizeof(hdr.crc)); |
Paul Burton | b1688ca | 2014-06-14 00:09:49 +0100 | [diff] [blame] | 406 | |
| 407 | if (fseek(out_file, 0, SEEK_SET)) { |
| 408 | error("Failed to seek to header: %d\n", errno); |
| 409 | goto out_err_close_out; |
| 410 | } |
| 411 | |
| 412 | if (fwrite(&hdr, sizeof(hdr), 1, out_file) != 1) { |
| 413 | error("Failed to write header: %d\n", errno); |
| 414 | goto out_err_close_out; |
| 415 | } |
| 416 | |
| 417 | fclose(in_file); |
| 418 | fclose(out_file); |
| 419 | return EXIT_SUCCESS; |
| 420 | |
| 421 | out_err_close_out: |
| 422 | fclose(out_file); |
| 423 | out_err_close_in: |
| 424 | fclose(in_file); |
| 425 | out_err: |
| 426 | return EXIT_FAILURE; |
| 427 | } |