Fiasco mmc image contains layout and two parts in one data section. First part is FAT32 partition (MyDocs) and second part is tar archive for second ext3 partition (/home). This change extends fiasco parser to load all parts from one data section and correctly unpack all parts.
716 lines
18 KiB
C
716 lines
18 KiB
C
/*
|
|
0xFFFF - Open Free Fiasco Firmware Flasher
|
|
Copyright (C) 2007-2011 pancake <pancake@youterm.com>
|
|
Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "global.h"
|
|
|
|
#include "device.h"
|
|
#include "image.h"
|
|
#include "fiasco.h"
|
|
|
|
#define CHECKSUM(checksum, buf, size) do { size_t _i; for ( _i = 0; _i < size; _i++ ) checksum += ((unsigned char *)buf)[_i]; } while (0)
|
|
#define FIASCO_READ_ERROR(fiasco, ...) do { ERROR_INFO(__VA_ARGS__); fiasco_free(fiasco); return NULL; } while (0)
|
|
#define FIASCO_WRITE_ERROR(file, fd, ...) do { ERROR_INFO_STR(file, __VA_ARGS__); if ( fd >= 0 ) close(fd); return -1; } while (0)
|
|
#define READ_OR_FAIL(fiasco, buf, size) do { if ( read(fiasco->fd, buf, size) != size ) { FIASCO_READ_ERROR(fiasco, "Cannot read %d bytes", size); } } while (0)
|
|
#define READ_OR_RETURN(fiasco, checksum, buf, size) do { if ( read(fiasco->fd, buf, size) != size ) return fiasco; CHECKSUM(checksum, buf, size); } while (0)
|
|
#define WRITE_OR_FAIL_FREE(file, fd, buf, size, var) do { if ( ! simulate ) { if ( write(fd, buf, size) != (ssize_t)size ) { free(var); FIASCO_WRITE_ERROR(file, fd, "Cannot write %d bytes", size); } } } while (0)
|
|
#define WRITE_OR_FAIL(file, fd, buf, size) WRITE_OR_FAIL_FREE(file, fd, buf, size, NULL)
|
|
|
|
struct fiasco * fiasco_alloc_empty(void) {
|
|
|
|
struct fiasco * fiasco = calloc(1, sizeof(struct fiasco));
|
|
if ( ! fiasco )
|
|
ALLOC_ERROR_RETURN(NULL);
|
|
|
|
fiasco->fd = -1;
|
|
return fiasco;
|
|
|
|
}
|
|
|
|
struct fiasco * fiasco_alloc_from_file(const char * file) {
|
|
|
|
uint8_t byte;
|
|
uint32_t length;
|
|
uint32_t count;
|
|
uint8_t length8;
|
|
uint8_t count8;
|
|
|
|
char type[13];
|
|
char device[17];
|
|
char hwrevs[1024];
|
|
char version[257];
|
|
char layout[257];
|
|
uint8_t asicidx;
|
|
uint8_t devicetype;
|
|
uint8_t deviceidx;
|
|
uint8_t checksum;
|
|
uint32_t address;
|
|
uint16_t hash;
|
|
off_t offset;
|
|
struct image * image;
|
|
struct image_part * image_part;
|
|
struct image_part * image_parts;
|
|
|
|
char hwrev[9];
|
|
unsigned char buf[512];
|
|
unsigned char *pbuf;
|
|
|
|
struct fiasco * fiasco = fiasco_alloc_empty();
|
|
if ( ! fiasco )
|
|
return NULL;
|
|
|
|
fiasco->fd = open(file, O_RDONLY);
|
|
if ( fiasco->fd < 0 ) {
|
|
ERROR_INFO("Cannot open file");
|
|
fiasco_free(fiasco);
|
|
return NULL;
|
|
}
|
|
|
|
fiasco->orig_filename = strdup(file);
|
|
|
|
READ_OR_FAIL(fiasco, &byte, 1);
|
|
if ( byte != 0xb4 )
|
|
FIASCO_READ_ERROR(fiasco, "Invalid fiasco signature");
|
|
|
|
READ_OR_FAIL(fiasco, &length, 4);
|
|
length = ntohl(length);
|
|
|
|
READ_OR_FAIL(fiasco, &count, 4);
|
|
count = ntohl(count);
|
|
|
|
VERBOSE("Number of header blocks: %d\n", count);
|
|
|
|
while ( count > 0 ) {
|
|
READ_OR_FAIL(fiasco, &byte, 1);
|
|
READ_OR_FAIL(fiasco, &length8, 1);
|
|
READ_OR_FAIL(fiasco, buf, length8);
|
|
if ( byte == 0xe8 ) {
|
|
memset(fiasco->name, 0, sizeof(fiasco->name));
|
|
strncpy(fiasco->name, (char *)buf, length8);
|
|
VERBOSE("Fiasco name: %s\n", fiasco->name);
|
|
} else if ( byte == 0x31 ) {
|
|
memset(fiasco->swver, 0, sizeof(fiasco->swver));
|
|
strncpy(fiasco->swver, (char *)buf, length8);
|
|
VERBOSE("SW version: %s\n", fiasco->swver);
|
|
} else {
|
|
VERBOSE("Unknown header %#x\n", byte);
|
|
}
|
|
--count;
|
|
}
|
|
|
|
/* walk the tree */
|
|
while ( 1 ) {
|
|
|
|
/* If end of file, return fiasco image */
|
|
READ_OR_RETURN(fiasco, checksum, buf, 1);
|
|
|
|
/* Header of next image (0x54) */
|
|
if ( buf[0] != 0x54 ) {
|
|
ERROR("Invalid next image header");
|
|
return fiasco;
|
|
}
|
|
|
|
checksum = 0x00;
|
|
|
|
READ_OR_RETURN(fiasco, checksum, &count8, 1);
|
|
|
|
if ( count8 == 0 ) {
|
|
ERROR("No section in image header");
|
|
return fiasco;
|
|
}
|
|
|
|
READ_OR_RETURN(fiasco, checksum, buf, 2);
|
|
|
|
/* File data section (0x2E) with length of 25 bytes */
|
|
if ( buf[0] != 0x2E || buf[1] != 25 ) {
|
|
ERROR("First section in image header is not file data with length of 25 bytes");
|
|
return fiasco;
|
|
}
|
|
|
|
READ_OR_RETURN(fiasco, checksum, &asicidx, 1);
|
|
READ_OR_RETURN(fiasco, checksum, &devicetype, 1);
|
|
READ_OR_RETURN(fiasco, checksum, &deviceidx, 1);
|
|
|
|
READ_OR_RETURN(fiasco, checksum, &hash, 2);
|
|
hash = ntohs(hash);
|
|
|
|
memset(type, 0, sizeof(type));
|
|
READ_OR_RETURN(fiasco, checksum, type, 12);
|
|
|
|
byte = type[0];
|
|
if ( byte == 0xFF )
|
|
return fiasco;
|
|
|
|
VERBOSE(" %s\n", type);
|
|
|
|
READ_OR_RETURN(fiasco, checksum, &length, 4);
|
|
length = ntohl(length);
|
|
|
|
/* load address (unused) */
|
|
READ_OR_RETURN(fiasco, checksum, &address, 4);
|
|
|
|
/* end of file data section */
|
|
--count8;
|
|
|
|
VERBOSE(" size: %d bytes\n", length);
|
|
VERBOSE(" address: 0x%04x\n", address);
|
|
VERBOSE(" hash: 0x%04x\n", hash);
|
|
VERBOSE(" asic idx: %d\n", asicidx);
|
|
VERBOSE(" device type: %d\n", devicetype);
|
|
VERBOSE(" device idx: %d\n", deviceidx);
|
|
VERBOSE(" subsections: %d\n", count8);
|
|
|
|
memset(device, 0, sizeof(device));
|
|
memset(hwrevs, 0, sizeof(hwrevs));
|
|
memset(version, 0, sizeof(version));
|
|
memset(layout, 0, sizeof(layout));
|
|
image_part = NULL;
|
|
image_parts = NULL;
|
|
|
|
while ( count8 > 0 ) {
|
|
|
|
READ_OR_RETURN(fiasco, checksum, &byte, 1);
|
|
READ_OR_RETURN(fiasco, checksum, &length8, 1);
|
|
READ_OR_RETURN(fiasco, checksum, buf, length8);
|
|
|
|
VERBOSE(" subinfo\n");
|
|
VERBOSE(" length: %d\n", length8);
|
|
VERBOSE(" type: ");
|
|
|
|
if ( byte == '1' ) {
|
|
memset(version, 0, sizeof(version));
|
|
strncpy(version, (char *)buf, length8);
|
|
VERBOSE("version string\n");
|
|
VERBOSE(" version: %s\n", version);
|
|
} else if ( byte == '2' ) {
|
|
int tmp = length8;
|
|
if ( tmp > 16 ) tmp = 16;
|
|
memset(device, 0, sizeof(device));
|
|
strncpy(device, (char *)buf, tmp);
|
|
VERBOSE("hw revision\n");
|
|
VERBOSE(" device: %s\n", device);
|
|
pbuf = buf + strlen(device) + 1;
|
|
while ( pbuf < buf + length8 ) {
|
|
while ( pbuf < buf + length8 && *pbuf < 32 )
|
|
++pbuf;
|
|
if ( pbuf >= buf + length8 ) break;
|
|
tmp = buf + length8 - pbuf;
|
|
if ( tmp > 8 ) tmp = 8;
|
|
memset(hwrev, 0, sizeof(hwrev));
|
|
strncpy(hwrev, (char *)pbuf, tmp);
|
|
if ( ! hwrevs[0] )
|
|
strcpy(hwrevs, hwrev);
|
|
else {
|
|
size_t len1 = strlen(hwrevs);
|
|
size_t len2 = strlen(hwrev);
|
|
if ( len1 + len2 + 2 < sizeof(hwrevs) ) {
|
|
hwrevs[len1] = ',';
|
|
memcpy(hwrevs+len1+1, hwrev, len2+1);
|
|
}
|
|
}
|
|
VERBOSE(" hw revision: %s\n", hwrev);
|
|
pbuf += strlen(hwrev) + 1;
|
|
}
|
|
} else if ( byte == '3' ) {
|
|
memset(layout, 0, sizeof(layout));
|
|
strncpy(layout, (char *)buf, length8);
|
|
VERBOSE("layout\n");
|
|
} else if ( byte == '4' ) {
|
|
VERBOSE("data part\n");
|
|
if ( length8 < 16 ) {
|
|
VERBOSE(" (damaged)\n");
|
|
} else {
|
|
if ( image_parts ) {
|
|
image_part->next = calloc(1, sizeof(struct image_part));
|
|
if ( ! image_part->next )
|
|
FIASCO_READ_ERROR(fiasco, "Cannot allocate image");
|
|
image_part = image_part->next;
|
|
} else {
|
|
image_parts = calloc(1, sizeof(struct image_part));
|
|
if ( ! image_parts )
|
|
FIASCO_READ_ERROR(fiasco, "Cannot allocate image");
|
|
image_part = image_parts;
|
|
}
|
|
image_part->offset = ntohl(*(uint32_t *)&buf[4]);
|
|
image_part->size = ntohl(*(uint32_t *)&buf[12]);
|
|
if ( length8 > 16 ) {
|
|
image_part->name = calloc(1, length8-16+1);
|
|
if ( image_part->name )
|
|
memcpy(image_part->name, &buf[16], length8-16);
|
|
}
|
|
VERBOSE(" unknown: 0x%02x 0x%02x 0x%02x 0x%02x\n", buf[0], buf[1], buf[2], buf[3]);
|
|
VERBOSE(" offset: %u bytes\n", image_part->offset);
|
|
VERBOSE(" unknown: 0x%02x 0x%02x 0x%02x 0x%02x\n", buf[8], buf[9], buf[10], buf[11]);
|
|
VERBOSE(" size: %u bytes\n", image_part->size);
|
|
if ( image_part->name )
|
|
VERBOSE(" partition name: %s\n", image_part->name);
|
|
}
|
|
} else {
|
|
VERBOSE("unknown ('%c':%#x)\n", byte, byte);
|
|
}
|
|
|
|
--count8;
|
|
}
|
|
|
|
/* checksum */
|
|
READ_OR_RETURN(fiasco, checksum, buf, 1);
|
|
VERBOSE(" subinfo checksum: 0x%02x\n", buf[0]);
|
|
|
|
if ( ! noverify && buf[0] != 0x00 && checksum != 0xFF ) {
|
|
ERROR("Image header subinfo checksum mishmash (counted 0x%02x, got 0x%02x)", (0xFF - checksum + buf[0]) & 0xFF, buf[0]);
|
|
return fiasco;
|
|
}
|
|
|
|
offset = lseek(fiasco->fd, 0, SEEK_CUR);
|
|
if ( offset == (off_t)-1 )
|
|
FIASCO_READ_ERROR(fiasco, "Cannot get offset of file");
|
|
|
|
VERBOSE(" version: %s\n", version);
|
|
VERBOSE(" device: %s\n", device);
|
|
VERBOSE(" hwrevs: %s\n", hwrevs);
|
|
VERBOSE(" data at: %#08x\n", (unsigned int)offset);
|
|
|
|
image = image_alloc_from_shared_fd(fiasco->fd, length, offset, hash, type, device, hwrevs, version, layout, image_parts);
|
|
|
|
if ( ! image )
|
|
FIASCO_READ_ERROR(fiasco, "Cannot allocate image");
|
|
|
|
fiasco_add_image(fiasco, image);
|
|
|
|
if ( lseek(fiasco->fd, offset+length, SEEK_SET) == (off_t)-1 )
|
|
FIASCO_READ_ERROR(fiasco, "Cannot seek to next image in file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void fiasco_free(struct fiasco * fiasco) {
|
|
|
|
struct image_list * list = fiasco->first;
|
|
|
|
while ( list ) {
|
|
struct image_list * next = list->next;
|
|
image_list_del(list);
|
|
list = next;
|
|
}
|
|
|
|
if ( fiasco->fd >= 0 )
|
|
close(fiasco->fd);
|
|
|
|
free(fiasco->orig_filename);
|
|
|
|
free(fiasco);
|
|
|
|
}
|
|
|
|
void fiasco_add_image(struct fiasco * fiasco, struct image * image) {
|
|
|
|
image_list_add(&fiasco->first, image);
|
|
|
|
}
|
|
|
|
int fiasco_write_to_file(struct fiasco * fiasco, const char * file) {
|
|
|
|
int fd = -1;
|
|
int i;
|
|
int device_count;
|
|
uint32_t size;
|
|
uint32_t length;
|
|
uint16_t hash;
|
|
uint8_t length8;
|
|
uint8_t checksum;
|
|
char ** device_hwrevs_bufs;
|
|
const char * str;
|
|
const char * type;
|
|
struct image_list * image_list;
|
|
struct image * image;
|
|
unsigned char buf[4096];
|
|
|
|
if ( ! fiasco )
|
|
return -1;
|
|
|
|
printf("Generating Fiasco image %s...\n", file);
|
|
|
|
if ( ! fiasco->first )
|
|
FIASCO_WRITE_ERROR(file, fd, "Nothing to write");
|
|
|
|
if ( strlen(fiasco->name)+1 > UINT8_MAX )
|
|
FIASCO_WRITE_ERROR(file, fd, "Fiasco name string is too long");
|
|
|
|
if ( strlen(fiasco->swver)+1 > UINT8_MAX )
|
|
FIASCO_WRITE_ERROR(file, fd, "SW version string is too long");
|
|
|
|
if ( ! simulate ) {
|
|
fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644);
|
|
if ( fd < 0 ) {
|
|
ERROR_INFO("Cannot create file");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
printf("Writing Fiasco header...\n");
|
|
|
|
WRITE_OR_FAIL(file, fd, "\xb4", 1); /* signature */
|
|
|
|
if ( fiasco->name[0] )
|
|
str = fiasco->name;
|
|
else
|
|
str = "OSSO UART+USB";
|
|
|
|
length = 4 + strlen(str) + 3;
|
|
if ( fiasco->swver[0] )
|
|
length += strlen(fiasco->swver) + 3;
|
|
length = htonl(length);
|
|
WRITE_OR_FAIL(file, fd, &length, 4); /* FW header length */
|
|
|
|
if ( fiasco->swver[0] )
|
|
length = htonl(2);
|
|
else
|
|
length = htonl(1);
|
|
WRITE_OR_FAIL(file, fd, &length, 4); /* FW header blocks count */
|
|
|
|
/* Fiasco name */
|
|
length8 = strlen(str)+1;
|
|
WRITE_OR_FAIL(file, fd, "\xe8", 1);
|
|
WRITE_OR_FAIL(file, fd, &length8, 1);
|
|
WRITE_OR_FAIL(file, fd, str, length8);
|
|
|
|
/* SW version */
|
|
if ( fiasco->swver[0] ) {
|
|
printf("Writing SW version: %s\n", fiasco->swver);
|
|
length8 = strlen(fiasco->swver)+1;
|
|
WRITE_OR_FAIL(file, fd, "\x31", 1);
|
|
WRITE_OR_FAIL(file, fd, &length8, 1);
|
|
WRITE_OR_FAIL(file, fd, fiasco->swver, length8);
|
|
};
|
|
|
|
printf("\n");
|
|
|
|
image_list = fiasco->first;
|
|
|
|
while ( image_list ) {
|
|
|
|
image = image_list->image;
|
|
|
|
if ( ! image )
|
|
FIASCO_WRITE_ERROR(file, fd, "Empty image");
|
|
|
|
printf("Writing image...\n");
|
|
image_print_info(image);
|
|
|
|
type = image_type_to_string(image->type);
|
|
|
|
if ( ! type )
|
|
FIASCO_WRITE_ERROR(file, fd, "Unknown image type");
|
|
|
|
if ( image->version && strlen(image->version) > UINT8_MAX )
|
|
FIASCO_WRITE_ERROR(file, fd, "Image version string is too long");
|
|
|
|
if ( image->layout && strlen(image->layout) > UINT8_MAX )
|
|
FIASCO_WRITE_ERROR(file, fd, "Image layout is too long");
|
|
|
|
device_hwrevs_bufs = device_list_alloc_to_bufs(image->devices);
|
|
|
|
device_count = 0;
|
|
if ( device_hwrevs_bufs && device_hwrevs_bufs[0] )
|
|
for ( ; device_hwrevs_bufs[device_count]; ++device_count );
|
|
|
|
printf("Writing image header...\n");
|
|
|
|
/* signature */
|
|
WRITE_OR_FAIL_FREE(file, fd, "T", 1, device_hwrevs_bufs);
|
|
|
|
checksum = 0x00;
|
|
|
|
/* number of subsections */
|
|
length8 = device_count+1;
|
|
if ( image->version )
|
|
++length8;
|
|
if ( image->layout )
|
|
++length8;
|
|
WRITE_OR_FAIL_FREE(file, fd, &length8, 1, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, &length8, 1);
|
|
|
|
/* file data: asic index: APE (0x01), device type: NAND (0x01), device index: 0 */
|
|
WRITE_OR_FAIL_FREE(file, fd, "\x2e\x19\x01\x01\x00", 5, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, "\x2e\x19\x01\x01\x00", 5);
|
|
|
|
/* checksum */
|
|
hash = htons(image->hash);
|
|
WRITE_OR_FAIL_FREE(file, fd, &hash, 2, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, &hash, 2);
|
|
|
|
/* image type name */
|
|
memset(buf, 0, 12);
|
|
strncpy((char *)buf, type, 12);
|
|
WRITE_OR_FAIL_FREE(file, fd, buf, 12, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, buf, 12);
|
|
|
|
/* image size */
|
|
size = htonl(image->size);
|
|
WRITE_OR_FAIL_FREE(file, fd, &size, 4, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, &size, 4);
|
|
|
|
/* image load address (unused always zero) */
|
|
WRITE_OR_FAIL_FREE(file, fd, "\x00\x00\x00\x00", 4, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, "\x00\x00\x00\x00", 4);
|
|
|
|
/* append version subsection */
|
|
if ( image->version ) {
|
|
WRITE_OR_FAIL_FREE(file, fd, "1", 1, device_hwrevs_bufs); /* 1 - version */
|
|
length8 = strlen(image->version)+1; /* +1 for NULL term */
|
|
WRITE_OR_FAIL_FREE(file, fd, &length8, 1, device_hwrevs_bufs);
|
|
WRITE_OR_FAIL_FREE(file, fd, image->version, length8, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, "1", 1);
|
|
CHECKSUM(checksum, &length8, 1);
|
|
CHECKSUM(checksum, image->version, length8);
|
|
}
|
|
|
|
/* append device & hwrevs subsection */
|
|
for ( i = 0; i < device_count; ++i ) {
|
|
WRITE_OR_FAIL_FREE(file, fd, "2", 1, device_hwrevs_bufs); /* 2 - device & hwrevs */
|
|
length8 = ((uint8_t *)(device_hwrevs_bufs[i]))[0];
|
|
WRITE_OR_FAIL_FREE(file, fd, &length8, 1, device_hwrevs_bufs);
|
|
WRITE_OR_FAIL_FREE(file, fd, device_hwrevs_bufs[i]+1, length8, device_hwrevs_bufs);
|
|
CHECKSUM(checksum, "2", 1);
|
|
CHECKSUM(checksum, &length8, 1);
|
|
CHECKSUM(checksum, device_hwrevs_bufs[i]+1, length8);
|
|
}
|
|
free(device_hwrevs_bufs);
|
|
|
|
/* append layout subsection */
|
|
if ( image->layout ) {
|
|
WRITE_OR_FAIL(file, fd, "3", 1); /* 3 - layout */
|
|
length8 = strlen(image->layout);
|
|
WRITE_OR_FAIL(file, fd, &length8, 1);
|
|
WRITE_OR_FAIL(file, fd, image->layout, length8);
|
|
CHECKSUM(checksum, "3", 1);
|
|
CHECKSUM(checksum, &length8, 1);
|
|
CHECKSUM(checksum, image->layout, length8);
|
|
}
|
|
|
|
/* checksum of header */
|
|
checksum = 0xFF - checksum;
|
|
WRITE_OR_FAIL(file, fd, &checksum, 1);
|
|
|
|
printf("Writing image data...\n");
|
|
|
|
image_seek(image, 0);
|
|
while ( 1 ) {
|
|
size = image_read(image, buf, sizeof(buf));
|
|
if ( size == 0 )
|
|
break;
|
|
WRITE_OR_FAIL(file, fd, buf, size);
|
|
}
|
|
|
|
image_list = image_list->next;
|
|
|
|
if ( image_list )
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
if ( ! simulate )
|
|
close(fd);
|
|
|
|
printf("\nDone\n\n");
|
|
return 0;
|
|
|
|
}
|
|
|
|
int fiasco_unpack(struct fiasco * fiasco, const char * dir) {
|
|
|
|
int fd;
|
|
char * name;
|
|
char * layout_name;
|
|
struct image * image;
|
|
struct image_part * image_part;
|
|
struct image_list * image_list;
|
|
uint32_t offset, size, need, total_size, written;
|
|
int part_num;
|
|
char cwd[256];
|
|
unsigned char buf[4096];
|
|
|
|
if ( dir ) {
|
|
|
|
memset(cwd, 0, sizeof(cwd));
|
|
|
|
if ( ! getcwd(cwd, sizeof(cwd)) ) {
|
|
ERROR_INFO("Cannot store current directory");
|
|
return -1;
|
|
}
|
|
|
|
if ( chdir(dir) < 0 ) {
|
|
ERROR_INFO("Cannot change current directory to %s", dir);
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
fiasco_print_info(fiasco);
|
|
|
|
image_list = fiasco->first;
|
|
|
|
while ( image_list ) {
|
|
|
|
image = image_list->image;
|
|
|
|
printf("\n");
|
|
printf("Unpacking image...\n");
|
|
image_print_info(image);
|
|
|
|
if ( image->layout ) {
|
|
|
|
name = image_name_alloc_from_values(image, -1);
|
|
if ( ! name )
|
|
ALLOC_ERROR_RETURN(-1);
|
|
|
|
layout_name = calloc(1, strlen(name) + sizeof("_layout")-1 + 1);
|
|
if ( ! layout_name ) {
|
|
free(name);
|
|
ALLOC_ERROR_RETURN(-1);
|
|
}
|
|
|
|
sprintf(layout_name, "%s_layout", name);
|
|
free(name);
|
|
|
|
printf(" Layout file: %s\n", layout_name);
|
|
|
|
if ( ! simulate ) {
|
|
fd = open(layout_name, O_RDWR|O_CREAT|O_TRUNC, 0644);
|
|
if ( fd < 0 ) {
|
|
ERROR_INFO("Cannot create layout file %s", layout_name);
|
|
free(layout_name);
|
|
return -1;
|
|
}
|
|
|
|
size = strlen(image->layout);
|
|
|
|
if ( write(fd, image->layout, size) != (ssize_t)size ) {
|
|
ERROR_INFO_STR(layout_name, "Cannot write %d bytes", size);
|
|
close(fd);
|
|
free(layout_name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(layout_name);
|
|
|
|
if ( ! simulate )
|
|
close(fd);
|
|
|
|
}
|
|
|
|
part_num = 0;
|
|
image_part = image->parts;
|
|
|
|
do {
|
|
|
|
offset = image_part ? image_part->offset : 0;
|
|
total_size = image_part ? image_part->size : image->size;
|
|
|
|
name = image_name_alloc_from_values(image, image_part ? part_num : -1);
|
|
if ( ! name )
|
|
ALLOC_ERROR_RETURN(-1);
|
|
|
|
if ( image_part && ( part_num > 0 || image_part->next ) )
|
|
printf(" Output file part %d: %s\n", part_num+1, name);
|
|
else
|
|
printf(" Output file: %s\n", name);
|
|
|
|
if ( ! simulate ) {
|
|
fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
|
|
if ( fd < 0 ) {
|
|
ERROR_INFO("Cannot create output file %s", name);
|
|
free(name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
written = 0;
|
|
image_seek(image, offset);
|
|
while ( written < total_size ) {
|
|
need = total_size - written;
|
|
if ( need > sizeof(buf) )
|
|
need = sizeof(buf);
|
|
size = image_read(image, buf, need);
|
|
if ( size == 0 )
|
|
break;
|
|
if ( ! simulate ) {
|
|
if ( write(fd, buf, size) != (ssize_t)size ) {
|
|
ERROR_INFO_STR(name, "Cannot write %d bytes", size);
|
|
close(fd);
|
|
free(name);
|
|
return -1;
|
|
}
|
|
}
|
|
written += size;
|
|
}
|
|
|
|
free(name);
|
|
|
|
if ( ! simulate )
|
|
close(fd);
|
|
|
|
if ( image_part ) {
|
|
image_part = image_part->next;
|
|
part_num++;
|
|
}
|
|
|
|
} while ( image_part );
|
|
|
|
image_list = image_list->next;
|
|
|
|
}
|
|
|
|
if ( dir ) {
|
|
if ( chdir(cwd) < 0 ) {
|
|
ERROR_INFO("Cannot change current directory back to %s", cwd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
printf("\nDone\n\n");
|
|
return 0;
|
|
|
|
}
|
|
|
|
void fiasco_print_info(struct fiasco * fiasco) {
|
|
|
|
if ( fiasco->orig_filename )
|
|
printf("File: %s\n", fiasco->orig_filename);
|
|
|
|
if ( fiasco->name[0] )
|
|
printf(" Fiasco Name: %s\n", fiasco->name);
|
|
|
|
if ( fiasco->swver[0] )
|
|
printf(" Fiasco Software release version: %s\n", fiasco->swver);
|
|
|
|
}
|