7c8748a661
implementing disk image support with an external C++ class. With the new code it's now easier to implement support for additional disk images formats, so we don't need the old stub anymore.
1394 lines
43 KiB
C++
1394 lines
43 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2001-2021 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
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Portions of this file contain code released under the following license.
|
|
//
|
|
// Connectix / Microsoft Virtual PC image creation code
|
|
//
|
|
// Copyright (c) 2005 Alex Beregszaszi
|
|
// Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
|
|
//
|
|
// VMDK version 4 image creation code
|
|
//
|
|
// Copyright (c) 2004 Fabrice Bellard
|
|
// Copyright (c) 2005 Filip Navara
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Create empty hard disk or floppy disk images for bochs.
|
|
// Convert a hard disk image from one format (mode) to another.
|
|
// Resize a hard disk image.
|
|
// Commit a redolog file to a supported base image.
|
|
|
|
#ifndef BXIMAGE
|
|
#define BXIMAGE
|
|
#endif
|
|
|
|
#include "config.h"
|
|
#include "bxcompat.h"
|
|
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
# include <conio.h>
|
|
# include <winioctl.h>
|
|
#endif
|
|
#include <ctype.h>
|
|
|
|
#include "osdep.h"
|
|
#include "bswap.h"
|
|
|
|
#include "iodev/hdimage/hdimage.h"
|
|
#include "iodev/hdimage/vmware3.h"
|
|
#include "iodev/hdimage/vmware4.h"
|
|
#include "iodev/hdimage/vpc.h"
|
|
|
|
#define BXIMAGE_MODE_NULL 0
|
|
#define BXIMAGE_MODE_CREATE_IMAGE 1
|
|
#define BXIMAGE_MODE_CONVERT_IMAGE 2
|
|
#define BXIMAGE_MODE_RESIZE_IMAGE 3
|
|
#define BXIMAGE_MODE_COMMIT_UNDOABLE 4
|
|
#define BXIMAGE_MODE_IMAGE_INFO 5
|
|
|
|
#define BX_MAX_CYL_BITS 24 // 8 TB
|
|
|
|
const int bx_max_hd_megs = (int)(((1 << BX_MAX_CYL_BITS) - 1) * 16.0 * 63.0 / 2048.0);
|
|
|
|
enum {
|
|
BX_HDIMAGE_MODE_FLAT,
|
|
BX_HDIMAGE_MODE_CONCAT,
|
|
BX_HDIMAGE_MODE_DLL_HD,
|
|
BX_HDIMAGE_MODE_SPARSE,
|
|
BX_HDIMAGE_MODE_VMWARE3,
|
|
BX_HDIMAGE_MODE_VMWARE4,
|
|
BX_HDIMAGE_MODE_UNDOABLE,
|
|
BX_HDIMAGE_MODE_GROWING,
|
|
BX_HDIMAGE_MODE_VOLATILE,
|
|
BX_HDIMAGE_MODE_VVFAT,
|
|
BX_HDIMAGE_MODE_VPC,
|
|
BX_HDIMAGE_MODE_VBOX
|
|
};
|
|
|
|
const char *hdimage_mode_names[] = {
|
|
"flat",
|
|
"concat",
|
|
"dll",
|
|
"sparse",
|
|
"vmware3",
|
|
"vmware4",
|
|
"undoable",
|
|
"growing",
|
|
"volatile",
|
|
"vvfat",
|
|
"vpc",
|
|
NULL
|
|
};
|
|
|
|
int bximage_mode;
|
|
int bx_hdimage;
|
|
int bx_fdsize_idx;
|
|
int bx_min_hd_megs = 10;
|
|
int bx_hdsize;
|
|
int bx_imagemode;
|
|
int bx_backup;
|
|
int bx_interactive;
|
|
int bx_sectsize_idx;
|
|
Bit16u bx_sectsize_val;
|
|
char bx_filename_1[512];
|
|
char bx_filename_2[522];
|
|
|
|
const char *EOF_ERR = "ERROR: End of input";
|
|
const char *svnid = "$Id$";
|
|
const char *divider = "========================================================================";
|
|
const char *main_menu_prompt =
|
|
"\n"
|
|
"1. Create new floppy or hard disk image\n"
|
|
"2. Convert hard disk image to other format (mode)\n"
|
|
"3. Resize hard disk image\n"
|
|
"4. Commit 'undoable' redolog to base image\n"
|
|
"5. Disk image info\n"
|
|
"\n"
|
|
"0. Quit\n"
|
|
"\n"
|
|
"Please choose one ";
|
|
|
|
// menu data for choosing floppy/hard disk
|
|
const char *fdhd_menu = "\nDo you want to create a floppy disk image or a hard disk image?\nPlease type hd or fd. ";
|
|
const char *fdhd_choices[] = { "fd", "hd" };
|
|
int fdhd_n_choices = 2;
|
|
|
|
// menu data for choosing floppy size
|
|
const char *fdsize_menu = "\nChoose the size of floppy disk image to create.\nPlease type 160k, 180k, 320k, 360k, 720k, 1.2M, 1.44M, 1.68M, 1.72M, or 2.88M.\n ";
|
|
const char *fdsize_choices[] = { "160k","180k","320k","360k","720k","1.2M","1.44M","1.68M","1.72M","2.88M" };
|
|
const unsigned fdsize_sectors[] = { 320, 360, 640, 720, 1440, 2400, 2880, 3360, 3444, 5760 };
|
|
int fdsize_n_choices = 10;
|
|
|
|
// menu data for choosing disk mode
|
|
const char *hdmode_menu = "\nWhat kind of image should I create?\nPlease type flat, sparse, growing, vpc or vmware4. ";
|
|
const char *hdmode_choices[] = {"flat", "sparse", "growing", "vpc", "vmware4" };
|
|
const int hdmode_choice_id[] = {BX_HDIMAGE_MODE_FLAT, BX_HDIMAGE_MODE_SPARSE,
|
|
BX_HDIMAGE_MODE_GROWING, BX_HDIMAGE_MODE_VPC,
|
|
BX_HDIMAGE_MODE_VMWARE4};
|
|
int hdmode_n_choices = 5;
|
|
|
|
// menu data for choosing hard disk sector size
|
|
const char *sectsize_menu = "\nChoose the size of hard disk sectors.\nPlease type 512, 1024 or 4096. ";
|
|
const char *sectsize_choices[] = { "512","1024","4096" };
|
|
int sectsize_n_choices = 3;
|
|
|
|
int hdimage_get_mode_id(const char *mode)
|
|
{
|
|
int i = 0;
|
|
|
|
while (hdimage_mode_names[i] != NULL) {
|
|
if (!strcmp(mode, hdimage_mode_names[i])) return i;
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#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 */
|
|
|
|
#if !BX_HAVE_MKSTEMP
|
|
int bx_mkstemp(char *tpl)
|
|
{
|
|
mktemp(tpl);
|
|
return ::open(tpl, O_RDWR | O_CREAT | O_TRUNC
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
, S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP);
|
|
}
|
|
#endif // !BX_HAVE_MKSTEMP
|
|
|
|
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, "bximage\n", 72);
|
|
bx_center_print(stdout, "Disk Image Creation / Conversion / Resize and 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);
|
|
}
|
|
|
|
/* check if the argument string is present in the list -
|
|
returns index on success, -1 on failure. */
|
|
int get_menu_index(char *arg, int n_choices, const char *choice[])
|
|
{
|
|
int i;
|
|
for (i=0; i<n_choices; i++) {
|
|
if (!strcmp(choice[i], arg)) {
|
|
// matched, return the choice number
|
|
return i;
|
|
}
|
|
}
|
|
return -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) {
|
|
fprintf(stderr, "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_menu(const char *prompt, int n_choices, const char *choice[], int the_default, int *out)
|
|
{
|
|
char buffer[1024];
|
|
char *clean;
|
|
int i;
|
|
*out = -1;
|
|
while(1) {
|
|
printf("%s", prompt);
|
|
printf("[%s] ", choice[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;
|
|
}
|
|
for (i=0; i<n_choices; i++) {
|
|
if (!strcmp(choice[i], clean)) {
|
|
// matched, return the choice number
|
|
*out = i;
|
|
return 0;
|
|
}
|
|
}
|
|
printf("Your choice (%s) did not match any of the choices:\n", clean);
|
|
for (i=0; i<n_choices; i++) {
|
|
if (i>0) printf(", ");
|
|
printf("%s", choice[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
fprintf(stderr, "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;
|
|
}
|
|
|
|
device_image_t* init_image(int image_mode)
|
|
{
|
|
device_image_t *hdimage = NULL;
|
|
|
|
// instantiate the right class
|
|
switch (image_mode) {
|
|
|
|
case BX_HDIMAGE_MODE_FLAT:
|
|
hdimage = new flat_image_t();
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_CONCAT:
|
|
hdimage = new concat_image_t();
|
|
break;
|
|
|
|
#ifdef WIN32
|
|
case BX_HDIMAGE_MODE_DLL_HD:
|
|
hdimage = new dll_image_t();
|
|
break;
|
|
#endif
|
|
|
|
case BX_HDIMAGE_MODE_SPARSE:
|
|
hdimage = new sparse_image_t();
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_VMWARE3:
|
|
hdimage = new vmware3_image_t();
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_VMWARE4:
|
|
hdimage = new vmware4_image_t();
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_GROWING:
|
|
hdimage = new growing_image_t();
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_VPC:
|
|
hdimage = new vpc_image_t();
|
|
break;
|
|
|
|
default:
|
|
fatal("unsupported disk image mode");
|
|
break;
|
|
}
|
|
return hdimage;
|
|
}
|
|
|
|
device_image_t* init_image_2(const char *image_mode)
|
|
{
|
|
return init_image(hdimage_get_mode_id(image_mode));
|
|
}
|
|
|
|
int create_image_file(const char *filename)
|
|
{
|
|
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP
|
|
);
|
|
if (fd < 0) {
|
|
fatal("ERROR: flat file is not writable");
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
void create_flat_image(const char *filename, Bit64u size)
|
|
{
|
|
char buffer[512];
|
|
|
|
int fd = create_image_file(filename);
|
|
memset(buffer, 0, 512);
|
|
if (bx_write_image(fd, size - 512, buffer, 512) != 512)
|
|
fatal("ERROR: while writing block in flat file !");
|
|
close(fd);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
void create_flat_image_win32(const char *filename, Bit64u size)
|
|
{
|
|
HANDLE hFile;
|
|
LARGE_INTEGER pos;
|
|
DWORD dwCount, errCode;
|
|
USHORT mode;
|
|
char buffer[1024];
|
|
|
|
hFile = CreateFile(filename, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
// attempt to print an error
|
|
#ifdef HAVE_PERROR
|
|
sprintf(buffer, "while opening '%s' for writing", filename);
|
|
perror(buffer);
|
|
#endif
|
|
fatal("ERROR: Could not write disk image");
|
|
}
|
|
|
|
SetLastError(NO_ERROR);
|
|
mode = COMPRESSION_FORMAT_DEFAULT;
|
|
dwCount = 0;
|
|
memset(buffer, 0, 512);
|
|
WriteFile(hFile, buffer, 512, &dwCount, NULL); // set the first sector to 0, Win98 doesn't zero out the file
|
|
// if there is a write at/over the end
|
|
DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, &mode, sizeof(mode), NULL, 0, &dwCount, NULL);
|
|
pos.u.LowPart = (unsigned long)((size - 512));
|
|
pos.u.HighPart = (unsigned long)((size - 512) >> 32);
|
|
pos.u.LowPart = SetFilePointer(hFile, pos.u.LowPart, &pos.u.HighPart, FILE_BEGIN);
|
|
memset(buffer, 0, 512);
|
|
if ((pos.u.LowPart == 0xffffffff && GetLastError() != NO_ERROR) ||
|
|
!WriteFile(hFile, buffer, 512, &dwCount, NULL) || (dwCount != 512)) {
|
|
errCode = GetLastError();
|
|
CloseHandle(hFile);
|
|
if (errCode == ERROR_DISK_FULL) {
|
|
fatal("\nERROR: Not enough space on disk for image!");
|
|
} else {
|
|
sprintf(buffer, "\nERROR: Disk image creation failed with error code %i!", errCode);
|
|
fatal(buffer);
|
|
}
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
#endif
|
|
|
|
void create_sparse_image(const char *filename, Bit64u size)
|
|
{
|
|
Bit64u numpages;
|
|
sparse_header_t header;
|
|
size_t sizesofar;
|
|
int padtopagesize;
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
header.magic = htod32(SPARSE_HEADER_MAGIC);
|
|
header.version = htod32(SPARSE_HEADER_VERSION);
|
|
|
|
header.pagesize = htod32((1 << 10) * 32); // Use 32 KB Pages - could be configurable
|
|
numpages = (size / dtoh32(header.pagesize)) + 1;
|
|
|
|
header.numpages = htod32((Bit32u)numpages);
|
|
header.disk = htod64(size);
|
|
|
|
if (numpages != dtoh32(header.numpages)) {
|
|
fatal("ERROR: The disk image is too large for a sparse image!");
|
|
// Could increase page size here.
|
|
// But note this only happens at 128 Terabytes!
|
|
}
|
|
|
|
int fd = create_image_file(filename);
|
|
if (bx_write_image(fd, 0, &header, sizeof(header)) != sizeof(header)) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write header!");
|
|
}
|
|
|
|
Bit32u *pagetable = new Bit32u[dtoh32(header.numpages)];
|
|
if (pagetable == NULL)
|
|
fatal("ERROR: The disk image is not complete - could not create pagetable!");
|
|
for (Bit32u i=0; i<dtoh32(header.numpages); i++)
|
|
pagetable[i] = htod32(SPARSE_PAGE_NOT_ALLOCATED);
|
|
|
|
if (bx_write_image(fd, sizeof(header), pagetable, 4 * dtoh32(header.numpages)) != (int)(4 * dtoh32(header.numpages))) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write pagetable!");
|
|
}
|
|
delete [] pagetable;
|
|
|
|
sizesofar = SPARSE_HEADER_SIZE + (4 * dtoh32(header.numpages));
|
|
padtopagesize = dtoh32(header.pagesize) - (sizesofar & (dtoh32(header.pagesize) - 1));
|
|
|
|
Bit8u *padding = new Bit8u[padtopagesize];
|
|
memset(padding, 0, padtopagesize);
|
|
|
|
if (bx_write_image(fd, sizesofar, padding, padtopagesize) != padtopagesize) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write padding!");
|
|
}
|
|
delete [] padding;
|
|
close(fd);
|
|
}
|
|
|
|
void create_growing_image(const char *filename, Bit64u size)
|
|
{
|
|
redolog_t *redolog = new redolog_t;
|
|
if (redolog->create(filename, REDOLOG_SUBTYPE_GROWING, size) < 0)
|
|
fatal("Can't create growing mode image");
|
|
redolog->close();
|
|
delete redolog;
|
|
}
|
|
|
|
void create_vpc_image(const char *filename, Bit64u size)
|
|
{
|
|
Bit8u buf[1024];
|
|
Bit16u cyls = 0;
|
|
Bit8u heads = 16, secs_per_cyl = 63;
|
|
Bit64u total_sectors;
|
|
Bit64s offset;
|
|
size_t block_size, num_bat_entries;
|
|
int fd, i;
|
|
|
|
total_sectors = size >> 9;
|
|
cyls = (Bit16u)(size/heads/secs_per_cyl/512.0);
|
|
|
|
vhd_footer_t *footer = (vhd_footer_t*)buf;
|
|
memset(footer, 0, HEADER_SIZE);
|
|
memcpy(footer->creator, "conectix", 8);
|
|
// TODO Check if "bxic" creator_app is ok for VPC
|
|
memcpy(footer->creator_app, "bxic", 4);
|
|
memcpy(footer->creator_os, "Wi2k", 4);
|
|
|
|
footer->features = be32_to_cpu(0x02);
|
|
footer->version = be32_to_cpu(0x00010000);
|
|
footer->data_offset = be64_to_cpu(HEADER_SIZE);
|
|
footer->timestamp = be32_to_cpu((Bit32u)time(NULL) - VHD_TIMESTAMP_BASE);
|
|
|
|
// Version of Virtual PC 2007
|
|
footer->major = be16_to_cpu(0x0005);
|
|
footer->minor = be16_to_cpu(0x0003);
|
|
footer->orig_size = be64_to_cpu(total_sectors * 512);
|
|
footer->size = be64_to_cpu(total_sectors * 512);
|
|
footer->cyls = be16_to_cpu(cyls);
|
|
footer->heads = heads;
|
|
footer->secs_per_cyl = secs_per_cyl;
|
|
footer->type = be32_to_cpu(VHD_DYNAMIC);
|
|
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
|
|
|
fd = create_image_file(filename);
|
|
// Write the footer (twice: at the beginning and at the end)
|
|
block_size = 0x200000;
|
|
num_bat_entries = (size_t)(total_sectors + block_size / 512) / (block_size / 512);
|
|
if (bx_write_image(fd, 0, footer, HEADER_SIZE) != HEADER_SIZE) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write footer!");
|
|
}
|
|
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
|
if (bx_write_image(fd, offset, footer, HEADER_SIZE) != HEADER_SIZE) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write footer!");
|
|
}
|
|
|
|
// Write the initial BAT
|
|
memset(buf, 0xFF, 512);
|
|
offset = 3 * 512;
|
|
for (i = 0; i < (int)(num_bat_entries * 4 + 511) / 512; i++) {
|
|
if (bx_write_image(fd, offset, buf, 512) != 512) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write BAT!");
|
|
}
|
|
offset += 512;
|
|
}
|
|
|
|
vhd_dyndisk_header_t *header = (vhd_dyndisk_header_t*)buf;
|
|
memset(header, 0, 1024);
|
|
memcpy(header->magic, "cxsparse", 8);
|
|
/*
|
|
* Note: The spec is actually wrong here for data_offset, it says
|
|
* 0xFFFFFFFF, but MS tools expect all 64 bits to be set.
|
|
*/
|
|
header->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
|
header->table_offset = be64_to_cpu(3 * 512);
|
|
header->version = be32_to_cpu(0x00010000);
|
|
header->block_size = be32_to_cpu(block_size);
|
|
header->max_table_entries = be32_to_cpu(num_bat_entries);
|
|
header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
|
|
if (bx_write_image(fd, 512, header, 1024) != 1024) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write header!");
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
|
|
void create_vmware4_image(const char *filename, Bit64u size)
|
|
{
|
|
const int SECTOR_SIZE = 512;
|
|
const char desc_template[] =
|
|
"# Disk DescriptorFile\n"
|
|
"version=1\n"
|
|
"CID=%x\n"
|
|
"parentCID=ffffffff\n"
|
|
"createType=\"monolithicSparse\"\n"
|
|
"\n"
|
|
"# Extent description\n"
|
|
"%s"
|
|
"\n"
|
|
"# The Disk Data Base\n"
|
|
"#DDB\n"
|
|
"\n"
|
|
"ddb.virtualHWVersion = \"4\"\n"
|
|
"ddb.geometry.cylinders = \"" FMT_LL "d\"\n"
|
|
"ddb.geometry.heads = \"16\"\n"
|
|
"ddb.geometry.sectors = \"63\"\n"
|
|
"ddb.adapterType = \"ide\"\n";
|
|
int fd, i;
|
|
Bit64s offset, cyl;
|
|
Bit32u tmp, grains, gt_size, gt_count, gd_size;
|
|
Bit8u buffer[SECTOR_SIZE];
|
|
char desc_line[256];
|
|
_VM4_Header header;
|
|
|
|
header.id[0] = 'K';
|
|
header.id[1] = 'D';
|
|
header.id[2] = 'M';
|
|
header.id[3] = 'V';
|
|
header.version = htod32(1);
|
|
header.flags = htod32(3);
|
|
header.total_sectors = htod64(size / SECTOR_SIZE);
|
|
header.tlb_size_sectors = 128;
|
|
header.description_offset_sectors = 1;
|
|
header.description_size_sectors = 20;
|
|
header.slb_count = 512;
|
|
header.flb_offset_sectors = header.description_offset_sectors +
|
|
header.description_size_sectors;
|
|
|
|
grains = (Bit32u)((size / 512 + header.tlb_size_sectors - 1) / header.tlb_size_sectors);
|
|
gt_size = ((header.slb_count * sizeof(Bit32u)) + 511) >> 9;
|
|
gt_count = (grains + header.slb_count - 1) / header.slb_count;
|
|
gd_size = (gt_count * sizeof(Bit32u) + 511) >> 9;
|
|
|
|
header.flb_copy_offset_sectors = header.flb_offset_sectors + gd_size + (gt_size * gt_count);
|
|
header.tlb_offset_sectors =
|
|
((header.flb_copy_offset_sectors + gd_size + (gt_size * gt_count) +
|
|
header.tlb_size_sectors - 1) / header.tlb_size_sectors) *
|
|
header.tlb_size_sectors;
|
|
|
|
header.tlb_size_sectors = htod64(header.tlb_size_sectors);
|
|
header.description_offset_sectors = htod64(header.description_offset_sectors);
|
|
header.description_size_sectors = htod64(header.description_size_sectors);
|
|
header.slb_count = htod32(header.slb_count);
|
|
header.flb_offset_sectors = htod64(header.flb_offset_sectors);
|
|
header.flb_copy_offset_sectors = htod64(header.flb_copy_offset_sectors);
|
|
header.tlb_offset_sectors = htod64(header.tlb_offset_sectors);
|
|
header.check_bytes[0] = 0x0a;
|
|
header.check_bytes[1] = 0x20;
|
|
header.check_bytes[2] = 0x0d;
|
|
header.check_bytes[3] = 0x0a;
|
|
|
|
memset(buffer, 0, SECTOR_SIZE);
|
|
memcpy(buffer, &header, sizeof(_VM4_Header));
|
|
|
|
fd = create_image_file(filename);
|
|
if (bx_write_image(fd, 0, buffer, SECTOR_SIZE) != SECTOR_SIZE) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write header!");
|
|
}
|
|
memset(buffer, 0, SECTOR_SIZE);
|
|
offset = dtoh64(header.tlb_offset_sectors * SECTOR_SIZE) - SECTOR_SIZE;
|
|
if (bx_write_image(fd, offset, buffer, SECTOR_SIZE) != SECTOR_SIZE) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write empty table!");
|
|
}
|
|
offset = dtoh64(header.flb_offset_sectors) * SECTOR_SIZE;
|
|
for (i = 0, tmp = (Bit32u)dtoh64(header.flb_offset_sectors) + gd_size; i < (int)gt_count; i++, tmp += gt_size) {
|
|
if (bx_write_image(fd, offset, &tmp, sizeof(tmp)) != sizeof(tmp)) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write table!");
|
|
}
|
|
offset += sizeof(tmp);
|
|
}
|
|
offset = dtoh64(header.flb_copy_offset_sectors) * SECTOR_SIZE;
|
|
for (i = 0, tmp = (Bit32u)dtoh64(header.flb_copy_offset_sectors) + gd_size; i < (int)gt_count; i++, tmp += gt_size) {
|
|
if (bx_write_image(fd, offset, &tmp, sizeof(tmp)) != sizeof(tmp)) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write backup table!");
|
|
}
|
|
offset += sizeof(tmp);
|
|
}
|
|
memset(buffer, 0, SECTOR_SIZE);
|
|
cyl = (Bit64u)(size / 16 / 63 / 512.0);
|
|
snprintf(desc_line, 256, "RW " FMT_LL "d SPARSE \"%s\"", size / SECTOR_SIZE, filename);
|
|
sprintf((char*)buffer, desc_template, (Bit32u)time(NULL), desc_line, cyl);
|
|
offset = dtoh64(header.description_offset_sectors) * SECTOR_SIZE;
|
|
if (bx_write_image(fd, offset, buffer, SECTOR_SIZE) != SECTOR_SIZE) {
|
|
close(fd);
|
|
fatal("ERROR: The disk image is not complete - could not write description!");
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
void create_hard_disk_image(const char *filename, int imgmode, Bit64u size)
|
|
{
|
|
switch (imgmode) {
|
|
case BX_HDIMAGE_MODE_FLAT:
|
|
#ifndef WIN32
|
|
create_flat_image(filename, size);
|
|
#else
|
|
create_flat_image_win32(filename, size);
|
|
#endif
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_GROWING:
|
|
create_growing_image(filename, size);
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_SPARSE:
|
|
create_sparse_image(filename, size);
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_VPC:
|
|
create_vpc_image(filename, size);
|
|
break;
|
|
|
|
case BX_HDIMAGE_MODE_VMWARE4:
|
|
create_vmware4_image(filename, size);
|
|
break;
|
|
|
|
default:
|
|
fatal("image mode not implemented yet");
|
|
}
|
|
}
|
|
|
|
void convert_image(int newimgmode, Bit64u newsize)
|
|
{
|
|
device_image_t *source_image, *dest_image;
|
|
int mode = -1;
|
|
Bit64u i, sc, s;
|
|
char buffer[512], null_sector[512];
|
|
const char *image_mode = NULL;
|
|
bx_bool error = 0;
|
|
|
|
printf("\n");
|
|
memset(null_sector, 0, 512);
|
|
if (newsize == 0) {
|
|
if (!strncmp(bx_filename_1, "concat:", 7)) {
|
|
image_mode = "concat";
|
|
strcpy(bx_filename_1, &bx_filename_1[7]);
|
|
#ifdef WIN32
|
|
} else if (!strncmp(bx_filename_1, "dll:", 4)) {
|
|
image_mode = "dll";
|
|
strcpy(bx_filename_1, &bx_filename_1[4]);
|
|
#endif
|
|
}
|
|
}
|
|
if (access(bx_filename_1, F_OK) < 0) {
|
|
fatal("source disk image doesn't exist");
|
|
}
|
|
if (image_mode == NULL) {
|
|
if (hdimage_detect_image_mode(bx_filename_1, &image_mode)) {
|
|
mode = hdimage_get_mode_id(image_mode);
|
|
}
|
|
}
|
|
if (mode == -1) {
|
|
fatal("source disk image mode not detected");
|
|
} else {
|
|
printf("source image mode = '%s'\n", hdimage_mode_names[mode]);
|
|
}
|
|
|
|
source_image = init_image(mode);
|
|
if (source_image->open(bx_filename_1, O_RDONLY) < 0)
|
|
fatal("cannot open source disk image");
|
|
|
|
if (newsize > 0) {
|
|
create_hard_disk_image(bx_filename_2, newimgmode, newsize);
|
|
} else {
|
|
create_hard_disk_image(bx_filename_2, newimgmode, source_image->hd_size);
|
|
}
|
|
dest_image = init_image(newimgmode);
|
|
if (dest_image->open(bx_filename_2) < 0)
|
|
fatal("cannot open destination disk image");
|
|
|
|
printf("\nConverting image file: [ 0%%]");
|
|
|
|
sc = source_image->hd_size / 512;
|
|
s = 0;
|
|
for (i = 0; i < source_image->hd_size; i+=512) {
|
|
printf("\x8\x8\x8\x8\x8%3d%%]", (int)((s+1)*100/sc));
|
|
fflush(stdout);
|
|
if (source_image->lseek(i, SEEK_SET) < 0) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
if ((source_image->read(buffer, 512) == 512) &&
|
|
(memcmp(buffer, null_sector, 512) != 0)) {
|
|
if (dest_image->lseek(i, SEEK_SET) < 0) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
if (dest_image->write(buffer, 512) < 0) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
}
|
|
s++;
|
|
}
|
|
|
|
source_image->close();
|
|
dest_image->close();
|
|
delete dest_image;
|
|
delete source_image;
|
|
|
|
if (error) {
|
|
fatal("image conversion failed");
|
|
} else {
|
|
printf(" Done.\n");
|
|
}
|
|
}
|
|
|
|
void commit_redolog()
|
|
{
|
|
device_image_t *base_image;
|
|
redolog_t *redolog;
|
|
int mode = -1, ret;
|
|
const char *image_mode = NULL;
|
|
|
|
printf("\n");
|
|
if (access(bx_filename_1, F_OK) < 0) {
|
|
fatal("base disk image doesn't exist");
|
|
}
|
|
if (hdimage_detect_image_mode(bx_filename_1, &image_mode)) {
|
|
mode = hdimage_get_mode_id(image_mode);
|
|
}
|
|
if (mode == -1)
|
|
fatal("base disk image mode not detected");
|
|
|
|
base_image = init_image(mode);
|
|
if (base_image->open(bx_filename_1) < 0)
|
|
fatal("cannot open base disk image");
|
|
|
|
redolog = new redolog_t;
|
|
if (redolog->open(bx_filename_2, REDOLOG_SUBTYPE_UNDOABLE) < 0)
|
|
fatal("Can't open redolog");
|
|
if (!coherency_check(base_image, redolog))
|
|
fatal("coherency check failed");
|
|
|
|
ret = redolog->commit(base_image);
|
|
|
|
base_image->close();
|
|
redolog->close();
|
|
delete base_image;
|
|
delete redolog;
|
|
|
|
if (ret < 0) {
|
|
fatal("redolog commit failed");
|
|
} else {
|
|
printf(" Done.\n\n");
|
|
}
|
|
}
|
|
|
|
void print_usage()
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: bximage [options] [filename1] [filename2]\n\n"
|
|
"Supported options:\n"
|
|
" -mode=... operation mode (create, convert, resize, commit, info)\n"
|
|
" -fd=... create: floppy image with size code\n"
|
|
" -hd=... create/resize: hard disk image with size in megabytes (M)\n"
|
|
" or gigabytes (G)\n"
|
|
" -imgmode=... create/convert: hard disk image mode\n"
|
|
" -sectsize=... create: hard disk sector size\n"
|
|
" -b convert/resize: create a backup of the source image\n"
|
|
" commit: create backups of the base image and redolog file\n"
|
|
" -q quiet mode (don't prompt for user input)\n"
|
|
" --help display this help and exit\n\n"
|
|
"Other arguments:\n"
|
|
" filename1 create: new image file\n"
|
|
" convert/resize: source image file\n"
|
|
" commit: base image file\n"
|
|
" filename2 convert/resize: destination image file\n"
|
|
" commit: redolog (journal) file\n\n");
|
|
}
|
|
|
|
void set_default_values()
|
|
{
|
|
if (bximage_mode == BXIMAGE_MODE_CREATE_IMAGE) {
|
|
if (bx_hdimage == -1) {
|
|
bx_hdimage = 1;
|
|
bx_fdsize_idx = 6;
|
|
bx_interactive = 1;
|
|
}
|
|
if (bx_hdimage == 1) {
|
|
if (bx_imagemode == -1) {
|
|
bx_imagemode = 0;
|
|
bx_interactive = 1;
|
|
}
|
|
if (bx_hdsize == 0) {
|
|
bx_hdsize = 10;
|
|
bx_interactive = 1;
|
|
}
|
|
} else {
|
|
if (bx_fdsize_idx == -1) {
|
|
bx_fdsize_idx = 6;
|
|
bx_interactive = 1;
|
|
}
|
|
}
|
|
} else if (bximage_mode == BXIMAGE_MODE_CONVERT_IMAGE) {
|
|
if (bx_imagemode == -1) {
|
|
bx_imagemode = 0;
|
|
bx_interactive = 1;
|
|
}
|
|
} else if (bximage_mode == BXIMAGE_MODE_RESIZE_IMAGE) {
|
|
if (bx_hdsize == 0) {
|
|
bx_interactive = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int parse_cmdline(int argc, char *argv[])
|
|
{
|
|
int arg = 1;
|
|
int ret = 1;
|
|
int fnargs = 0;
|
|
char *suffix;
|
|
|
|
bximage_mode = BXIMAGE_MODE_NULL;
|
|
bx_hdimage = -1;
|
|
bx_fdsize_idx = -1;
|
|
bx_hdsize = 0;
|
|
bx_imagemode = -1;
|
|
bx_backup = 0;
|
|
bx_interactive = 1;
|
|
bx_sectsize_idx = 0;
|
|
bx_sectsize_val = 512;
|
|
bx_filename_1[0] = 0;
|
|
bx_filename_2[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 (bximage_mode != BXIMAGE_MODE_NULL) {
|
|
printf("bximage mode already defined\n\n");
|
|
ret = 0;
|
|
} else if (!strcmp(&argv[arg][6], "create")) {
|
|
bximage_mode = BXIMAGE_MODE_CREATE_IMAGE;
|
|
} else if (!strcmp(&argv[arg][6], "convert")) {
|
|
bximage_mode = BXIMAGE_MODE_CONVERT_IMAGE;
|
|
} else if (!strcmp(&argv[arg][6], "resize")) {
|
|
bximage_mode = BXIMAGE_MODE_RESIZE_IMAGE;
|
|
} else if (!strcmp(&argv[arg][6], "commit")) {
|
|
bximage_mode = BXIMAGE_MODE_COMMIT_UNDOABLE;
|
|
} else if (!strcmp(&argv[arg][6], "info")) {
|
|
bximage_mode = BXIMAGE_MODE_IMAGE_INFO;
|
|
} else {
|
|
printf("Unknown bximage mode '%s'\n\n", &argv[arg][6]);
|
|
ret = 0;
|
|
}
|
|
}
|
|
else if (!strncmp("-fd=", argv[arg], 4)) {
|
|
bx_hdimage = 0;
|
|
bx_fdsize_idx = get_menu_index(&argv[arg][4], fdsize_n_choices, fdsize_choices);
|
|
if (bx_fdsize_idx < 0) {
|
|
printf("Unknown floppy image size: %s\n\n", &argv[arg][6]);
|
|
ret = 0;
|
|
}
|
|
}
|
|
else if (!strncmp("-hd=", argv[arg], 4)) {
|
|
bx_hdimage = 1;
|
|
bx_imagemode = 0;
|
|
bx_hdsize = (int)strtol(&argv[arg][4], &suffix, 10);
|
|
if (bx_hdsize <= 0) {
|
|
printf("Error in hard disk image size argument: %s\n\n", &argv[arg][4]);
|
|
ret = 0;
|
|
} else if (!strcmp(suffix, "G")) {
|
|
bx_hdsize <<= 10;
|
|
} else if ((strlen(suffix) > 0) && strcmp(suffix, "M")) {
|
|
printf("Error in hard disk image size suffix: %s\n\n", &argv[arg][4]);
|
|
ret = 0;
|
|
}
|
|
if ((bx_hdsize < bx_min_hd_megs) || (bx_hdsize > bx_max_hd_megs)) {
|
|
printf("Hard disk image size out of range\n\n");
|
|
ret = 0;
|
|
}
|
|
}
|
|
else if (!strncmp("-imgmode=", argv[arg], 9)) {
|
|
bx_imagemode = get_menu_index(&argv[arg][9], hdmode_n_choices, hdmode_choices);
|
|
if (bx_imagemode < 0) {
|
|
printf("Unknown image mode: %s\n\n", &argv[arg][9]);
|
|
ret = 0;
|
|
}
|
|
}
|
|
else if (!strncmp("-sectsize=", argv[arg], 10)) {
|
|
bx_sectsize_idx = get_menu_index(&argv[arg][10], sectsize_n_choices, sectsize_choices);
|
|
if (bx_sectsize_idx < 0) {
|
|
printf("Unsupported hard disk sector size: %s\n\n", &argv[arg][10]);
|
|
ret = 0;
|
|
} else {
|
|
bx_sectsize_val = atoi(sectsize_choices[bx_sectsize_idx]);
|
|
}
|
|
}
|
|
else if (!strcmp("-b", argv[arg])) {
|
|
bx_backup = 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_filename_1, argv[arg]);
|
|
} else if (fnargs == 1) {
|
|
strcpy(bx_filename_2, argv[arg]);
|
|
} else {
|
|
printf("Ignoring extra parameter: %s\n\n", argv[arg]);
|
|
}
|
|
fnargs++;
|
|
}
|
|
arg++;
|
|
}
|
|
if (bximage_mode == BXIMAGE_MODE_NULL) {
|
|
bx_interactive = 1;
|
|
} else {
|
|
set_default_values();
|
|
if (fnargs < 1) {
|
|
bx_interactive = 1;
|
|
}
|
|
if ((bximage_mode == BXIMAGE_MODE_COMMIT_UNDOABLE) && (fnargs == 1)) {
|
|
snprintf(bx_filename_2, 520, "%s%s", bx_filename_1, UNDOABLE_REDOLOG_EXTENSION);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void image_overwrite_check(const char *filename)
|
|
{
|
|
char buffer[512];
|
|
|
|
int fd = hdimage_open_file(filename, O_RDONLY, NULL, NULL);
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
int confirm;
|
|
sprintf(buffer, "\nThe disk image '%s' already exists. Are you sure you want to replace it?\nPlease type yes or no. ", filename);
|
|
if (ask_yn(buffer, 0, &confirm) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!confirm)
|
|
fatal("ERROR: Aborted");
|
|
}
|
|
}
|
|
|
|
void check_image_names()
|
|
{
|
|
char backup_fname[520];
|
|
|
|
if (!strlen(bx_filename_2)) {
|
|
strcpy(bx_filename_2, bx_filename_1);
|
|
}
|
|
if ((!strcmp(bx_filename_1, bx_filename_2)) && bx_backup) {
|
|
snprintf(backup_fname, 517, "%s%s", bx_filename_1, ".orig");
|
|
if (hdimage_copy_file(bx_filename_1, backup_fname) != 1)
|
|
fatal("backup of source image file failed");
|
|
} else if (strcmp(bx_filename_1, bx_filename_2)) {
|
|
image_overwrite_check(bx_filename_2);
|
|
}
|
|
}
|
|
|
|
int get_image_mode_and_hdsize(const char *filename, int *hdsize_megs)
|
|
{
|
|
device_image_t *source_image;
|
|
int mode = -1;
|
|
const char *image_mode = NULL;
|
|
|
|
if (hdimage_detect_image_mode(bx_filename_1, &image_mode)) {
|
|
mode = hdimage_get_mode_id(image_mode);
|
|
}
|
|
if (mode == -1)
|
|
fatal("source disk image mode not detected");
|
|
source_image = init_image(mode);
|
|
if (source_image->open(bx_filename_1, O_RDONLY) < 0) {
|
|
fatal("cannot open source disk image");
|
|
} else {
|
|
*hdsize_megs = (int)(source_image->hd_size >> 20);
|
|
source_image->close();
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
int CDECL main(int argc, char *argv[])
|
|
{
|
|
char bochsrc_line[1024], prompt[80], tmpfname[528];
|
|
int imgmode = 0;
|
|
Bit64u hdsize = 0;
|
|
device_image_t *hdimage;
|
|
const char *image_mode = NULL;
|
|
|
|
if (!parse_cmdline(argc, argv))
|
|
myexit(1);
|
|
|
|
print_banner();
|
|
|
|
if (bx_interactive) {
|
|
if (ask_int(main_menu_prompt, 0, 5, bximage_mode, &bximage_mode) < 0)
|
|
fatal(EOF_ERR);
|
|
|
|
set_default_values();
|
|
switch (bximage_mode) {
|
|
case BXIMAGE_MODE_NULL:
|
|
myexit(0);
|
|
break;
|
|
|
|
case BXIMAGE_MODE_CREATE_IMAGE:
|
|
printf("\nCreate image\n");
|
|
if (ask_menu(fdhd_menu, fdhd_n_choices, fdhd_choices, bx_hdimage, &bx_hdimage) < 0)
|
|
fatal(EOF_ERR);
|
|
if (bx_hdimage == 0) { // floppy
|
|
if (ask_menu(fdsize_menu, fdsize_n_choices, fdsize_choices, bx_fdsize_idx, &bx_fdsize_idx) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "a.img");
|
|
}
|
|
if (ask_string("\nWhat should be the name of the image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
} else { // hard disk
|
|
if (ask_menu(hdmode_menu, hdmode_n_choices, hdmode_choices, bx_imagemode, &bx_imagemode) < 0)
|
|
fatal(EOF_ERR);
|
|
imgmode = hdmode_choice_id[bx_imagemode];
|
|
if ((imgmode == BX_HDIMAGE_MODE_FLAT) ||
|
|
(imgmode == BX_HDIMAGE_MODE_SPARSE) ||
|
|
(imgmode == BX_HDIMAGE_MODE_GROWING)) {
|
|
if (ask_menu(sectsize_menu, sectsize_n_choices, sectsize_choices, bx_sectsize_idx, &bx_sectsize_idx) < 0)
|
|
fatal(EOF_ERR);
|
|
bx_sectsize_val = atoi(sectsize_choices[bx_sectsize_idx]);
|
|
} else if (bx_sectsize_val != 512) {
|
|
fatal(EOF_ERR);
|
|
}
|
|
sprintf(prompt, "\nEnter the hard disk size in megabytes, between %d and %d\n",
|
|
bx_min_hd_megs, bx_max_hd_megs);
|
|
if (ask_int(prompt, bx_min_hd_megs, bx_max_hd_megs, bx_hdsize, &bx_hdsize) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "c.img");
|
|
}
|
|
if (ask_string("\nWhat should be the name of the image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
}
|
|
break;
|
|
|
|
case BXIMAGE_MODE_CONVERT_IMAGE:
|
|
printf("\nConvert image\n");
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the source image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_filename_2)) {
|
|
strcpy(tmpfname, bx_filename_1);
|
|
} else {
|
|
strcpy(tmpfname, bx_filename_2);
|
|
}
|
|
if (ask_string("\nWhat should be the name of the new image?\n", tmpfname, bx_filename_2) < 0)
|
|
fatal(EOF_ERR);
|
|
if (ask_menu(hdmode_menu, hdmode_n_choices, hdmode_choices, bx_imagemode, &bx_imagemode) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strcmp(bx_filename_1, bx_filename_2)) {
|
|
if (ask_yn("\nShould I create a backup of the source image\n", 0, &bx_backup) < 0)
|
|
fatal(EOF_ERR);
|
|
}
|
|
break;
|
|
|
|
case BXIMAGE_MODE_RESIZE_IMAGE:
|
|
printf("\nResize image\n");
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the source image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_filename_2)) {
|
|
strcpy(tmpfname, bx_filename_1);
|
|
} else {
|
|
strcpy(tmpfname, bx_filename_2);
|
|
}
|
|
if (ask_string("\nWhat should be the name of the new image?\n", tmpfname, bx_filename_2) < 0)
|
|
fatal(EOF_ERR);
|
|
imgmode = get_image_mode_and_hdsize(bx_filename_1, &bx_min_hd_megs);
|
|
sprintf(prompt, "\nEnter the new hard disk size in megabytes, between %d and %d\n",
|
|
bx_min_hd_megs, bx_max_hd_megs);
|
|
if (bx_hdsize < bx_min_hd_megs) bx_hdsize = bx_min_hd_megs;
|
|
if (ask_int(prompt, bx_min_hd_megs, bx_max_hd_megs, bx_hdsize, &bx_hdsize) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strcmp(bx_filename_1, bx_filename_2)) {
|
|
if (ask_yn("\nShould I create a backup of the source image\n", 0, &bx_backup) < 0)
|
|
fatal(EOF_ERR);
|
|
}
|
|
break;
|
|
|
|
case BXIMAGE_MODE_COMMIT_UNDOABLE:
|
|
printf("\nCommit redolog\n");
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the base image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
if (!strlen(bx_filename_2)) {
|
|
snprintf(tmpfname, 520, "%s%s", bx_filename_1, UNDOABLE_REDOLOG_EXTENSION);
|
|
} else {
|
|
strcpy(tmpfname, bx_filename_2);
|
|
}
|
|
if (ask_string("\nWhat is the redolog name?\n", tmpfname, bx_filename_2) < 0)
|
|
fatal(EOF_ERR);
|
|
if (ask_yn("\nShould I create a backup of base image and redolog\n", 0, &bx_backup) < 0)
|
|
fatal(EOF_ERR);
|
|
break;
|
|
|
|
case BXIMAGE_MODE_IMAGE_INFO:
|
|
printf("\nDisk image info\n");
|
|
if (!strlen(bx_filename_1)) {
|
|
strcpy(bx_filename_1, "c.img");
|
|
}
|
|
if (ask_string("\nWhat is the name of the image?\n", bx_filename_1, bx_filename_1) < 0)
|
|
fatal(EOF_ERR);
|
|
break;
|
|
|
|
default:
|
|
fatal("\nbximage: unknown mode");
|
|
}
|
|
}
|
|
switch (bximage_mode) {
|
|
case BXIMAGE_MODE_CREATE_IMAGE:
|
|
image_overwrite_check(bx_filename_1);
|
|
if (bx_hdimage == 0) {
|
|
sprintf(bochsrc_line, "floppya: image=\"%s\", status=inserted", bx_filename_1);
|
|
printf("\nCreating floppy image '%s' with %u sectors\n", bx_filename_1, fdsize_sectors[bx_fdsize_idx]);
|
|
create_flat_image(bx_filename_1, fdsize_sectors[bx_fdsize_idx] * 512);
|
|
} else {
|
|
int heads = 16, spt = 63;
|
|
|
|
if (bx_sectsize_val == 512) {
|
|
sprintf(bochsrc_line, "ata0-master: type=disk, path=\"%s\", mode=%s",
|
|
bx_filename_1, hdmode_choices[bx_imagemode]);
|
|
} else {
|
|
sprintf(bochsrc_line, "ata0-master: type=disk, path=\"%s\", mode=%s, sect_size=%d",
|
|
bx_filename_1, hdmode_choices[bx_imagemode], bx_sectsize_val);
|
|
}
|
|
imgmode = hdmode_choice_id[bx_imagemode];
|
|
hdsize = ((Bit64u)bx_hdsize) << 20;
|
|
Bit64u cyl = (Bit64u)(hdsize/16.0/63.0/bx_sectsize_val);
|
|
if (cyl >= (1 << BX_MAX_CYL_BITS))
|
|
fatal("ERROR: number of cylinders out of range !\n");
|
|
printf("\nCreating hard disk image '%s' with CHS=" FMT_LL "d/%d/%d (sector size = %d)\n",
|
|
bx_filename_1, cyl, heads, spt, bx_sectsize_val);
|
|
hdsize = cyl * heads * spt * bx_sectsize_val;
|
|
create_hard_disk_image(bx_filename_1, imgmode, hdsize);
|
|
}
|
|
printf("\nThe following line should appear in your bochsrc:\n");
|
|
printf(" %s\n", bochsrc_line);
|
|
#ifdef WIN32
|
|
if (OpenClipboard(NULL)) {
|
|
HGLOBAL hgClip;
|
|
EmptyClipboard();
|
|
hgClip = GlobalAlloc(GMEM_DDESHARE, (strlen(bochsrc_line) + 1));
|
|
strcpy((char *)GlobalLock(hgClip), bochsrc_line);
|
|
GlobalUnlock(hgClip);
|
|
SetClipboardData(CF_TEXT, hgClip);
|
|
CloseClipboard();
|
|
printf("(The line is stored in your windows clipboard, use CTRL-V to paste)\n");
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case BXIMAGE_MODE_CONVERT_IMAGE:
|
|
check_image_names();
|
|
imgmode = hdmode_choice_id[bx_imagemode];
|
|
convert_image(imgmode, 0);
|
|
break;
|
|
|
|
case BXIMAGE_MODE_RESIZE_IMAGE:
|
|
imgmode = get_image_mode_and_hdsize(bx_filename_1, &bx_min_hd_megs);
|
|
if (bx_hdsize < bx_min_hd_megs)
|
|
fatal("invalid image size");
|
|
hdsize = ((Bit64u)bx_hdsize) << 20;
|
|
check_image_names();
|
|
convert_image(imgmode, hdsize);
|
|
break;
|
|
|
|
case BXIMAGE_MODE_COMMIT_UNDOABLE:
|
|
if (bx_backup) {
|
|
snprintf(tmpfname, 517, "%s%s", bx_filename_1, ".orig");
|
|
if (hdimage_copy_file(bx_filename_1, tmpfname) != 1)
|
|
fatal("backup of base image file failed");
|
|
}
|
|
commit_redolog();
|
|
if (bx_backup) {
|
|
snprintf(tmpfname, 527, "%s%s", bx_filename_2, ".orig");
|
|
if (rename(bx_filename_2, tmpfname) != 0)
|
|
fatal("rename of redolog file failed");
|
|
} else {
|
|
if (unlink(bx_filename_2) != 0)
|
|
fatal("ERROR: while removing the redolog !\n");
|
|
}
|
|
break;
|
|
|
|
case BXIMAGE_MODE_IMAGE_INFO:
|
|
imgmode = -1;
|
|
if (hdimage_detect_image_mode(bx_filename_1, &image_mode)) {
|
|
imgmode = hdimage_get_mode_id(image_mode);
|
|
}
|
|
if (imgmode == -1) {
|
|
fatal("disk image mode not detected");
|
|
} else {
|
|
printf("\ndisk image mode = '%s'\n", hdimage_mode_names[imgmode]);
|
|
}
|
|
hdimage = init_image(imgmode);
|
|
if (hdimage->open(bx_filename_1, O_RDONLY) < 0) {
|
|
fatal("cannot open source disk image");
|
|
} else {
|
|
if (hdimage->get_capabilities() & HDIMAGE_AUTO_GEOMETRY) {
|
|
Bit64u cyl = (Bit64u)(hdimage->hd_size/16.0/63.0/512.0);
|
|
printf("geometry = " FMT_LL "d/16/63 (" FMT_LL "d MB)\n\n", cyl,
|
|
hdimage->hd_size >> 20);
|
|
} else {
|
|
printf("geometry = %d/%d/%d (" FMT_LL "d MB)\n\n", hdimage->cylinders,
|
|
hdimage->heads, hdimage->spt, hdimage->hd_size >> 20);
|
|
}
|
|
hdimage->close();
|
|
}
|
|
break;
|
|
}
|
|
myexit(0);
|
|
|
|
return 0;
|
|
}
|