rendered paste body#include <time.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define __PACK__ __attribute__ ((packed))
#define SIGNATURE_LENGTH 4
#define WEBP_SIGNATURE "WEBP"
#define WEBP_DEFAULT_OFFSET 0x8000
#define MAX_PAGE_NAME 60
#define DEFAULT_PAGE_PATH "./web"
#define DEFAULT_TEMPORARY_PATH "./"
#define BUFFER_SIZE 0x4000
#define EXIT_EINVAL 1
#define EXIT_IMAGE_FILE 2
#define EXIT_TEMPORARY 3
#define EXIT_CUT_SECTOR 4
#define EXIT_UNPACK 5
#define EXIT_DECOMPRESS 6
#define TEMPORARY_NAME_LENGTH 10
inline unsigned long SWAP_DWORD(unsigned long dw)
{
return
(((dw << 24) & 0xff000000) |
((dw << 8) & 0x00ff0000) |
((dw >> 8) & 0x0000ff00) |
((dw >> 24) & 0x000000ff));
}
typedef struct {
unsigned char signature[SIGNATURE_LENGTH];
unsigned long start_address;
unsigned long flash_address;
unsigned long length;
} __PACK__ SECTOR;
typedef struct {
char name[MAX_PAGE_NAME];
unsigned long size;
} __PACK__ PAGE;
static int generate_temporary_name(
char *new_name, int max_length,
const char *temporary_path)
{
static const char *symbols = {"0123456789abcdefghijklmnopqrstuvwxyz"};
static char generated_name[TEMPORARY_NAME_LENGTH + 1];
int ret_code = 0;
memset(generated_name, '\0', sizeof(generated_name));
for (unsigned int i = 0; i < TEMPORARY_NAME_LENGTH; i++)
generated_name[i] = symbols[rand() % strlen(symbols)];
if (max_length >= strlen(temporary_path) + strlen(generated_name) + 2) {
memset(new_name, '\0', max_length);
snprintf(new_name, max_length, "%s/%s",
temporary_path, generated_name);
} else {
ret_code = -1;
}
return ret_code;
}
static int read_data(FILE *fp, void *data, unsigned int length, long offset)
{
int ret_code = 0;
if (fseek(fp, offset, SEEK_SET) == 0) {
if (fread(data, 1, length, fp) != length) {
fprintf(stderr,
"Can't read at 0x%08X %d bytes.\n",
offset, length);
}
} else {
fprintf(stderr, "Can't seek to 0x%08X.\n", offset);
ret_code = -1;
}
return ret_code;
}
static int cut_and_store(
FILE *input_fp, long offset, unsigned int length,
const char *output_file)
{
int ret_code = 0;
if (fseek(input_fp, offset, SEEK_SET) == 0) {
FILE *output_fp;
if ((output_fp = fopen(output_file, "wb")) == NULL) {
ret_code = -1;
} else {
unsigned int copied = 0;
char buffer[BUFFER_SIZE];
while ((ret_code == 0) && (copied != length)) {
long bytes_to_copy =
(length - copied) >= sizeof(buffer) ?
sizeof(buffer) : (length - copied);
if ((fread(buffer, bytes_to_copy, 1, input_fp) == 1) &&
(fwrite(buffer, bytes_to_copy, 1, output_fp) == 1)) {
copied += bytes_to_copy;
} else {
ret_code = -1;
}
}
fclose(output_fp);
}
}
return ret_code;
}
static int create_directory_for(const char *full_file_name)
{
int ret_code = 0;
char name[FILENAME_MAX];
char path[FILENAME_MAX];
char *p = (char *) name;
char *start;
memset(name, '\0', sizeof(name));
strncpy(name, full_file_name, sizeof(name) - 1);
while ((p = strchr(p, '/')) != NULL) {
start = p;
while (*p == '/')
*p++;
memmove(start + 1, p, strlen(p) + 1);
p = start + 1;
}
start = p = name;
while ((ret_code == 0) && ((p = strchr(p, '/')) != NULL)) {
struct stat path_stat;
memset(path, '\0', sizeof(path) - 1);
strncpy(path, start, p - start);
if (stat(path, &path_stat) != 0) {
if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
ret_code = -1;
} else {
if (!S_ISDIR(path_stat.st_mode))
ret_code = -1;
}
p++;
}
return ret_code;
}
static int decompress(
const char *compressed_name,
const char *decompressed_name)
{
char command[FILENAME_MAX];
memset(command, '\0', sizeof(command));
snprintf(command, sizeof(command) - 1,
"bzip2 -dqc %s 2>/dev/null > %s",
compressed_name, decompressed_name);
return system(command);
}
static int file_size(const char *name, long *size)
{
FILE *fp = fopen(name, "rb");
int ret_code = -1;
if (fp != NULL) {
if (fseek(fp, 0, SEEK_END) == 0) {
*size = ftell(fp);
ret_code = 0;
}
fclose(fp);
}
return ret_code;
}
static int unpack_pages(
const char *storage_name,
const char *webpage_path,
int swap_bytes)
{
FILE *storage_fp;
int ret_code = 0;
long storage_size;
if (file_size(storage_name, &storage_size) != 0) {
fprintf(stderr, "Can't get web page storage file size.\n");
ret_code = -1;
} else
if ((storage_fp = fopen(storage_name, "rb")) == NULL) {
fprintf(stderr, "Can't open web page binary storage.\n");
ret_code = -1;
} else {
PAGE page;
while ((ret_code == 0) && (ftell(storage_fp) != storage_size)) {
memset(&page, '\0', sizeof(page));
if (read_data(storage_fp, &page,
sizeof(page), ftell(storage_fp)) == 0) {
char page_full_name[FILENAME_MAX];
memset(page_full_name, '\0', sizeof(page_full_name));
snprintf(page_full_name, sizeof(page_full_name) - 1,
"%s/%s", webpage_path, page.name);
page.size = SWAP_DWORD(page.size);
fprintf(stdout, "Storing %s... ", page_full_name);
if (create_directory_for(page_full_name) != 0) {
fprintf(stdout, "error!\n");
fprintf(stderr, "Can't create directory for %s.\n",
page_full_name);
ret_code = -1;
} else
if (cut_and_store(storage_fp, ftell(storage_fp),
page.size, page_full_name) == 0) {
fprintf(stdout, "done.\n");
} else {
fprintf(stdout, "error!\n");
fprintf(stderr,
"Can't store a page into %s.\n",
page_full_name);
ret_code = -1;
}
} else {
fprintf(stderr, "Can't read page description.\n");
ret_code = -1;
}
}
}
return ret_code;
}
static int parse_string(
int argc, char *argv[], int *arg_index, char **value,
const char *full_option_name, const char *option, int *parse_flag)
{
int ret_code = 0;
if (!(*parse_flag)) {
if (argc == *arg_index) {
fprintf(stderr, "%s required after %s.\n",
full_option_name, option);
ret_code = 1;
} else {
*value = argv[*arg_index];
*parse_flag = 1;
(*arg_index)++;
}
} else {
fprintf(stderr, "%s redifinition (%s option).\n",
full_option_name, option);
ret_code = 1;
}
return ret_code;
}
static int parse_hex_or_decimal(
int argc, char *argv[], int *arg_index, unsigned long *value,
const char *full_option_name, const char *option, int *parse_flag)
{
int ret_code = 0;
if (!(*parse_flag)) {
if (argc == *arg_index) {
fprintf(stderr, "%s required after %s.\n",
full_option_name, option);
ret_code = 1;
} else {
char *end_ptr;
long int parsed_value;
parsed_value = strtol(argv[*arg_index], &end_ptr, 0);
if (*end_ptr == '\0') {
*parse_flag = 1;
*value = (unsigned long) parsed_value;
(*arg_index)++;
} else {
fprintf(stderr, "Invalid offset value: %s.\n",
argv[*arg_index]);
ret_code = 1;
}
}
} else {
fprintf(stderr, "%s redifinition (%s option).\n",
full_option_name, option);
ret_code = 1;
}
return ret_code;
}
int parse_arguments(int argc, char *argv[],
char **pages_path, char **temporary_path,
char **image_name, unsigned long *webp_offset, int *swap_bytes)
{
int ret_code = 0;
int image_parsed = 0;
int pages_path_parsed = 0;
int temporary_path_parsed = 0;
int webp_offset_parsed = 0;
int swap_bytes_parsed = 0;
if (argc < 2) {
ret_code = -1;
} else {
int arg_index = 1;
while ((ret_code == 0) && (arg_index < argc)) {
if (*argv[arg_index] == '-') {
char *string_option = argv[arg_index] + 1;
if (strlen(string_option) == 1) {
char option = tolower(*(argv[arg_index] + 1));
arg_index++;
switch (option) {
case 'i': {
ret_code = parse_string(
argc, argv, &arg_index,
image_name, "Image file name",
"-i", &image_parsed);
break;
}
case 'o': {
ret_code = parse_hex_or_decimal(
argc, argv, &arg_index,
webp_offset, "Web page offset",
"-o", &webp_offset_parsed);
break;
}
case 't': {
ret_code = parse_string(
argc, argv, &arg_index,
temporary_path, "Temporary file path",
"-t", &temporary_path_parsed);
break;
}
case 'w': {
ret_code = parse_string(
argc, argv, &arg_index,
pages_path, "Web page file path",
"-w", &pages_path_parsed);
break;
}
case 's': {
if (!swap_bytes_parsed) {
*swap_bytes = 1;
} else {
fprintf(stderr, "Swap byte flag redefined.\n");
ret_code = 1;
}
break;
}
default: {
fprintf(stderr, "Unknown option '%c'.\n", option);
ret_code = 1;
break;
}
}
} else {
fprintf(stderr, "Unknown option '%s'.\n", string_option);
ret_code = 1;
}
} else {
fprintf(stderr, "An option expected instead of '%s'.\n",
argv[arg_index]);
ret_code = 1;
}
}
}
if ((ret_code == 0) && !image_parsed) {
fprintf(stderr, "Image file name (-i option) should be specified.\n");
ret_code = 1;
}
return ret_code;
}
int main(int argc, char *argv[])
{
char *image_name = "";
char *pages_path = DEFAULT_PAGE_PATH;
char *temporary_path = DEFAULT_TEMPORARY_PATH;
unsigned long webp_offset = WEBP_DEFAULT_OFFSET;
int swap_bytes = 0;
FILE *image_fp;
SECTOR sector;
int ret_code = EXIT_SUCCESS;
srand(time(NULL));
fprintf(stdout,
"decompweb v1.0 unpacks web pages\n" \
"containing into ZyXEL P-330 firmware binary images.\n\n");
ret_code = parse_arguments(argc, argv, &pages_path,
&temporary_path, &image_name, &webp_offset, &swap_bytes);
if (ret_code > 0) {
ret_code = EXIT_EINVAL;
} else
if (ret_code == -1) {
fprintf(stdout,
"Use the following options:\n" \
"\t-i <image name>\n" \
"\t-o <start offset> (default 0x8000)\n" \
"\t-t <temporary path> (default ./)\n" \
"\t-w <web page output path> (default ./web)\n" \
"\t-s swap bytes in size header fields\n\n");
ret_code = EXIT_SUCCESS;
} else
if ((image_fp = fopen(image_name, "rb")) == NULL) {
fprintf(stderr, "Can't open image file: %s.\n", image_name);
ret_code = EXIT_IMAGE_FILE;
} else {
SECTOR webp_sector;
fprintf(stdout,
"Image name : %s\n" \
"Web page offset : 0x%08X\n" \
"Output directory : %s\n" \
"Temporary directory : %s\n" \
"Swap bytes : %s\n\n",
image_name, webp_offset, pages_path,
temporary_path, swap_bytes ? "yes" : "no");
if ((read_data(image_fp, &webp_sector,
sizeof(webp_sector), webp_offset) == 0) &&
(memcmp(webp_sector.signature, WEBP_SIGNATURE,
sizeof(webp_sector.signature)) == 0)) {
if (swap_bytes) {
webp_sector.start_address =
SWAP_DWORD(webp_sector.start_address);
webp_sector.flash_address =
SWAP_DWORD(webp_sector.flash_address);
webp_sector.length =
SWAP_DWORD(webp_sector.length);
}
fprintf(stdout,
"Sector: %s\n" \
"Start : 0x%08X\n" \
"Length: 0x%08X\n" \
"Flash : 0x%08X\n\n",
WEBP_SIGNATURE, webp_sector.start_address,
webp_sector.length, webp_sector.flash_address);
char compressed_file[FILENAME_MAX];
long sector_offset = webp_offset + sizeof(webp_sector);
memset(compressed_file, '\0', sizeof(compressed_file));
if (generate_temporary_name(compressed_file,
sizeof(compressed_file) - 1, temporary_path) != 0) {
fprintf(stderr, "Can't generate a temporary file name.");
ret_code = EXIT_TEMPORARY;
} else
if (cut_and_store(image_fp, sector_offset,
webp_sector.length, compressed_file) != 0) {
fprintf(stderr,
"Can't cut %s sector from %s at 0x%08X.\n",
WEBP_SIGNATURE, image_name, sector_offset);
ret_code = EXIT_CUT_SECTOR;
} else {
char decompressed_file[FILENAME_MAX];
memset(decompressed_file, '\0', sizeof(decompressed_file));
if (generate_temporary_name(decompressed_file,
sizeof(decompressed_file) - 1, temporary_path) != 0) {
fprintf(stderr, "Can't generate a temporary file name.");
ret_code = EXIT_TEMPORARY;
} else
if (decompress(compressed_file, decompressed_file) == 0) {
if (unpack_pages(decompressed_file,
pages_path, swap_bytes) == 0) {
fprintf(stdout,
"Web pages were unpacked into %s.\n",
pages_path);
} else {
fprintf(stderr, "Can't unpack web pages into %s.\n",
pages_path);
ret_code = EXIT_UNPACK;
}
} else {
fprintf(stderr,
"Can't decompress webpage set. " \
"Possibly %s image part is damaged.\n",
WEBP_SIGNATURE);
ret_code = EXIT_DECOMPRESS;
}
remove(decompressed_file);
}
remove(compressed_file);
} else {
fprintf(stderr, "Can't read %s sector at 0x%08X.\n",
WEBP_SIGNATURE, webp_offset);
ret_code = EXIT_CUT_SECTOR;
}
fclose(image_fp);
}
return ret_code;
}