X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/89df7db876c01d73f17cda5ecf810a775e15bc03..a143751bb56550e535784929e4e6402bdc4b959c:/tools/firmware-utils/src/mkcsysimg.c diff --git a/tools/firmware-utils/src/mkcsysimg.c b/tools/firmware-utils/src/mkcsysimg.c index 993ac07fe..4f2352a60 100644 --- a/tools/firmware-utils/src/mkcsysimg.c +++ b/tools/firmware-utils/src/mkcsysimg.c @@ -1,9 +1,8 @@ /* - * $Id$ * - * Copyright (C) 2007 Gabor Juhos + * Copyright (C) 2007-2009 Gabor Juhos * - * This program was based on the code found in various Linux + * This program was based on the code found in various Linux * source tarballs released by Edimax for it's devices. * Original author: David Hsu * @@ -52,8 +51,6 @@ # define LE32_TO_HOST(x) bswap_32(x) #endif -#define ALIGN(x,y) ((x)+((y)-1)) & ~((y)-1) - #define MAX_NUM_BLOCKS 8 #define MAX_ARG_COUNT 32 #define MAX_ARG_LEN 1024 @@ -66,6 +63,12 @@ #define BLOCK_TYPE_CODE 3 #define BLOCK_TYPE_XTRA 4 +#define DEFAULT_BLOCK_ALIGN 0x10000U + +#define CSUM_SIZE_NONE 0 +#define CSUM_SIZE_8 1 +#define CSUM_SIZE_16 2 + struct csum_state{ int size; @@ -82,17 +85,18 @@ struct csys_block { char *file_name; /* name of the file */ uint32_t file_size; /* length of the file */ - uint32_t size; - int size_set; + unsigned char sig[SIG_LEN]; + uint32_t addr; + int addr_set; + uint32_t align; + int align_set; uint8_t padc; + uint32_t size; uint32_t size_hdr; uint32_t size_csum; uint32_t size_avail; - unsigned char sig[SIG_LEN]; - uint32_t addr; - int addr_set; struct csum_state *css; }; @@ -111,7 +115,7 @@ struct board_info { uint32_t webp_size; uint32_t webp_size_max; uint32_t code_size; - + uint32_t addr_code; uint32_t addr_webp; }; @@ -123,7 +127,7 @@ struct board_info { .webp_size = ws, .webp_size_max = 3*0x10000, \ .addr_code = ac, .addr_webp = aw \ } - + #define BOARD_ADM(m,n,f, sigw) BOARD(m,n,f, ADM_BOOT_SIG, sigw, \ ADM_BOOT_SIZE, ADM_CONF_SIZE, ADM_WEBP_SIZE, \ ADM_CODE_ADDR, ADM_WEBP_ADDR) @@ -135,7 +139,8 @@ struct board_info { char *progname; char *ofname = NULL; int verblevel = 0; -int size_check = 1; +int invalid_causes_error = 1; +int keep_invalid_images = 0; struct board_info *board = NULL; @@ -147,11 +152,11 @@ struct csys_block *code_block = NULL; struct csys_block blocks[MAX_NUM_BLOCKS]; int num_blocks = 0; - static struct board_info boards[] = { /* The original Edimax products */ BOARD_ADM("BR-6104K", "Edimax BR-6104K", 2, SIG_BR6104K), BOARD_ADM("BR-6104KP", "Edimax BR-6104KP", 2, SIG_BR6104KP), + BOARD_ADM("BR-6104Wg", "Edimax BR-6104Wg", 2, SIG_BR6104Wg), BOARD_ADM("BR-6114WG", "Edimax BR-6114WG", 2, SIG_BR6114WG), BOARD_ADM("BR-6524K", "Edimax BR-6524K", 2, SIG_BR6524K), BOARD_ADM("BR-6524KP", "Edimax BR-6524KP", 2, SIG_BR6524KP), @@ -172,49 +177,43 @@ static struct board_info boards[] = { /* Planet products */ BOARD_ADM("XRT-401D", "Planet XRT-401D", 2, SIG_XRT401D), BOARD_ADM("XRT-402D", "Planet XRT-402D", 2, SIG_XRT402D), - + + /* Conceptronic products */ + BOARD_ADM("C54BSR4", "Conceptronic C54BSR4", 2, SIG_C54BSR4), + + /* OSBRiDGE products */ + BOARD_ADM("5GXi", "OSBDRiDGE 5GXi", 2, SIG_5GXI), + {.model = NULL} }; /* - * Helper routines + * Message macros */ -void -errmsgv(int syserr, const char *fmt, va_list arg_ptr) -{ - int save = errno; - - fflush(0); - fprintf(stderr, "%s: error, ", progname); - vfprintf(stderr, fmt, arg_ptr); - if (syserr != 0) { - fprintf(stderr, " (%s)", strerror(save)); - } - fprintf(stderr, "\n"); -} - -void -errmsg(int syserr, const char *fmt, ...) -{ - va_list arg_ptr; - va_start(arg_ptr, fmt); - errmsgv(syserr, fmt, arg_ptr); - va_end(arg_ptr); -} - -void -dbgmsg(int level, const char *fmt, ...) -{ - va_list arg_ptr; - if (verblevel >= level) { - fflush(0); - va_start(arg_ptr, fmt); - vfprintf(stderr, fmt, arg_ptr); - fprintf(stderr, "\n"); - va_end(arg_ptr); - } -} - +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ \ + , strerror(save)); \ +} while (0) + +#define WARN(fmt, ...) do { \ + fprintf(stderr, "[%s] *** warning: " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define DBG(lev, fmt, ...) do { \ + if (verblevel < lev) \ + break;\ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERR_FATAL -1 +#define ERR_INVALID_IMAGE -2 void usage(int status) @@ -235,17 +234,17 @@ usage(int status) board->model, board->name); }; fprintf(stream, -" -d disable check for available space\n" -" add boot code to the image\n" -" -b [:[:]]\n" +" -d don't throw error on invalid images\n" +" -k keep invalid images\n" +" -b [:[:]]\n" " add boot code to the image\n" -" -c [:[:]]\n" +" -c [:[:]]\n" " add configuration settings to the image\n" -" -r :[][:[:]]\n" +" -r :[][:[:]]\n" " add runtime code to the image\n" -" -w [:[][:[:]]]\n" +" -w [:[][:[:]]]\n" " add webpages to the image\n" -" -x [:[:]]\n" +" -x [:[:]]\n" " add extra data at the end of the image\n" " -h show this screen\n" "Parameters:\n" @@ -255,6 +254,19 @@ usage(int status) exit(status); } +static inline uint32_t align(uint32_t base, uint32_t alignment) +{ + uint32_t ret; + + if (alignment) { + ret = (base + alignment - 1); + ret &= ~(alignment-1); + } else { + ret = base; + } + + return ret; +} /* * argument parsing @@ -264,7 +276,7 @@ str2u32(char *arg, uint32_t *val) { char *err = NULL; uint32_t t; - + errno=0; t = strtoul(arg, &err, 0); if (errno || (err==arg) || ((err != NULL) && *err)) { @@ -287,7 +299,7 @@ str2u16(char *arg, uint16_t *val) if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { return -1; } - + *val = t & 0xFFFF; return 0; } @@ -303,7 +315,7 @@ str2u8(char *arg, uint8_t *val) if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) { return -1; } - + *val = t & 0xFF; return 0; } @@ -315,7 +327,7 @@ str2sig(char *arg, uint32_t *sig) return -1; *sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24); - + return 0; } @@ -374,8 +386,8 @@ required_arg(char c, char *arg) if (arg == NULL || *arg != '-') return 0; - errmsg(0,"option -%c requires an argument\n", c); - return -1; + ERR("option -%c requires an argument\n", c); + return ERR_FATAL; } @@ -403,7 +415,7 @@ uint16_t csum8_get(struct csum_state *css) { uint8_t t; - + t = css->val; return ~t + 1; } @@ -413,7 +425,7 @@ void csum16_update(uint8_t *p, uint32_t len, struct csum_state *css) { uint16_t t; - + if (css->odd) { t = css->tmp + (p[0]<<8); css->val += LE16_TO_HOST(t); @@ -421,7 +433,7 @@ csum16_update(uint8_t *p, uint32_t len, struct csum_state *css) len--; p++; } - + for ( ; len > 1; len -= 2, p +=2 ) { t = p[0] + (p[1] << 8); css->val += LE16_TO_HOST(t); @@ -458,10 +470,10 @@ void csum_update(uint8_t *p, uint32_t len, struct csum_state *css) { switch (css->size) { - case 1: + case CSUM_SIZE_8: csum8_update(p,len,css); break; - case 2: + case CSUM_SIZE_16: csum16_update(p,len,css); break; } @@ -474,10 +486,10 @@ csum_get(struct csum_state *css) uint16_t ret; switch (css->size) { - case 1: + case CSUM_SIZE_8: ret = csum8_get(css); break; - case 2: + case CSUM_SIZE_16: ret = csum16_get(css); break; } @@ -497,8 +509,8 @@ write_out_data(FILE *outfile, uint8_t *data, size_t len, fwrite(data, len, 1, outfile); if (errno) { - errmsg(1,"unable to write output file"); - return -1; + ERRS("unable to write output file"); + return ERR_FATAL; } if (css) { @@ -515,15 +527,17 @@ write_out_padding(FILE *outfile, size_t len, uint8_t padc, { uint8_t buf[512]; size_t buflen = sizeof(buf); + int err; memset(buf, padc, buflen); while (len > 0) { if (len < buflen) buflen = len; - if (write_out_data(outfile, buf, buflen, css)) - return -1; - + err = write_out_data(outfile, buf, buflen, css); + if (err) + return err; + len -= buflen; } @@ -535,15 +549,15 @@ int block_stat_file(struct csys_block *block) { struct stat st; - int res; + int err; if (block->file_name == NULL) return 0; - res = stat(block->file_name, &st); - if (res){ - errmsg(1, "stat failed on %s", block->file_name); - return res; + err = stat(block->file_name, &st); + if (err){ + ERRS("stat failed on %s", block->file_name); + return ERR_FATAL; } block->file_size = st.st_size; @@ -563,9 +577,9 @@ block_writeout_hdr(FILE *outfile, struct csys_block *block) /* setup header fields */ memcpy(hdr.sig, block->sig, 4); hdr.addr = HOST_TO_LE32(block->addr); - hdr.size = HOST_TO_LE32(block->size-block->size_hdr); + hdr.size = HOST_TO_LE32(block->size - block->size_hdr - block->size_csum); - dbgmsg(1,"writing header for block"); + DBG(1,"writing header for block"); res = write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr),NULL); return res; @@ -590,8 +604,8 @@ block_writeout_file(FILE *outfile, struct csys_block *block) errno = 0; f = fopen(block->file_name,"r"); if (errno) { - errmsg(1,"unable to open file: %s", block->file_name); - return -1; + ERRS("unable to open file: %s", block->file_name); + return ERR_FATAL; } len = block->file_size; @@ -603,9 +617,8 @@ block_writeout_file(FILE *outfile, struct csys_block *block) errno = 0; fread(buf, buflen, 1, f); if (errno != 0) { - errmsg(1,"unable to read from file: %s", - block->file_name); - res = -1; + ERRS("unable to read from file: %s", block->file_name); + res = ERR_FATAL; break; } @@ -621,7 +634,7 @@ block_writeout_file(FILE *outfile, struct csys_block *block) } -int +int block_writeout_data(FILE *outfile, struct csys_block *block) { int res; @@ -633,14 +646,14 @@ block_writeout_data(FILE *outfile, struct csys_block *block) /* write padding data if neccesary */ padlen = block->size_avail - block->file_size; - dbgmsg(1,"padding block, length=%d", padlen); + DBG(1,"padding block, length=%d", padlen); res = write_out_padding(outfile, padlen, block->padc, block->css); return res; } -int +int block_writeout_csum(FILE *outfile, struct csys_block *block) { uint16_t csum; @@ -649,7 +662,7 @@ block_writeout_csum(FILE *outfile, struct csys_block *block) if (block->size_csum == 0) return 0; - dbgmsg(1,"writing checksum for block"); + DBG(1,"writing checksum for block"); csum = HOST_TO_LE16(csum_get(block->css)); res = write_out_data(outfile, (uint8_t *)&csum, block->size_csum, NULL); @@ -662,7 +675,7 @@ block_writeout(FILE *outfile, struct csys_block *block) { int res; struct csum_state css; - + res = 0; if (block == NULL) @@ -670,7 +683,7 @@ block_writeout(FILE *outfile, struct csys_block *block) block->css = NULL; - dbgmsg(2, "writing block, file=%s, file_size=%d, space=%d", + DBG(2, "writing block, file=%s, file_size=%d, space=%d", block->file_name, block->file_size, block->size_avail); res = block_writeout_hdr(outfile, block); if (res) @@ -718,15 +731,15 @@ write_out_blocks(FILE *outfile) res = 0; for (i=0; i < num_blocks; i++) { block = &blocks[i]; - + if (block->type != BLOCK_TYPE_XTRA) continue; - + res = block_writeout(outfile, block); if (res) break; } - + return res; } @@ -739,7 +752,7 @@ find_board(char *model) ret = NULL; for (board = boards; board->model != NULL; board++){ - if (strcmp(model, board->model) == 0) { + if (strcasecmp(model, board->model) == 0) { ret = board; break; } @@ -753,20 +766,20 @@ int parse_opt_board(char ch, char *arg) { - dbgmsg(1,"parsing board option: -%c %s", ch, arg); + DBG(1,"parsing board option: -%c %s", ch, arg); if (board != NULL) { - errmsg(0,"only one board option allowed"); - return -1; + ERR("only one board option allowed"); + return ERR_FATAL; } if (required_arg(ch, arg)) - return -1; + return ERR_FATAL; board = find_board(arg); if (board == NULL){ - errmsg(0,"invalid/unknown board specified: %s", arg); - return -1; + ERR("invalid/unknown board specified: %s", arg); + return ERR_FATAL; } return 0; @@ -784,20 +797,20 @@ parse_opt_block(char ch, char *arg) int i; if ( num_blocks > MAX_NUM_BLOCKS ) { - errmsg(0,"too many blocks specified"); - return -1; + ERR("too many blocks specified"); + return ERR_FATAL; } block = &blocks[num_blocks]; - + /* setup default field values */ block->need_file = 1; block->padc = 0xFF; - + switch (ch) { case 'b': if (boot_block) { - errmsg(0,"only one boot block allowed"); + WARN("only one boot block allowed"); break; } block->type = BLOCK_TYPE_BOOT; @@ -805,7 +818,7 @@ parse_opt_block(char ch, char *arg) break; case 'c': if (conf_block) { - errmsg(0,"only one config block allowed"); + WARN("only one config block allowed"); break; } block->type = BLOCK_TYPE_CONF; @@ -813,31 +826,31 @@ parse_opt_block(char ch, char *arg) break; case 'w': if (webp_block) { - errmsg(0,"only one web block allowed"); + WARN("only one web block allowed"); break; } block->type = BLOCK_TYPE_WEBP; block->size_hdr = sizeof(struct csys_header); - block->size_csum = 1; + block->size_csum = CSUM_SIZE_8; block->need_file = 0; webp_block = block; break; case 'r': if (code_block) { - errmsg(0,"only one runtime block allowed"); + WARN("only one runtime block allowed"); break; } block->type = BLOCK_TYPE_CODE; block->size_hdr = sizeof(struct csys_header); - block->size_csum = 2; + block->size_csum = CSUM_SIZE_16; code_block = block; break; case 'x': block->type = BLOCK_TYPE_XTRA; break; default: - errmsg(0,"unknown block type \"%c\"", ch); - return -1; + ERR("unknown block type \"%c\"", ch); + return ERR_FATAL; } argc = parse_arg(arg, buf, argv); @@ -847,20 +860,20 @@ parse_opt_block(char ch, char *arg) if (!is_empty_arg(p)) { block->file_name = strdup(p); if (block->file_name == NULL) { - errmsg(0,"not enough memory"); - return -1; + ERR("not enough memory"); + return ERR_FATAL; } } else if (block->need_file){ - errmsg(0,"no file specified in %s", arg); - return -1; + ERR("no file specified in %s", arg); + return ERR_FATAL; } if (block->size_hdr) { p = argv[i++]; if (!is_empty_arg(p)) { if (str2u32(p, &block->addr) != 0) { - errmsg(0,"invalid start address in %s", arg); - return -1; + ERR("invalid start address in %s", arg); + return ERR_FATAL; } block->addr_set = 1; } @@ -868,17 +881,17 @@ parse_opt_block(char ch, char *arg) p = argv[i++]; if (!is_empty_arg(p)) { - if (str2u32(p, &block->size) != 0) { - errmsg(0,"invalid block size in %s", arg); - return -1; + if (str2u32(p, &block->align) != 0) { + ERR("invalid alignment value in %s", arg); + return ERR_FATAL; } - block->size_set = 1; + block->align_set = 1; } p = argv[i++]; if (!is_empty_arg(p) && (str2u8(p, &block->padc) != 0)) { - errmsg(0,"invalid paddig character in %s", arg); - return -1; + ERR("invalid paddig character in %s", arg); + return ERR_FATAL; } num_blocks++; @@ -891,10 +904,11 @@ int process_blocks(void) { struct csys_block *block; - uint32_t size_avail; + uint32_t offs = 0; int i; int res; + res = 0; /* collecting stats */ for (i = 0; i < num_blocks; i++) { block = &blocks[i]; @@ -903,73 +917,82 @@ process_blocks(void) return res; } - size_avail = board->flash_size; - /* bootloader */ block = boot_block; if (block) { - if (block->size_set) { - board->boot_size= block->size; - } else { - block->size = board->boot_size; - } - if (block->size > size_avail) { - errmsg(0,"boot block is too big"); - return -1; + block->size = board->boot_size; + if (block->file_size > board->boot_size) { + WARN("boot block is too big"); + res = ERR_INVALID_IMAGE; } } - size_avail -= board->boot_size; + offs += board->boot_size; /* configuration data */ block = conf_block; if (block) { - if (block->size_set) { - board->conf_size = block->size; - } else { - block->size = board->conf_size; + block->size = board->conf_size; + if (block->file_size > board->conf_size) { + WARN("config block is too big"); + res = ERR_INVALID_IMAGE; } - if (block->size > size_avail) { - errmsg(0,"config block is too big"); - return -1; - } - } - size_avail -= board->conf_size; + offs += board->conf_size; /* webpages */ block = webp_block; if (block) { - if (block->size_set == 0) - block->size = board->webp_size; - board->webp_size = block->size; - if (block->size > board->webp_size_max) { - errmsg(0,"webpages block is too big"); - return -1; - } + memcpy(block->sig, board->sig_webp, 4); + if (block->addr_set == 0) block->addr = board->addr_webp; + + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; + + block->size = align(offs + block->file_size + block->size_hdr + + block->size_csum, block->align) - offs; + + if (block->size > board->webp_size_max) { + WARN("webpages block is too big"); + res = ERR_INVALID_IMAGE; + } + + DBG(2,"webpages start at %08x, size=%08x", offs, + block->size); + + offs += block->size; + if (offs > board->flash_size) { + WARN("webp block is too big"); + res = ERR_INVALID_IMAGE; + } } - size_avail -= board->webp_size; /* runtime code */ block = code_block; if (block) { - if (block->size_set == 0) { - block->size =ALIGN(block->file_size+ block->size_hdr + - block->size_csum, 0x10000); - } - board->code_size = block->size; - if (board->code_size > size_avail) { - errmsg(0,"code block is too big"); - if (size_check) - return -1; - } memcpy(code_block->sig, SIG_CSYS, 4); - if (code_block->addr_set == 0) - code_block->addr = board->addr_code; + + if (block->addr_set == 0) + block->addr = board->addr_code; + + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; + + block->size = align(offs + block->file_size + + block->size_hdr + block->size_csum, + block->align) - offs; + + DBG(2,"code block start at %08x, size=%08x", offs, + block->size); + + offs += block->size; + if (offs > board->flash_size) { + WARN("code block is too big"); + res = ERR_INVALID_IMAGE; + } } - size_avail -= board->code_size; for (i = 0; i < num_blocks; i++) { block = &blocks[i]; @@ -977,17 +1000,22 @@ process_blocks(void) if (block->type != BLOCK_TYPE_XTRA) continue; - if (block->size_set == 0) - block->size = ALIGN(block->file_size, 0x10000); + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; - if (block->size > size_avail) { - errmsg(0,"no space for file %s", - block->file_name); - if (size_check) - return -1; - } + block->size = align(offs + block->file_size, + block->align) - offs; - size_avail -= block->size; + DBG(2,"file %s start at %08x, size=%08x, align=%08x", + block->file_name, offs, block->size, block->align); + + offs += block->size; + if (offs > board->flash_size) { + WARN("file %s is too big, size=%d, avail=%d", + block->file_name, block->file_size, + board->flash_size - offs); + res = ERR_INVALID_IMAGE; + } } for (i = 0; i < num_blocks; i++) { @@ -997,11 +1025,10 @@ process_blocks(void) block->size_csum; if (block->size_avail < block->file_size) { - errmsg(0,"file %s is too big, size=%d, avail=%d", - block->file_name, block->file_size, + WARN("file %s is too big, size=%d, avail=%d", + block->file_name, block->file_size, block->size_avail); - res = -1; - break; + res = ERR_INVALID_IMAGE; } } @@ -1014,17 +1041,17 @@ main(int argc, char *argv[]) { int optinvalid = 0; /* flag for invalid option */ int c; - int res = EXIT_FAILURE; + int res = ERR_FATAL; FILE *outfile; - + progname=basename(argv[0]); opterr = 0; /* could not print standard getopt error messages */ while ( 1 ) { optinvalid = 0; - - c = getopt(argc, argv, "b:c:dB:hr:vw:x:"); + + c = getopt(argc, argv, "b:B:c:dhkr:vw:x:"); if (c == -1) break; @@ -1044,7 +1071,10 @@ main(int argc, char *argv[]) optinvalid = parse_opt_block(c,optarg); break; case 'd': - size_check = 0; + invalid_causes_error = 0; + break; + case 'k': + keep_invalid_images = 1; break; case 'B': optinvalid = parse_opt_board(c,optarg); @@ -1060,52 +1090,67 @@ main(int argc, char *argv[]) break; } if (optinvalid != 0 ){ - errmsg(0, "invalid option: -%c", optopt); + ERR("invalid option: -%c", optopt); goto out; } } + if (board == NULL) { + ERR("no board specified"); + goto out; + } + if (optind == argc) { - errmsg(0, "no output file specified"); + ERR("no output file specified"); goto out; } ofname = argv[optind++]; if (optind < argc) { - errmsg(0, "invalid option: %s", argv[optind]); + ERR("invalid option: %s", argv[optind]); goto out; } - - if (board == NULL) { - errmsg(0, "no board specified"); + res = process_blocks(); + if (res == ERR_FATAL) goto out; - } - if (process_blocks() != 0) { - goto out; + if (res == ERR_INVALID_IMAGE) { + if (invalid_causes_error) + res = ERR_FATAL; + + if (keep_invalid_images == 0) { + WARN("generation of invalid images disabled", ofname); + goto out; + } + + WARN("generating invalid image", ofname); } outfile = fopen(ofname, "w"); if (outfile == NULL) { - errmsg(1, "could not open \"%s\" for writing", ofname); + ERRS("could not open \"%s\" for writing", ofname); + res = ERR_FATAL; goto out; } - if (write_out_blocks(outfile) != 0) + if (write_out_blocks(outfile) != 0) { + res = ERR_FATAL; goto out_flush; + } - dbgmsg(1,"Image file %s completed.", ofname); - - res = EXIT_SUCCESS; + DBG(1,"Image file %s completed.", ofname); out_flush: fflush(outfile); fclose(outfile); - if (res != EXIT_SUCCESS) { + if (res == ERR_FATAL) { unlink(ofname); } out: - return res; + if (res == ERR_FATAL) + return EXIT_FAILURE; + + return EXIT_SUCCESS; }