📄 netflash.c
字号:
/****************************************************************************//* * netflash.c: network FLASH loader. * * Copyright (C) 1999-2001, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2000-2001, Lineo (www.lineo.com) * Copyright (C) 2000-2002, SnapGear (www.snapgear.com) * * Copied and hacked from rootloader.c which was: * * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *//****************************************************************************/#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <assert.h>#include <ctype.h>#include <getopt.h>#include <errno.h>#include <unistd.h>#include <dirent.h>#include <signal.h>#include <sys/types.h>#include <sys/sysmacros.h>#include <sys/syscall.h>#include <sys/stat.h>#include <sys/mount.h>#include <sys/termios.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <stdarg.h>#include <linux/autoconf.h>#include <linux/version.h>#include <config/autoconf.h>#include <linux/major.h>#ifdef CONFIG_USER_NETFLASH_CRYPTO#include <openssl/bio.h>#include "crypto.h"#endif#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULES)#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)#include <mtd/mtd-user.h>#define MTD_CHAR_MAJOR 90#define MTD_BLOCK_MAJOR 31#else #include <linux/mtd/mtd.h>#endif#else#include <linux/blkmem.h>#endif#ifdef CONFIG_LEDMAN#include <linux/ledman.h>#endif#ifdef CONFIG_USER_NETFLASH_DECOMPRESS#include <zlib.h>#endif#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULES)#include <linux/jffs2.h>#endif#if defined(CONFIG_NFTL_RW) && !defined(NFTL_MAJOR) #define NFTL_MAJOR 93#endif#ifdef CONFIG_IDE#include <linux/hdreg.h>#endif#include <asm/byteorder.h>#include "netflash.h"#include "exit_codes.h"#include "versioning.h"/****************************************************************************/#ifdef CONFIG_USER_NETFLASH_HMACMD5#include "hmacmd5.h"#define HMACMD5_OPTIONS "m:"#else#define HMACMD5_OPTIONS#endif#ifdef CONFIG_USER_NETFLASH_DECOMPRESS#define DECOMPRESS_OPTIONS "z"#else#define DECOMPRESS_OPTIONS#endif /*CONFIG_USER_NETFLASH_DECOMPRESS*/#ifdef CONFIG_USER_NETFLASH_SETSRC#define SETSRC_OPTIONS "I:"#else#define SETSRC_OPTIONS#endif#define CMD_LINE_OPTIONS "bc:Cd:fFhiHjkKlno:pr:sStuv?" DECOMPRESS_OPTIONS HMACMD5_OPTIONS SETSRC_OPTIONS#define PID_DIR "/var/run"#define DHCPCD_PID_FILE "dhcpcd-"#define CHECKSUM_LENGTH 4#ifdef CONFIG_USER_BUSYBOX_WATCHDOGD#define CONFIG_USER_NETFLASH_WATCHDOG 1#endif/****************************************************************************/char *version = "2.1.4";int exitstatus = 0;struct fileblock_t *fileblocks = NULL;int fileblocks_num = 0;unsigned long file_offset = 0;unsigned long file_length = 0;unsigned long image_length = 0;unsigned int calc_checksum = 0;#define BLOCK_OVERHEAD 16#define block_len (_block_len - BLOCK_OVERHEAD)int _block_len = 8192;int dothrow = 0; /* Check version info of image; no program */int dolock, dounlock; /* do we lock/unlock segments as we go */int checkimage; /* Compare with current flash contents; no program */#if CONFIG_USER_NETFLASH_WATCHDOGint watchdog = 1; /* tickle watchdog while writing to flash */int watchdog_fd = -1; /* ensure this is initalised to an invalid fd */#endifint preserveconfig; /* Preserve special bits of flash such as config fs */int preserve = 0; /* Preserve and portions of flash not written to */int offset = 0; /* Offset to start writing at */int stop_early = 0; /* stop at end of input data, do not write full dev. */int nostop_early = 0; /* no stop at end of input data, do write full dev. */int dojffs2; /* Write the jffs2 magic to unused segments */#ifdef CONFIG_USER_NETFLASH_DECOMPRESSint doinflate; /* Decompress the image */#endifint docgi = 0; /* Read options and data from stdin in mime multipart format */#if defined(CONFIG_USER_NETFLASH_WITH_CGI) && !defined(RECOVER_PROGRAM)char cgi_data[64]; /* CGI section name for the image part */char cgi_options[64]; /* CGI section name for the command line options part */extern size_t cgi_load(const char *data_name, const char *options_name, char options[64]);#endifextern int tftpverbose;extern int ftpverbose;FILE *nfd;#ifdef CONFIG_USER_NETFLASH_DECOMPRESSz_stream z;struct fileblock_t *zfb;static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */#endifstruct stat stat_rdev;#ifdef CONFIG_USER_NETFLASH_SETSRCchar *srcaddr = NULL;#endifstatic void (* program_segment)(int rd, char *sgdata, int sgpos, int sglength, int sgsize);static void exit_failed(int rc);/****************************************************************************/#define notice(a...) fprintf(stdout, "netflash: " a); fprintf(stdout, "\n"); fflush(stdout);#if defined(CONFIG_USER_NETFLASH_WITH_CGI) && !defined(RECOVER_PROGRAM)#define error(a...) fprintf(stdout, "netflash: " a); fprintf(stdout, "\n"); fflush(stdout);#else#define error(a...) fprintf(stderr, "netflash: " a); fprintf(stderr, "\n"); fflush(stderr);#endif/****************************************************************************/void restartinit(void){#if !defined(CONFIG_USER_NETFLASH_WITH_CGI) || defined(RECOVER_PROGRAM) error("restarting init process...");#endif kill(1, SIGCONT);#ifdef CONFIG_USER_NETFLASH_WATCHDOG close(watchdog_fd); system("watchdog /dev/watchdog");#endif}#ifdef CONFIG_USER_NETFLASH_CRYPTO#define memcpy_withsum(d, s, l, sum) memcpy(d, s, l)#elsestatic void memcpy_withsum(void *dst, void *src, int len, unsigned int *sum){ unsigned char *dp = (unsigned char *)dst; unsigned char *sp = (unsigned char *)src; while(len > 0){ *dp = *sp; *sum += *dp++; sp++; len--; }}#endif/* * Note: This routine is more general than it currently needs to * be since it handles out of order writes. */void add_data(unsigned long address, unsigned char * data, unsigned long len){ int l; struct fileblock_t *fb; struct fileblock_t *fbprev = NULL; struct fileblock_t *fbnew; static unsigned long total = 0; static unsigned lastk = 0; /*printf("add_data(%lx:%lx)\n", address, len);*/ /* The fileblocks list is ordered, so initialise this outside * the while loop to save some search time. */ fb=fileblocks; do { /* Search for any blocks that overlap with the range we are adding */ for (; fb!=NULL; fbprev=fb, fb=fb->next) { if (address < fb->pos) break; if (address < (fb->pos + fb->maxlength)) { l = fb->maxlength - (address - fb->pos); if (l > len) l = len; memcpy_withsum(fb->data + (address - fb->pos), data, l, &calc_checksum); fb->length = l + (address - fb->pos); address += l; data += l; len -= l; total += l; if (len == 0) return; } } if (!docgi) { if (total / 1024 != lastk) { lastk = total / 1024; printf("\r%dK", lastk); fflush(stdout); } }#ifdef CONFIG_USER_NETFLASH_WATCHDOG if (watchdog) write(watchdog_fd, "\0", 1); #endif /* At this point: * fb = block following the range we are adding, * or NULL if at end * fbprev = block preceding the range, or NULL if at start */#ifdef CONFIG_USER_NETFLASH_VERSION#ifndef CONFIG_USER_NETFLASH_CRYPTO if (dothrow && fileblocks_num > 2) { /* Steal the first block from the list. */ fbnew = fileblocks; fileblocks = fbnew->next; if (fbprev == fbnew) fbprev = NULL; fbnew->pos = address; if (fb && ((fb->pos - address) < fbnew->maxlength)) fbnew->maxlength = fb->pos - address; } else {#endif#endif /*CONFIG_USER_NETFLASH_VERSION*/ fbnew = malloc(sizeof(*fbnew)); if (!fbnew) { error("Insufficient memory for image!"); exit_failed(NO_MEMORY); } fbnew->pos = address; for (;;) { if (fb && ((fb->pos - address) < block_len)) fbnew->maxlength = fb->pos - address; else fbnew->maxlength = block_len; fbnew->data = malloc(fbnew->maxlength); if (fbnew->data) break; /* Halve the block size and try again, down to 1 page */ if (_block_len < 4096) { error("Insufficient memory for image!"); exit_failed(NO_MEMORY); } _block_len /= 2; } fileblocks_num++;#ifdef CONFIG_USER_NETFLASH_VERSION#ifndef CONFIG_USER_NETFLASH_CRYPTO }#endif#endif l = fbnew->maxlength; if (l > len) l = len; memcpy_withsum(fbnew->data, data, l, &calc_checksum); fbnew->length = l; address += l; data += l; len -= l; fbnew->next = fb; if (fbprev) fbprev->next = fbnew; else fileblocks = fbnew; /* Next search starts after the block we just added */ fbprev = fbnew; } while (len > 0);}/* * Remove bytes from the end of the data. This is used to remove * checksum/versioning data before writing or decompressing. */void remove_data(int length){ struct fileblock_t *fb; struct fileblock_t *fbnext; if (fileblocks != NULL && file_length >= length) { file_length -= length; for (fb = fileblocks; fb != NULL; fb = fb->next) { if ((fb->pos + fb->length) >= file_length) break; } fb->length = file_length - fb->pos; while (fb->next != NULL) { fbnext = fb->next; fb->next = fbnext->next; free(fbnext->data); free(fbnext); } }}/* * Generate a checksum over the data. */void chksum(){ unsigned char *sp = 0, *ep; unsigned int file_checksum; int i; struct fileblock_t *fb; file_checksum = 0; if (fileblocks != NULL && file_length >= CHECKSUM_LENGTH) {#ifdef CONFIG_USER_NETFLASH_CRYPTO /* No on the fly check sum so we have to calculate it all here */ for (fb = fileblocks; fb != NULL; fb = fb->next) for (i=0; i<fb->length; i++) calc_checksum += fb->data[i];#endif for (fb = fileblocks; fb != NULL; fb = fb->next) { if ((fb->pos + fb->length) >= (file_length - CHECKSUM_LENGTH)) { sp = fb->data + (file_length - CHECKSUM_LENGTH - fb->pos); break; } } if (!fb) { error("could not find fileblock containing checksum"); exit_failed(IMAGE_SHORT); } ep = fb->data + fb->length; for (i = 0; i < CHECKSUM_LENGTH; i++) { if (sp >= ep) { fb = fb->next; sp = fb->data; ep = sp + fb->length; } file_checksum = (file_checksum << 8) | *sp; calc_checksum -= *sp; sp++; } remove_data(CHECKSUM_LENGTH); calc_checksum = (calc_checksum & 0xffff) + (calc_checksum >> 16); calc_checksum = (calc_checksum & 0xffff) + (calc_checksum >> 16); if (calc_checksum != file_checksum) { error("bad image checksum=0x%04x, expected checksum=0x%04x", calc_checksum, file_checksum); exit_failed(BAD_CHECKSUM); } } else { error("image is too short to contain a checksum"); exit_failed(IMAGE_SHORT); }}#ifdef CONFIG_USER_NETFLASH_HMACMD5int check_hmac_md5(char *key){ HMACMD5_CTX ctx; int length, total; unsigned char hash[16]; int i; struct fileblock_t *fb; if (fileblocks != NULL && file_length >= 16) { HMACMD5Init(&ctx, key, strlen(key)); total = 0; length = 0; for (fb = fileblocks; fb != NULL; fb = fb->next) { if (fb->length > (file_length - total - 16)) length = file_length - total - 16; else length = fb->length; HMACMD5Update(&ctx, fb->data, length); total += length; if (length != fb->length) break; } HMACMD5Final(hash, &ctx); for (i=0; i<16; i++, length++) { if (length>=fb->length) { length = 0; fb = fb->next; } if (hash[i] != fb->data[length]) { error("bad HMAC MD5 signature"); exit_failed(BAD_HMAC_SIG); } } notice("HMAC MD5 signature okay"); remove_data(16); }}#endif#ifdef CONFIG_USER_NETFLASH_CRYPTO/* Extract bytes from end of the data. * These bytes are removed from the data. */static inline void extract_data(int length, char buf[]) { unsigned long i, tpos; struct fileblock_t *fb; unsigned long target_length; if (fileblocks != NULL && file_length >= length) { target_length = file_length - length; for (fb = fileblocks; fb != NULL; fb = fb->next) { if ((fb->pos + fb->length) >= target_length) break; } tpos = target_length - fb->pos; for (i=0; i<length;) { if (tpos >= fb->length) { fb = fb->next; tpos = 0; } buf[i++] = fb->data[tpos++]; } remove_data(length); } else { error("insufficent data at end of image need %d only have %d", length, (int)file_length); exit_failed(IMAGE_SHORT); }}/* Grab a block at the specified position. This could span fileblock boundaries etc so * we pull it out piecemeal. This could be written significantly more efficiently * I expect. */static inline void get_block(struct fileblock_t *fb, unsigned long posn, char buf[], int sz) { int i = 0; unsigned long offset = posn - fb->pos; while (i<sz) { while (offset >= fb->length) offset = 0, fb = fb->next; buf[i++] = fb->data[offset++]; }}/* Write a block of information back into the file at the specified * position. Again this isn't written overly efficiently. */static inline struct fileblock_t *put_block(struct fileblock_t *fb, unsigned long posn, const char buf[], int sz) { int i = 0; unsigned long offset = posn - fb->pos; while (i<sz) { while (offset >= fb->length) offset = 0, fb = fb->next; fb->data[offset++] = buf[i++]; } return fb;}/* Check the crypto signature on the image... * This always includes a public key encrypted header and an MD5 * checksum. It optionally includes AES encryption of the image. */void check_crypto_signature(void) { struct fileblock_t *fb; RSA *pkey; struct header hdr; struct little_header lhdr; /* Load public key */ { BIO *in; struct stat st; if (stat(PUBLIC_KEY_FILE, &st) == -1 && errno == ENOENT) { printf("WARNING: no public key file found, %s\n", PUBLIC_KEY_FILE); return; } in = BIO_new(BIO_s_file()); if (in == NULL) { error("cannot allocate a bio structure"); exit_failed(BAD_DECRYPT); } if (BIO_read_filename(in, PUBLIC_KEY_FILE) <= 0) { error("cannot open public key file"); exit_failed(BAD_PUB_KEY); } pkey = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL); if (pkey == NULL) { error("cannot read public key"); exit_failed(BAD_PUB_KEY); } } /* Decode header information */ extract_data(sizeof(struct little_header), (char *)&lhdr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -