All pastes #743993 Raw Edit

Unnamed

public text v1 · immutable
·published 2007-10-20 23:40 UTC
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;
}