891 lines
26 KiB
C
891 lines
26 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2003-2013 The Bochs Project
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/* Commits a redolog file to a 'flat' or 'growing' mode base image. */
|
|
/* Converts growing mode image to flat and vice versa */
|
|
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
# include <conio.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#else
|
|
#include <io.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#if !BX_HAVE_SNPRINTF
|
|
#include <stdarg.h>
|
|
/* XXX use real snprintf */
|
|
/* if they don't have snprintf, just use sprintf */
|
|
int snprintf(char *s, size_t maxlen, const char *format, ...)
|
|
{
|
|
va_list arg;
|
|
int done;
|
|
|
|
va_start(arg, format);
|
|
done = vsprintf(s, format, arg);
|
|
va_end(arg);
|
|
|
|
return done;
|
|
}
|
|
#endif /* !BX_HAVE_SNPRINTF */
|
|
|
|
#include "../osdep.h"
|
|
#include "bswap.h"
|
|
|
|
#define HDIMAGE_HEADERS_ONLY 1
|
|
#include "../iodev/hdimage/hdimage.h"
|
|
|
|
#define BXCOMMIT_MODE_COMMIT_UNDOABLE 1
|
|
#define BXCOMMIT_MODE_GROWING_TO_FLAT 2
|
|
#define BXCOMMIT_MODE_FLAT_TO_GROWING 3
|
|
|
|
#define BXCOMMIT_BASE_FLAT 1
|
|
#define BXCOMMIT_BASE_GROWING 2
|
|
|
|
typedef struct {
|
|
int fd;
|
|
redolog_header_t header;
|
|
Bit32u *catalog;
|
|
Bit8u *bitmap;
|
|
bx_bool bitmap_update;
|
|
Bit32u extent_index;
|
|
Bit32u extent_offset;
|
|
Bit32u extent_next;
|
|
|
|
Bit32u bitmap_blocks;
|
|
Bit32u extent_blocks;
|
|
|
|
Bit64s imagepos;
|
|
} redolog_t;
|
|
|
|
int bxcommit_mode;
|
|
int bx_remove;
|
|
int bx_interactive;
|
|
char bx_flat_filename[256];
|
|
char bx_redolog_name[256];
|
|
Bit8u null_sector[512];
|
|
|
|
const char *EOF_ERR = "ERROR: End of input";
|
|
const char *svnid = "$Id$";
|
|
const char *divider = "========================================================================";
|
|
const char *main_menu_prompt =
|
|
"\n"
|
|
"1. Commit 'undoable' redolog to 'flat' or 'growing' mode base image\n"
|
|
"2. Create 'flat' disk image from 'growing' disk image\n"
|
|
"3. Create 'growing' disk image from 'flat' disk image\n"
|
|
"\n"
|
|
"0. Quit\n"
|
|
"\n"
|
|
"Please choose one ";
|
|
|
|
void myexit(int code)
|
|
{
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
printf("\nPress any key to continue\n");
|
|
getch();
|
|
#endif
|
|
exit(code);
|
|
}
|
|
|
|
/* stolen from main.cc */
|
|
void bx_center_print(FILE *file, const char *line, int maxwidth)
|
|
{
|
|
int imax;
|
|
int i;
|
|
imax = (maxwidth - strlen(line)) >> 1;
|
|
for (i=0; i<imax; i++) fputc(' ', file);
|
|
fputs(line, file);
|
|
}
|
|
|
|
void print_banner()
|
|
{
|
|
printf("%s\n", divider);
|
|
bx_center_print(stdout, "bxcommit\n", 72);
|
|
bx_center_print(stdout, "Growing / Undoable Disk Image Commit Tool for Bochs\n", 72);
|
|
bx_center_print(stdout, svnid, 72);
|
|
printf("\n%s\n", divider);
|
|
}
|
|
|
|
/* this is how we crash */
|
|
void fatal(const char *c)
|
|
{
|
|
printf("%s\n", c);
|
|
myexit(1);
|
|
}
|
|
|
|
/* remove leading spaces, newline junk at end. returns pointer to
|
|
cleaned string, which is between s0 and the null */
|
|
char *clean_string(char *s0)
|
|
{
|
|
char *s = s0;
|
|
char *ptr;
|
|
/* find first nonblank */
|
|
while (isspace(*s))
|
|
s++;
|
|
/* truncate string at first non-alphanumeric */
|
|
ptr = s;
|
|
while (isprint(*ptr))
|
|
ptr++;
|
|
*ptr = 0;
|
|
return s;
|
|
}
|
|
|
|
/* returns 0 on success, -1 on failure. The value goes into out. */
|
|
int ask_int(const char *prompt, int min, int max, int the_default, int *out)
|
|
{
|
|
int n = max + 1;
|
|
char buffer[1024];
|
|
char *clean;
|
|
int illegal;
|
|
while (1) {
|
|
printf("%s", prompt);
|
|
printf("[%d] ", the_default);
|
|
if (!fgets(buffer, sizeof(buffer), stdin))
|
|
return -1;
|
|
clean = clean_string(buffer);
|
|
if (strlen(clean) < 1) {
|
|
// empty line, use the default
|
|
*out = the_default;
|
|
return 0;
|
|
}
|
|
illegal = (1 != sscanf(buffer, "%d", &n));
|
|
if (illegal || n<min || n>max) {
|
|
printf("Your choice (%s) was not an integer between %d and %d.\n\n",
|
|
clean, min, max);
|
|
} else {
|
|
// choice is okay
|
|
*out = n;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ask_yn(const char *prompt, int the_default, int *out)
|
|
{
|
|
char buffer[16];
|
|
char *clean;
|
|
*out = -1;
|
|
while (1) {
|
|
printf("%s", prompt);
|
|
printf("[%s] ", the_default?"yes":"no");
|
|
if (!fgets(buffer, sizeof(buffer), stdin))
|
|
return -1;
|
|
clean = clean_string(buffer);
|
|
if (strlen(clean) < 1) {
|
|
// empty line, use the default
|
|
*out = the_default;
|
|
return 0;
|
|
}
|
|
switch (tolower(clean[0])) {
|
|
case 'y': *out=1; return 0;
|
|
case 'n': *out=0; return 0;
|
|
}
|
|
printf("Please type either yes or no.\n");
|
|
}
|
|
}
|
|
|
|
int ask_string(const char *prompt, char *the_default, char *out)
|
|
{
|
|
char buffer[1024];
|
|
char *clean;
|
|
printf("%s", prompt);
|
|
printf("[%s] ", the_default);
|
|
if (!fgets(buffer, sizeof(buffer), stdin))
|
|
return -1;
|
|
clean = clean_string(buffer);
|
|
if (strlen(clean) < 1) {
|
|
// empty line, use the default
|
|
strcpy(out, the_default);
|
|
return 0;
|
|
}
|
|
strcpy(out, clean);
|
|
return 0;
|
|
}
|
|
|
|
/* helper functions */
|
|
int bx_read_image(int fd, Bit64s offset, void *buf, int count)
|
|
{
|
|
if (lseek(fd, offset, SEEK_SET) == -1) {
|
|
return -1;
|
|
}
|
|
return read(fd, buf, count);
|
|
}
|
|
|
|
int bx_write_image(int fd, Bit64s offset, void *buf, int count)
|
|
{
|
|
if (lseek(fd, offset, SEEK_SET) == -1) {
|
|
return -1;
|
|
}
|
|
return write(fd, buf, count);
|
|
}
|
|
|
|
/* Read one sector and check if empty */
|
|
int flat_image_read_sector(int fd, Bit8u *buffer, Bit64u offset)
|
|
{
|
|
if (bx_read_image(fd, offset, buffer, 512) < 0)
|
|
return 0;
|
|
if (memcmp(buffer, null_sector, 512) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Create a suited redolog header */
|
|
void redolog_make_header(redolog_header_t *header, const char* type, Bit64u size)
|
|
{
|
|
Bit32u entries, extent_size, bitmap_size;
|
|
Bit64u maxsize;
|
|
|
|
// Set standard header values
|
|
strcpy((char*)header->standard.magic, STANDARD_HEADER_MAGIC);
|
|
strcpy((char*)header->standard.type, REDOLOG_TYPE);
|
|
strcpy((char*)header->standard.subtype, type);
|
|
header->standard.version = htod32(STANDARD_HEADER_VERSION);
|
|
header->standard.header = htod32(STANDARD_HEADER_SIZE);
|
|
|
|
entries = 512;
|
|
bitmap_size = 1;
|
|
|
|
// Compute #entries and extent size values
|
|
do {
|
|
static Bit32u flip=0;
|
|
|
|
extent_size = 8 * bitmap_size * 512;
|
|
|
|
header->specific.catalog = htod32(entries);
|
|
header->specific.bitmap = htod32(bitmap_size);
|
|
header->specific.extent = htod32(extent_size);
|
|
|
|
maxsize = (Bit64u)entries * (Bit64u)extent_size;
|
|
|
|
flip++;
|
|
|
|
if(flip&0x01) bitmap_size *= 2;
|
|
else entries *= 2;
|
|
} while (maxsize < size);
|
|
|
|
header->specific.disk = htod64(size);
|
|
}
|
|
|
|
/* Redolog functions */
|
|
|
|
int redolog_open(redolog_t *r, int fd, redolog_header_t *header)
|
|
{
|
|
int res;
|
|
Bit32u i;
|
|
|
|
memset(r, 0, sizeof(redolog_t));
|
|
r->fd = fd;
|
|
memcpy(&r->header, header, sizeof(redolog_header_t));
|
|
r->catalog = (Bit32u*)malloc(dtoh32(r->header.specific.catalog) * sizeof(Bit32u));
|
|
printf("\nReading Catalog: [");
|
|
res = bx_read_image(r->fd, dtoh32(r->header.standard.header),
|
|
r->catalog, dtoh32(r->header.specific.catalog) * sizeof(Bit32u));
|
|
if (res != (ssize_t)(dtoh32(r->header.specific.catalog) * sizeof(Bit32u))) {
|
|
fatal("ERROR: redolog : could not read catalog");
|
|
}
|
|
printf("...] Done.");
|
|
r->extent_next = 0;
|
|
for (i = 0; i < dtoh32(r->header.specific.catalog); i++) {
|
|
if (dtoh32(r->catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED) {
|
|
if (dtoh32(r->catalog[i]) >= r->extent_next)
|
|
r->extent_next = dtoh32(r->catalog[i]) + 1;
|
|
}
|
|
}
|
|
r->bitmap = (Bit8u *)malloc(dtoh32(r->header.specific.bitmap));
|
|
r->bitmap_blocks = 1 + (dtoh32(r->header.specific.bitmap) - 1) / 512;
|
|
r->extent_blocks = 1 + (dtoh32(r->header.specific.extent) - 1) / 512;
|
|
r->bitmap_update = 1;
|
|
return 0;
|
|
}
|
|
|
|
int redolog_close(redolog_t *r)
|
|
{
|
|
if (r->fd >= 0)
|
|
close(r->fd);
|
|
|
|
if (r->catalog != NULL)
|
|
free(r->catalog);
|
|
|
|
if (r->bitmap != NULL)
|
|
free(r->bitmap);
|
|
return 0;
|
|
}
|
|
|
|
int redolog_check_format(int fd, const char *subtype, redolog_header_t *header)
|
|
{
|
|
redolog_header_t temp_header;
|
|
|
|
int res = bx_read_image(fd, 0, &temp_header, sizeof(redolog_header_t));
|
|
if (res != STANDARD_HEADER_SIZE) {
|
|
return HDIMAGE_READ_ERROR;
|
|
}
|
|
|
|
if (strcmp((char*)temp_header.standard.magic, STANDARD_HEADER_MAGIC) != 0) {
|
|
return HDIMAGE_NO_SIGNATURE;
|
|
}
|
|
|
|
if (strcmp((char*)temp_header.standard.type, REDOLOG_TYPE) != 0) {
|
|
return HDIMAGE_TYPE_ERROR;
|
|
}
|
|
if (strcmp((char*)temp_header.standard.subtype, subtype) != 0) {
|
|
return HDIMAGE_TYPE_ERROR;
|
|
}
|
|
|
|
if (dtoh32(temp_header.standard.version) != STANDARD_HEADER_VERSION) {
|
|
return HDIMAGE_VERSION_ERROR;
|
|
}
|
|
memcpy(header, &temp_header, sizeof(redolog_header_t));
|
|
return HDIMAGE_FORMAT_OK;
|
|
}
|
|
|
|
Bit64s redolog_lseek(redolog_t *redolog, Bit64s offset, int whence)
|
|
{
|
|
char msg[80];
|
|
Bit32u old_extent_index;
|
|
|
|
if (whence == SEEK_SET) {
|
|
redolog->imagepos = offset;
|
|
} else if (whence == SEEK_CUR) {
|
|
redolog->imagepos += offset;
|
|
} else {
|
|
fatal("redolog: lseek() mode not supported yet");
|
|
return -1;
|
|
}
|
|
if (redolog->imagepos > (Bit64s)dtoh64(redolog->header.specific.disk)) {
|
|
sprintf(msg, "redolog : lseek() to byte %ld failed", (long)offset);
|
|
fatal(msg);
|
|
return -1;
|
|
}
|
|
|
|
old_extent_index = redolog->extent_index;
|
|
redolog->extent_index = (Bit32u)(redolog->imagepos / dtoh32(redolog->header.specific.extent));
|
|
if (redolog->extent_index != old_extent_index) {
|
|
redolog->bitmap_update = 1;
|
|
}
|
|
redolog->extent_offset = (Bit32u)((redolog->imagepos % dtoh32(redolog->header.specific.extent)) / 512);
|
|
|
|
return redolog->imagepos;
|
|
}
|
|
|
|
int redolog_write_sector(redolog_t *redolog, Bit8u *buf, Bit64u offset)
|
|
{
|
|
Bit32u i;
|
|
Bit64s block_offset, bitmap_offset, catalog_offset;
|
|
ssize_t written;
|
|
bx_bool update_catalog = 0;
|
|
char *zerobuffer;
|
|
char msg[80];
|
|
|
|
redolog_lseek(redolog, offset, SEEK_SET);
|
|
|
|
if (dtoh32(redolog->catalog[redolog->extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) {
|
|
if (redolog->extent_next >= dtoh32(redolog->header.specific.catalog)) {
|
|
fatal("redolog : can't allocate new extent... catalog is full");
|
|
return -1;
|
|
}
|
|
|
|
// Extent not allocated, allocate new
|
|
redolog->catalog[redolog->extent_index] = htod32(redolog->extent_next);
|
|
|
|
redolog->extent_next += 1;
|
|
|
|
zerobuffer = (char*)malloc(512);
|
|
memset(zerobuffer, 0, 512);
|
|
|
|
// Write bitmap
|
|
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(redolog->header.specific.catalog) * sizeof(Bit32u));
|
|
bitmap_offset += (Bit64s)512 * dtoh32(redolog->catalog[redolog->extent_index]) * (redolog->extent_blocks + redolog->bitmap_blocks);
|
|
lseek(redolog->fd, (off_t)bitmap_offset, SEEK_SET);
|
|
for (i=0; i<redolog->bitmap_blocks; i++) {
|
|
write(redolog->fd, zerobuffer, 512);
|
|
}
|
|
// Write extent
|
|
for (i=0; i<redolog->extent_blocks; i++) {
|
|
write(redolog->fd, zerobuffer, 512);
|
|
}
|
|
|
|
free(zerobuffer);
|
|
|
|
update_catalog = 1;
|
|
}
|
|
|
|
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(redolog->header.specific.catalog) * sizeof(Bit32u));
|
|
bitmap_offset += (Bit64s)512 * dtoh32(redolog->catalog[redolog->extent_index]) * (redolog->extent_blocks + redolog->bitmap_blocks);
|
|
block_offset = bitmap_offset + ((Bit64s)512 * (redolog->bitmap_blocks + redolog->extent_offset));
|
|
|
|
// Write block
|
|
written = bx_write_image(redolog->fd, (off_t)block_offset, (void*)buf, 512);
|
|
|
|
// Write bitmap
|
|
if (redolog->bitmap_update) {
|
|
if (bx_read_image(redolog->fd, (off_t)bitmap_offset, redolog->bitmap, dtoh32(redolog->header.specific.bitmap)) != (ssize_t)dtoh32(redolog->header.specific.bitmap)) {
|
|
sprintf(msg, "redolog : failed to read bitmap for extent %d", redolog->extent_index);
|
|
fatal(msg);
|
|
return 0;
|
|
}
|
|
redolog->bitmap_update = 0;
|
|
}
|
|
|
|
// If block does not belong to extent yet
|
|
if (((redolog->bitmap[redolog->extent_offset/8] >> (redolog->extent_offset%8)) & 0x01) == 0x00) {
|
|
redolog->bitmap[redolog->extent_offset/8] |= 1 << (redolog->extent_offset%8);
|
|
bx_write_image(redolog->fd, (off_t)bitmap_offset, redolog->bitmap, dtoh32(redolog->header.specific.bitmap));
|
|
}
|
|
|
|
// Write catalog
|
|
if (update_catalog) {
|
|
// FIXME if mmap
|
|
catalog_offset = (Bit64s)STANDARD_HEADER_SIZE + (redolog->extent_index * sizeof(Bit32u));
|
|
|
|
bx_write_image(redolog->fd, (off_t)catalog_offset, &redolog->catalog[redolog->extent_index], sizeof(Bit32u));
|
|
}
|
|
|
|
if (written >= 0) redolog_lseek(redolog, 512, SEEK_CUR);
|
|
|
|
return written;
|
|
}
|
|
|
|
int convert_flat_image()
|
|
{
|
|
int flatfd, redologfd;
|
|
Bit64u disk_size, offset;
|
|
redolog_t redolog;
|
|
Bit32u i;
|
|
struct stat stat_buf;
|
|
Bit8u buffer[512];
|
|
|
|
if (bxcommit_mode == BXCOMMIT_MODE_FLAT_TO_GROWING) {
|
|
// check if flat file exists
|
|
flatfd = open(bx_flat_filename, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (flatfd < 0) {
|
|
fatal("ERROR: flat file not found or not readable");
|
|
}
|
|
if (fstat(flatfd, &stat_buf)) {
|
|
fatal("fstat() returns error!");
|
|
}
|
|
disk_size = (Bit64u)stat_buf.st_size;
|
|
memset(null_sector, 0, 512);
|
|
|
|
redologfd = open(bx_redolog_name, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (redologfd >= 0) {
|
|
close(flatfd);
|
|
close(redologfd);
|
|
fatal("ERROR: growing file already exists");
|
|
}
|
|
fprintf(stderr, "\nCreating growing image file: [");
|
|
memset(&redolog, 0, sizeof(redolog_t));
|
|
redolog.fd = open(bx_redolog_name, O_RDWR | O_CREAT
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP
|
|
);
|
|
if (redolog.fd < 0) {
|
|
close(flatfd);
|
|
fatal("ERROR: redolog file is not writable");
|
|
}
|
|
redolog_make_header(&redolog.header, REDOLOG_SUBTYPE_GROWING, disk_size);
|
|
if (write(redolog.fd, &redolog.header, sizeof(redolog.header)) < 0) {
|
|
fatal("ERROR: The disk image is not complete - could not write header!");
|
|
} else {
|
|
lseek(redolog.fd, sizeof(redolog.header), SEEK_SET);
|
|
}
|
|
redolog.catalog = (Bit32u*)malloc(dtoh32(redolog.header.specific.catalog) * sizeof(Bit32u));
|
|
for (i=0; i<dtoh32(redolog.header.specific.catalog); i++) {
|
|
redolog.catalog[i] = htod32(REDOLOG_PAGE_NOT_ALLOCATED);
|
|
}
|
|
if (bx_write_image(redolog.fd, dtoh32(redolog.header.standard.header), redolog.catalog,
|
|
dtoh32(redolog.header.specific.catalog) * sizeof(Bit32u)) < 0) {
|
|
close(redolog.fd);
|
|
fatal("ERROR: The disk image is not complete - could not write catalog!");
|
|
}
|
|
redolog.bitmap = (Bit8u *)malloc(dtoh32(redolog.header.specific.bitmap));
|
|
redolog.bitmap_blocks = 1 + (dtoh32(redolog.header.specific.bitmap) - 1) / 512;
|
|
redolog.extent_blocks = 1 + (dtoh32(redolog.header.specific.extent) - 1) / 512;
|
|
redolog.bitmap_update = 1;
|
|
|
|
offset = 0;
|
|
while (offset < disk_size) {
|
|
if (flat_image_read_sector(flatfd, buffer, offset) > 0) {
|
|
if (redolog_write_sector(&redolog, buffer, offset) <= 0)
|
|
break;
|
|
}
|
|
offset += 512;
|
|
if (((offset >> 9) % (disk_size >> 15)) == 0) fprintf(stderr, ".");
|
|
}
|
|
fprintf(stderr, "]\n");
|
|
} else {
|
|
fatal("ERROR: unknown / unsupported mode");
|
|
}
|
|
|
|
close(flatfd);
|
|
close(redolog.fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* produce the image file */
|
|
int commit_redolog()
|
|
{
|
|
int flatfd = -1, redologfd, res = -1;
|
|
int destmode = BXCOMMIT_BASE_FLAT;
|
|
redolog_header_t header;
|
|
redolog_t r1, r2;
|
|
Bit32u i;
|
|
Bit8u buffer[512];
|
|
|
|
if (bxcommit_mode == BXCOMMIT_MODE_COMMIT_UNDOABLE) {
|
|
// check if flat file exists
|
|
flatfd = open(bx_flat_filename, O_RDWR
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (flatfd < 0) {
|
|
fatal("ERROR: flat file not found or not writable");
|
|
}
|
|
printf("\nMode of base image: ");
|
|
res = redolog_check_format(flatfd, REDOLOG_SUBTYPE_GROWING, &header);
|
|
if (res == HDIMAGE_FORMAT_OK) {
|
|
printf("'growing'\n");
|
|
redolog_open(&r1, flatfd, &header);
|
|
destmode = BXCOMMIT_BASE_GROWING;
|
|
} else {
|
|
printf("'flat'\n");
|
|
}
|
|
}
|
|
|
|
// Check if redolog exists
|
|
printf("\nOpening '%s'\n", bx_redolog_name);
|
|
redologfd = open(bx_redolog_name, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (redologfd < 0) {
|
|
fatal("ERROR: redolog file not found");
|
|
}
|
|
|
|
printf("\nReading redolog header: [");
|
|
|
|
if (bxcommit_mode == BXCOMMIT_MODE_COMMIT_UNDOABLE) {
|
|
res = redolog_check_format(redologfd, REDOLOG_SUBTYPE_UNDOABLE, &header);
|
|
} else if (bxcommit_mode == BXCOMMIT_MODE_GROWING_TO_FLAT) {
|
|
res = redolog_check_format(redologfd, REDOLOG_SUBTYPE_GROWING, &header);
|
|
} else {
|
|
fatal("\nERROR: unknown bxcommit mode!");
|
|
}
|
|
|
|
if (res != HDIMAGE_FORMAT_OK) {
|
|
switch (res) {
|
|
case HDIMAGE_READ_ERROR:
|
|
fatal("\nERROR: while reading redolog header!");
|
|
break;
|
|
case HDIMAGE_NO_SIGNATURE:
|
|
fatal("\nERROR: bad magic in redolog header!");
|
|
break;
|
|
case HDIMAGE_TYPE_ERROR:
|
|
fatal("\nERROR: bad type or subtype in redolog header!");
|
|
break;
|
|
case HDIMAGE_VERSION_ERROR:
|
|
fatal("\nERROR: bad version in redolog header!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print infos on redlog
|
|
printf("Type='%s', Subtype='%s', Version=%d.%d] Done.",
|
|
header.standard.type, header.standard.subtype,
|
|
dtoh32(header.standard.version)/0x10000,
|
|
dtoh32(header.standard.version)%0x10000);
|
|
|
|
printf("\nChecking redolog header: [");
|
|
|
|
printf("#entries=%d, bitmap size=%d, exent size = %d] Done.",
|
|
dtoh32(header.specific.catalog),
|
|
dtoh32(header.specific.bitmap),
|
|
dtoh32(header.specific.extent));
|
|
|
|
if (bxcommit_mode == BXCOMMIT_MODE_GROWING_TO_FLAT) {
|
|
flatfd = open(bx_flat_filename, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (flatfd >= 0) {
|
|
close(flatfd);
|
|
fatal("ERROR: flat file already exists");
|
|
}
|
|
printf("\nCreating flat image file: [");
|
|
flatfd = open(bx_flat_filename, O_WRONLY | O_CREAT
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP
|
|
);
|
|
if (flatfd < 0) {
|
|
fatal("ERROR: flat file is not writable");
|
|
}
|
|
lseek(flatfd, dtoh64(header.specific.disk) - 512, SEEK_SET);
|
|
if (write(flatfd, buffer, 512) != 512)
|
|
fatal("ERROR: while writing block in flat file !");
|
|
|
|
printf("...] Done.");
|
|
}
|
|
|
|
redolog_open(&r2, redologfd, &header);
|
|
|
|
printf("\nCommitting changes to base image file: [ 0%%]");
|
|
|
|
for(i=0; i<dtoh32(header.specific.catalog); i++) {
|
|
printf("\x8\x8\x8\x8\x8%3d%%]", (i+1)*100/dtoh32(header.specific.catalog));
|
|
fflush(stdout);
|
|
|
|
if (dtoh32(r2.catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED) {
|
|
Bit64s bitmap_offset;
|
|
Bit32u bitmap_size, j;
|
|
|
|
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
|
|
bitmap_offset += (Bit64s)512 * dtoh32(r2.catalog[i]) * (r2.extent_blocks + r2.bitmap_blocks);
|
|
|
|
// Read bitmap
|
|
lseek(redologfd, bitmap_offset, SEEK_SET);
|
|
|
|
bitmap_size = dtoh32(header.specific.bitmap);
|
|
if ((Bit32u) read(redologfd, r2.bitmap, bitmap_size) != bitmap_size)
|
|
fatal("\nERROR: while reading bitmap from redolog !");
|
|
|
|
for(j=0; j<dtoh32(header.specific.bitmap); j++) {
|
|
Bit32u bit;
|
|
|
|
for(bit=0; bit<8; bit++) {
|
|
if ( (r2.bitmap[j] & (1<<bit)) != 0) {
|
|
Bit64s flat_offset, block_offset;
|
|
|
|
block_offset = bitmap_offset + ((Bit64s)512 * (r2.bitmap_blocks + ((j*8)+bit)));
|
|
|
|
if (bx_read_image(redologfd, (off_t)block_offset, buffer, 512) != 512)
|
|
fatal("\nERROR: while reading block from redolog !");
|
|
|
|
flat_offset = (Bit64s)i * (dtoh32(header.specific.extent));
|
|
flat_offset += (Bit64s)512 * ((j * 8) + bit);
|
|
|
|
if (destmode == BXCOMMIT_BASE_FLAT) {
|
|
if (bx_write_image(flatfd, (off_t)flat_offset, buffer, 512) != 512)
|
|
fatal("\nERROR: while writing block in flat file !");
|
|
} else if (destmode == BXCOMMIT_BASE_GROWING) {
|
|
if (redolog_write_sector(&r1, buffer, flat_offset) <= 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
printf(" Done.");
|
|
printf("\n");
|
|
|
|
if (destmode == BXCOMMIT_BASE_FLAT) {
|
|
close(flatfd);
|
|
} else if (destmode == BXCOMMIT_BASE_GROWING) {
|
|
redolog_close(&r1);
|
|
}
|
|
redolog_close(&r2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void print_usage()
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: bxcommit [options] [flat filename] [redolog filename]\n\n"
|
|
"Supported options:\n"
|
|
" -mode=commit-undoable commit undoable redolog to base image file\n"
|
|
" -mode=growing-to-flat create flat disk image from growing disk image\n"
|
|
" -mode=flat-to-growing create growing disk image from flat disk image\n"
|
|
" -d delete source file after commit\n"
|
|
" -q quiet mode (don't prompt for user input)\n"
|
|
" --help display this help and exit\n\n");
|
|
}
|
|
|
|
int parse_cmdline(int argc, char *argv[])
|
|
{
|
|
int arg = 1;
|
|
int ret = 1;
|
|
int fnargs = 0;
|
|
|
|
bxcommit_mode = 0;
|
|
bx_remove = 0;
|
|
bx_interactive = 1;
|
|
bx_flat_filename[0] = 0;
|
|
bx_redolog_name[0] = 0;
|
|
while ((arg < argc) && (ret == 1)) {
|
|
// parse next arg
|
|
if (!strcmp("--help", argv[arg]) || !strncmp("/?", argv[arg], 2)) {
|
|
print_usage();
|
|
ret = 0;
|
|
}
|
|
else if (!strncmp("-mode=", argv[arg], 6)) {
|
|
if (!strcmp(&argv[arg][6], "commit-undoable")) {
|
|
bxcommit_mode = BXCOMMIT_MODE_COMMIT_UNDOABLE;
|
|
} else if (!strcmp(&argv[arg][6], "growing-to-flat")) {
|
|
bxcommit_mode = BXCOMMIT_MODE_GROWING_TO_FLAT;
|
|
} else if (!strcmp(&argv[arg][6], "flat-to-growing")) {
|
|
bxcommit_mode = BXCOMMIT_MODE_FLAT_TO_GROWING;
|
|
} else {
|
|
printf("Unknown bxcommit mode '%s'\n\n", &argv[arg][6]);
|
|
}
|
|
}
|
|
else if (!strcmp("-d", argv[arg])) {
|
|
bx_remove = 1;
|
|
}
|
|
else if (!strcmp("-q", argv[arg])) {
|
|
bx_interactive = 0;
|
|
}
|
|
else if (argv[arg][0] == '-') {
|
|
printf("Unknown option: %s\n\n", argv[arg]);
|
|
ret = 0;
|
|
} else {
|
|
if (fnargs == 0) {
|
|
strcpy(bx_flat_filename, argv[arg]);
|
|
} else if (fnargs == 1) {
|
|
strcpy(bx_redolog_name, argv[arg]);
|
|
} else {
|
|
printf("Ignoring extra parameter: %s\n\n", argv[arg]);
|
|
}
|
|
fnargs++;
|
|
}
|
|
arg++;
|
|
}
|
|
if ((bxcommit_mode == 0) ||
|
|
(fnargs < 2)) {
|
|
bx_interactive = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int CDECL main(int argc, char *argv[])
|
|
{
|
|
char tmplogname[256];
|
|
|
|
if (!parse_cmdline(argc, argv))
|
|
myexit(1);
|
|
|
|
print_banner();
|
|
|
|
if (bx_interactive) {
|
|
if (ask_int(main_menu_prompt, 0, 3, bxcommit_mode, &bxcommit_mode) < 0)
|
|
fatal(EOF_ERR);
|
|
|
|
switch (bxcommit_mode) {
|
|
case 0:
|
|
myexit(0);
|
|
break;
|
|
|
|
case BXCOMMIT_MODE_COMMIT_UNDOABLE:
|
|
if (!strlen(bx_flat_filename)) {
|
|
strcpy(bx_flat_filename, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the base image?\n", bx_flat_filename, bx_flat_filename) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_redolog_name)) {
|
|
snprintf(tmplogname, 256, "%s%s", bx_flat_filename, UNDOABLE_REDOLOG_EXTENSION);
|
|
} else {
|
|
strcpy(tmplogname, bx_redolog_name);
|
|
}
|
|
if (ask_string("\nWhat is the redolog name?\n", tmplogname, bx_redolog_name) < 0)
|
|
fatal(EOF_ERR);
|
|
if (ask_yn("\nShould the redolog been removed afterwards?\n", 1, &bx_remove) < 0)
|
|
fatal(EOF_ERR);
|
|
break;
|
|
|
|
case BXCOMMIT_MODE_GROWING_TO_FLAT:
|
|
if (!strlen(bx_redolog_name)) {
|
|
strcpy(bx_redolog_name, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the 'growing' image?\n", bx_redolog_name, bx_redolog_name) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_flat_filename)) {
|
|
strcpy(bx_flat_filename, "flat.img");
|
|
}
|
|
if (ask_string("\nWhat should be the name of the 'flat' image?\n", bx_flat_filename, bx_flat_filename) < 0)
|
|
fatal(EOF_ERR);
|
|
if (ask_yn("\nShould the 'growing' image been removed afterwards?\n", 0, &bx_remove) < 0)
|
|
fatal(EOF_ERR);
|
|
break;
|
|
|
|
case BXCOMMIT_MODE_FLAT_TO_GROWING:
|
|
if (!strlen(bx_flat_filename)) {
|
|
strcpy(bx_flat_filename, "flat.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the 'flat' image?\n", bx_flat_filename, bx_flat_filename) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_redolog_name)) {
|
|
strcpy(bx_redolog_name, "c.img");
|
|
}
|
|
if (ask_string("\nWhat should be the 'growing' image name?\n", bx_redolog_name, bx_redolog_name) < 0)
|
|
fatal(EOF_ERR);
|
|
if (ask_yn("\nShould the 'flat' image been removed afterwards?\n", 0, &bx_remove) < 0)
|
|
fatal(EOF_ERR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bxcommit_mode != BXCOMMIT_MODE_FLAT_TO_GROWING) {
|
|
commit_redolog();
|
|
if (bx_remove) {
|
|
if (unlink(bx_redolog_name) != 0)
|
|
fatal("ERROR: while removing the redolog !\n");
|
|
}
|
|
} else {
|
|
convert_flat_image();
|
|
if (bx_remove) {
|
|
if (unlink(bx_flat_filename) != 0)
|
|
fatal("ERROR: while removing the flat file !\n");
|
|
}
|
|
}
|
|
|
|
// make picky compilers (c++, gcc) happy,
|
|
// even though we leave via 'myexit' just above
|
|
return 0;
|
|
}
|