Bochs/bochs/iodev/hdimage.cc
Volker Ruppert aad2d89c83 - rewrite of the optional plugin control feature. Now the plugins are loaded
directly while parsing the bochsrc or command line. If plugin support is enabled,
  the option could load all optional plugins, not only the ones supported before.
  NOTE #1: The old option had all plugins enabled by default and gave the user
           a chance to diable them. Now the plugins are only loaded if they
           appear in the config line and they are set to "1".
  NOTE #2: Loading a plugin that is controlled by a bochsrc option is possible,
           but it currently leads to a panic, since the load command is still
           present in devices.cc.
  NOTE #3: The plugin init code creates the device object and registers the
           optional plugin device. As an option, it can create config parameters
           and register an option parser. The device init, register state and
           reset is still handled in devices.cc, but in the order the devices
           have been loaded with the plugin control.
  NOTE #4: If plugin support is disabled, the plugin control only accepts the
           devices listed in plugin.cc.
- plugin init of core plugins now fails if they are not loaded with the expected
  type. For core plugins the load order is important and they cannot be handled
  with the chained devices list (used for optional and user plugins).
- some additions for calling config.cc functions from a plugin device
2011-12-25 08:52:34 +00:00

1646 lines
42 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2002-2011 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
/////////////////////////////////////////////////////////////////////////
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#include "cdrom.h"
#include "hdimage.h"
#include "vmware3.h"
#include "vmware4.h"
#include "vvfat.h"
#if BX_HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef linux
#include <linux/fs.h>
#include <sys/ioctl.h>
#endif
#define LOG_THIS theHDImageCtl->
bx_hdimage_ctl_c* theHDImageCtl = NULL;
int libhdimage_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
{
if (type == PLUGTYPE_CORE) {
theHDImageCtl = new bx_hdimage_ctl_c;
bx_devices.pluginHDImageCtl = theHDImageCtl;
return 0; // Success
} else {
return -1;
}
}
void libhdimage_LTX_plugin_fini(void)
{
delete theHDImageCtl;
}
bx_hdimage_ctl_c::bx_hdimage_ctl_c()
{
put("IMG");
}
device_image_t* bx_hdimage_ctl_c::init_image(Bit8u image_mode, Bit64u disk_size, const char *journal)
{
device_image_t *hdimage = NULL;
// instantiate the right class
switch (image_mode) {
case BX_HDIMAGE_MODE_FLAT:
hdimage = new default_image_t();
break;
case BX_HDIMAGE_MODE_CONCAT:
hdimage = new concat_image_t();
break;
#if EXTERNAL_DISK_SIMULATOR
case BX_HDIMAGE_MODE_EXTDISKSIM:
hdimage = new EXTERNAL_DISK_SIMULATOR_CLASS();
break;
#endif //EXTERNAL_DISK_SIMULATOR
#if DLL_HD_SUPPORT
case BX_HDIMAGE_MODE_DLL_HD:
hdimage = new dll_image_t();
break;
#endif //DLL_HD_SUPPORT
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_UNDOABLE:
hdimage = new undoable_image_t(journal);
break;
case BX_HDIMAGE_MODE_GROWING:
hdimage = new growing_image_t();
break;
case BX_HDIMAGE_MODE_VOLATILE:
hdimage = new volatile_image_t(journal);
break;
case BX_HDIMAGE_MODE_VVFAT:
hdimage = new vvfat_image_t(disk_size, journal);
break;
default:
BX_PANIC(("unsupported HD mode : '%s'", hdimage_mode_names[image_mode]));
break;
}
return hdimage;
}
#ifdef LOWLEVEL_CDROM
LOWLEVEL_CDROM* bx_hdimage_ctl_c::init_cdrom(const char *dev)
{
return new LOWLEVEL_CDROM(dev);
}
#endif
/*** base class device_image_t ***/
device_image_t::device_image_t()
{
hd_size = 0;
}
Bit32u device_image_t::get_capabilities()
{
return (cylinders == 0) ? HDIMAGE_AUTO_GEOMETRY : 0;
}
/*** default_image_t function definitions ***/
int default_image_t::open(const char* pathname)
{
return open(pathname, O_RDWR);
}
int default_image_t::open(const char* pathname, int flags)
{
#ifdef WIN32
HANDLE hFile = CreateFile(pathname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
ULARGE_INTEGER FileSize;
FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
CloseHandle(hFile);
if ((FileSize.LowPart != INVALID_FILE_SIZE) || (GetLastError() == NO_ERROR)) {
hd_size = FileSize.QuadPart;
} else {
return -1;
}
} else {
return -1;
}
#endif
fd = ::open(pathname, flags
#ifdef O_BINARY
| O_BINARY
#endif
);
if (fd < 0) {
return fd;
}
#ifndef WIN32
/* look at size of image file to calculate disk geometry */
struct stat stat_buf;
if (fstat(fd, &stat_buf)) {
BX_PANIC(("fstat() returns error!"));
}
#ifdef linux
if (stat_buf.st_rdev) { // Is this a special device file (e.g. /dev/sde) ?
ioctl(fd, BLKGETSIZE64, &hd_size); // yes it's!
}
else
#endif
{
hd_size = (Bit64u)stat_buf.st_size; // standard unix procedure to get size of regular files
}
#endif
BX_INFO(("hd_size: "FMT_LL"u", hd_size));
if (hd_size <= 0) BX_PANIC(("size of disk image not detected / invalid"));
if ((hd_size % 512) != 0) BX_PANIC(("size of disk image must be multiple of 512 bytes"));
return fd;
}
void default_image_t::close()
{
if (fd > -1) {
::close(fd);
}
}
Bit64s default_image_t::lseek(Bit64s offset, int whence)
{
return (Bit64s)::lseek(fd, (off_t)offset, whence);
}
ssize_t default_image_t::read(void* buf, size_t count)
{
return ::read(fd, (char*) buf, count);
}
ssize_t default_image_t::write(const void* buf, size_t count)
{
return ::write(fd, (char*) buf, count);
}
char increment_string(char *str, int diff)
{
// find the last character of the string, and increment it.
char *p = str;
while (*p != 0) p++;
BX_ASSERT(p>str); // choke on zero length strings
p--; // point to last character of the string
(*p) += diff; // increment to next/previous ascii code.
BX_DEBUG(("increment string returning '%s'", str));
return (*p);
}
/*** concat_image_t function definitions ***/
concat_image_t::concat_image_t()
{
fd = -1;
}
void concat_image_t::increment_string(char *str)
{
::increment_string(str, +1);
}
int concat_image_t::open(const char* pathname0)
{
char *pathname = strdup(pathname0);
BX_DEBUG(("concat_image_t.open"));
Bit64s start_offset = 0;
for (int i=0; i<BX_CONCAT_MAX_IMAGES; i++) {
fd_table[i] = ::open(pathname, O_RDWR
#ifdef O_BINARY
| O_BINARY
#endif
);
if (fd_table[i] < 0) {
// open failed.
// if no FD was opened successfully, return -1 (fail).
if (i==0) return -1;
// otherwise, it only means that all images in the series have
// been opened. Record the number of fds opened successfully.
maxfd = i;
break;
}
BX_DEBUG(("concat_image: open image %s, fd[%d] = %d", pathname, i, fd_table[i]));
/* look at size of image file to calculate disk geometry */
struct stat stat_buf;
int ret = fstat(fd_table[i], &stat_buf);
if (ret) {
BX_PANIC(("fstat() returns error!"));
}
#ifdef S_ISBLK
if (S_ISBLK(stat_buf.st_mode)) {
BX_PANIC(("block devices should REALLY NOT be used as concat images"));
}
#endif
if ((stat_buf.st_size % 512) != 0) {
BX_PANIC(("size of disk image must be multiple of 512 bytes"));
}
length_table[i] = stat_buf.st_size;
start_offset_table[i] = start_offset;
start_offset += stat_buf.st_size;
increment_string(pathname);
}
// start up with first image selected
index = 0;
fd = fd_table[0];
thismin = 0;
thismax = length_table[0]-1;
seek_was_last_op = 0;
hd_size = start_offset;
return 0; // success.
}
void concat_image_t::close()
{
BX_DEBUG(("concat_image_t.close"));
if (fd > -1) {
::close(fd);
}
}
Bit64s concat_image_t::lseek(Bit64s offset, int whence)
{
if ((offset % 512) != 0)
BX_PANIC(("lseek HD with offset not multiple of 512"));
BX_DEBUG(("concat_image_t.lseek(%d)", whence));
// is this offset in this disk image?
if (offset < thismin) {
// no, look at previous images
for (int i=index-1; i>=0; i--) {
if (offset >= start_offset_table[i]) {
index = i;
fd = fd_table[i];
thismin = start_offset_table[i];
thismax = thismin + length_table[i] - 1;
BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index));
break;
}
}
} else if (offset > thismax) {
// no, look at later images
for (int i=index+1; i<maxfd; i++) {
if (offset < start_offset_table[i] + length_table[i]) {
index = i;
fd = fd_table[i];
thismin = start_offset_table[i];
thismax = thismin + length_table[i] - 1;
BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index));
break;
}
}
}
// now offset should be within the current image.
offset -= start_offset_table[index];
if (offset < 0 || offset >= length_table[index]) {
BX_PANIC(("concat_image_t.lseek to byte %ld failed", (long)offset));
return -1;
}
seek_was_last_op = 1;
return (Bit64s)::lseek(fd, (off_t)offset, whence);
}
ssize_t concat_image_t::read(void* buf, size_t count)
{
BX_DEBUG(("concat_image_t.read %ld bytes", (long)count));
// notice if anyone does sequential read or write without seek in between.
// This can be supported pretty easily, but needs additional checks for
// end of a partial image.
if (!seek_was_last_op)
BX_PANIC(("no seek before read"));
return ::read(fd, (char*) buf, count);
}
ssize_t concat_image_t::write(const void* buf, size_t count)
{
BX_DEBUG(("concat_image_t.write %ld bytes", (long)count));
// notice if anyone does sequential read or write without seek in between.
// This can be supported pretty easily, but needs additional checks for
// end of a partial image.
if (!seek_was_last_op)
BX_PANIC(("no seek before write"));
return ::write(fd, (char*) buf, count);
}
/*** sparse_image_t function definitions ***/
sparse_image_t::sparse_image_t ()
{
fd = -1;
pathname = NULL;
#ifdef _POSIX_MAPPED_FILES
mmap_header = NULL;
#endif
pagetable = NULL;
parent_image = NULL;
}
/*
void showpagetable(Bit32u * pagetable, size_t numpages)
{
printf("Non null pages: ");
for (int i = 0; i < numpages; i++)
{
if (pagetable[i] != 0xffffffff)
{
printf("%d ", i);
}
}
printf("\n");
}
*/
void sparse_image_t::read_header()
{
BX_ASSERT(sizeof(header) == SPARSE_HEADER_SIZE);
int ret = ::read(fd, &header, sizeof(header));
if (-1 == ret)
{
panic(strerror(errno));
}
if (sizeof(header) != ret)
{
panic("could not read entire header");
}
if (dtoh32(header.magic) != SPARSE_HEADER_MAGIC)
{
panic("failed header magic check");
}
if ((dtoh32(header.version) != SPARSE_HEADER_VERSION) &&
(dtoh32(header.version) != SPARSE_HEADER_V1))
{
panic("unknown version in header");
}
pagesize = dtoh32(header.pagesize);
Bit32u numpages = dtoh32(header.numpages);
total_size = pagesize;
total_size *= numpages;
pagesize_shift = 0;
while ((pagesize >> pagesize_shift) > 1) pagesize_shift++;
if ((Bit32u)(1 << pagesize_shift) != pagesize)
{
panic("failed block size header check");
}
pagesize_mask = pagesize - 1;
size_t preamble_size = (sizeof(Bit32u) * numpages) + sizeof(header);
data_start = 0;
while ((size_t)data_start < preamble_size) data_start += pagesize;
bx_bool did_mmap = 0;
#ifdef _POSIX_MAPPED_FILES
// Try to memory map from the beginning of the file (0 is trivially a page multiple)
void *mmap_header = mmap(NULL, preamble_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mmap_header == MAP_FAILED)
{
BX_INFO(("failed to mmap sparse disk file - using conventional file access"));
mmap_header = NULL;
}
else
{
mmap_length = preamble_size;
did_mmap = 1;
pagetable = ((Bit32u *) (((Bit8u *) mmap_header) + sizeof(header)));
system_pagesize_mask = getpagesize() - 1;
}
#endif
if (!did_mmap)
{
pagetable = new Bit32u[numpages];
if (pagetable == NULL)
{
panic("could not allocate memory for sparse disk block table");
}
ret = ::read(fd, pagetable, sizeof(Bit32u) * numpages);
if (-1 == ret)
{
panic(strerror(errno));
}
if ((int)(sizeof(Bit32u) * numpages) != ret)
{
panic("could not read entire block table");
}
}
}
int sparse_image_t::open (const char* pathname0)
{
pathname = strdup(pathname0);
BX_DEBUG(("sparse_image_t.open"));
fd = ::open(pathname, O_RDWR
#ifdef O_BINARY
| O_BINARY
#endif
);
if (fd < 0)
{
return -1; // open failed
}
BX_DEBUG(("sparse_image: open image %s", pathname));
read_header();
struct stat stat_buf;
if (fstat(fd, &stat_buf) != 0) panic(("fstat() returns error!"));
underlying_filesize = stat_buf.st_size;
if ((underlying_filesize % pagesize) != 0)
panic("size of sparse disk image is not multiple of page size");
underlying_current_filepos = 0;
if (-1 == ::lseek(fd, 0, SEEK_SET))
panic("error while seeking to start of file");
lseek(0, SEEK_SET);
//showpagetable(pagetable, header.numpages);
char * parentpathname = strdup(pathname);
char lastchar = ::increment_string(parentpathname, -1);
if ((lastchar >= '0') && (lastchar <= '9'))
{
struct stat stat_buf;
if (0 == stat(parentpathname, &stat_buf))
{
parent_image = new sparse_image_t();
int ret = parent_image->open(parentpathname);
if (ret != 0) return ret;
if ( (parent_image->pagesize != pagesize)
|| (parent_image->total_size != total_size))
{
panic("child drive image does not have same page count/page size configuration");
}
}
}
if (parentpathname != NULL) free(parentpathname);
if (dtoh32(header.version) == SPARSE_HEADER_VERSION) {
hd_size = dtoh64(header.disk);
}
return 0; // success
}
void sparse_image_t::close()
{
BX_DEBUG(("concat_image_t.close"));
if (pathname != NULL)
{
free(pathname);
}
#ifdef _POSIX_MAPPED_FILES
if (mmap_header != NULL)
{
int ret = munmap(mmap_header, mmap_length);
if (ret != 0)
BX_INFO(("failed to un-memory map sparse disk file"));
}
pagetable = NULL; // We didn't malloc it
#endif
if (fd > -1) {
::close(fd);
}
if (pagetable != NULL)
{
delete [] pagetable;
}
if (parent_image != NULL)
{
delete parent_image;
}
}
Bit64s sparse_image_t::lseek(Bit64s offset, int whence)
{
//showpagetable(pagetable, header.numpages);
if ((offset % 512) != 0)
BX_PANIC(("lseek HD with offset not multiple of 512"));
if (whence != SEEK_SET)
BX_PANIC(("lseek HD with whence not SEEK_SET"));
BX_DEBUG(("sparse_image_t.lseek(%d)", whence));
if (offset > total_size)
{
BX_PANIC(("sparse_image_t.lseek to byte %ld failed", (long)offset));
return -1;
}
//printf("Seeking to position %ld\n", (long) offset);
set_virtual_page((Bit32u)(offset >> pagesize_shift));
position_page_offset = (Bit32u)(offset & pagesize_mask);
return 0;
}
inline Bit64s sparse_image_t::get_physical_offset()
{
Bit64s physical_offset = data_start;
physical_offset += ((Bit64s)position_physical_page << pagesize_shift);
physical_offset += position_page_offset;
return physical_offset;
}
void sparse_image_t::set_virtual_page(Bit32u new_virtual_page)
{
position_virtual_page = new_virtual_page;
position_physical_page = dtoh32(pagetable[position_virtual_page]);
}
ssize_t sparse_image_t::read_page_fragment(Bit32u read_virtual_page, Bit32u read_page_offset, size_t read_size, void * buf)
{
if (read_virtual_page != position_virtual_page)
{
set_virtual_page(read_virtual_page);
}
position_page_offset = read_page_offset;
if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED)
{
if (parent_image != NULL)
{
return parent_image->read_page_fragment(read_virtual_page, read_page_offset, read_size, buf);
}
else
{
memset(buf, 0, read_size);
}
}
else
{
Bit64s physical_offset = get_physical_offset();
if (physical_offset != underlying_current_filepos)
{
off_t ret = ::lseek(fd, (off_t)physical_offset, SEEK_SET);
// underlying_current_filepos update deferred
if (ret == -1)
panic(strerror(errno));
}
//printf("Reading %s at position %ld size %d\n", pathname, (long) physical_offset, (long) read_size);
ssize_t readret = ::read(fd, buf, read_size);
if (readret == -1)
{
panic(strerror(errno));
}
if ((size_t)readret != read_size)
{
panic("could not read block contents from file");
}
underlying_current_filepos = physical_offset + read_size;
}
return read_size;
}
ssize_t sparse_image_t::read(void* buf, size_t count)
{
//showpagetable(pagetable, header.numpages);
ssize_t total_read = 0;
BX_DEBUG(("sparse_image_t.read %ld bytes", (long)count));
while (count != 0)
{
size_t can_read = pagesize - position_page_offset;
if (count < can_read) can_read = count;
BX_ASSERT (can_read != 0);
#if BX_ASSERT_ENABLE
size_t was_read = read_page_fragment(position_virtual_page, position_page_offset, can_read, buf);
#endif
BX_ASSERT(was_read == can_read);
total_read += can_read;
position_page_offset += can_read;
if (position_page_offset == pagesize)
{
position_page_offset = 0;
set_virtual_page(position_virtual_page + 1);
}
BX_ASSERT(position_page_offset < pagesize);
buf = (((Bit8u *) buf) + can_read);
count -= can_read;
}
return total_read;
}
void sparse_image_t::panic(const char * message)
{
char buffer[1024];
if (message == NULL)
{
snprintf(buffer, sizeof(buffer), "error with sparse disk image %s", pathname);
}
else
{
snprintf(buffer, sizeof(buffer), "error with sparse disk image %s - %s", pathname, message);
}
BX_PANIC(("%s", buffer));
}
ssize_t sparse_image_t::write (const void* buf, size_t count)
{
//showpagetable(pagetable, header.numpages);
ssize_t total_written = 0;
Bit32u update_pagetable_start = position_virtual_page;
Bit32u update_pagetable_count = 0;
BX_DEBUG(("sparse_image_t.write %ld bytes", (long)count));
while (count != 0)
{
size_t can_write = pagesize - position_page_offset;
if (count < can_write) can_write = count;
BX_ASSERT (can_write != 0);
if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED)
{
// We just add on another page at the end of the file
// Reclamation, compaction etc should currently be done off-line
Bit64s data_size = underlying_filesize - data_start;
BX_ASSERT((data_size % pagesize) == 0);
Bit32u data_size_pages = (Bit32u)(data_size / pagesize);
Bit32u next_data_page = data_size_pages;
pagetable[position_virtual_page] = htod32(next_data_page);
position_physical_page = next_data_page;
Bit64s page_file_start = data_start + ((Bit64s)position_physical_page << pagesize_shift);
if (parent_image != NULL)
{
// If we have a parent, we must merge our portion with the parent
void *writebuffer = NULL;
if (can_write == pagesize)
{
writebuffer = (void *) buf;
}
else
{
writebuffer = malloc(pagesize);
if (writebuffer == NULL)
panic("Cannot allocate sufficient memory for page-merge in write");
// Read entire page - could optimize, but simple for now
parent_image->read_page_fragment(position_virtual_page, 0, pagesize, writebuffer);
void *dest_start = ((Bit8u *) writebuffer) + position_page_offset;
memcpy(dest_start, buf, can_write);
}
int ret = (int)::lseek(fd, page_file_start, SEEK_SET);
// underlying_current_filepos update deferred
if (ret == -1) panic(strerror(errno));
ret = ::write(fd, writebuffer, pagesize);
if (ret == -1) panic(strerror(errno));
if (pagesize != (Bit32u)ret) panic("failed to write entire merged page to disk");
if (can_write != pagesize)
{
free(writebuffer);
}
}
else
{
// We need to write a zero page because read has been returning zeroes
// We seek as close to the page end as possible, and then write a little
// This produces a sparse file which has blanks
// Also very quick, even when pagesize is massive
int ret = (int)::lseek(fd, page_file_start + pagesize - 4, SEEK_SET);
// underlying_current_filepos update deferred
if (ret == -1) panic(strerror(errno));
Bit32u zero = 0;
ret = ::write(fd, &zero, 4);
if (ret == -1) panic(strerror(errno));
if (ret != 4) panic("failed to write entire blank page to disk");
}
update_pagetable_count = (position_virtual_page - update_pagetable_start) + 1;
underlying_filesize = underlying_current_filepos = page_file_start + pagesize;
}
BX_ASSERT(position_physical_page != SPARSE_PAGE_NOT_ALLOCATED);
Bit64s physical_offset = get_physical_offset();
if (physical_offset != underlying_current_filepos)
{
off_t ret = ::lseek(fd, (off_t)physical_offset, SEEK_SET);
// underlying_current_filepos update deferred
if (ret == -1)
panic(strerror(errno));
}
//printf("Writing at position %ld size %d\n", (long) physical_offset, can_write);
ssize_t writeret = ::write(fd, buf, can_write);
if (writeret == -1)
{
panic(strerror(errno));
}
if ((size_t)writeret != can_write)
{
panic("could not write block contents to file");
}
underlying_current_filepos = physical_offset + can_write;
total_written += can_write;
position_page_offset += can_write;
if (position_page_offset == pagesize)
{
position_page_offset = 0;
set_virtual_page(position_virtual_page + 1);
}
BX_ASSERT(position_page_offset < pagesize);
buf = (((Bit8u *) buf) + can_write);
count -= can_write;
}
if (update_pagetable_count != 0)
{
bx_bool done = 0;
off_t pagetable_write_from = sizeof(header) + (sizeof(Bit32u) * update_pagetable_start);
size_t write_bytecount = update_pagetable_count * sizeof(Bit32u);
#ifdef _POSIX_MAPPED_FILES
if (mmap_header != NULL)
{
// Sync from the beginning of the page
size_t system_page_offset = pagetable_write_from & system_pagesize_mask;
void *start = ((Bit8u *) mmap_header + pagetable_write_from - system_page_offset);
int ret = msync(start, system_page_offset + write_bytecount, MS_ASYNC);
if (ret != 0)
panic(strerror(errno));
done = 1;
}
#endif
if (!done)
{
int ret = (int)::lseek(fd, pagetable_write_from, SEEK_SET);
// underlying_current_filepos update deferred
if (ret == -1) panic(strerror(errno));
//printf("Writing header at position %ld size %ld\n", (long) pagetable_write_from, (long) write_bytecount);
ret = ::write(fd, &pagetable[update_pagetable_start], write_bytecount);
if (ret == -1) panic(strerror(errno));
if ((size_t)ret != write_bytecount) panic("could not write entire updated block header");
underlying_current_filepos = pagetable_write_from + write_bytecount;
}
}
return total_written;
}
#if DLL_HD_SUPPORT
/*** dll_image_t function definitions ***/
/*
function vdisk_open(path:PChar;numclusters,clustersize:integer):integer;
procedure vdisk_read(vunit:integer;blk:integer;var buf:TBlock);
procedure vdisk_write(vunit:integer;blk:integer;var buf:TBlock);
procedure vdisk_close(vunit:integer);
*/
HINSTANCE hlib_vdisk = 0;
int (*vdisk_open) (const char *path,int numclusters,int clustersize);
void (*vdisk_read) (int vunit,int blk,void *buf);
void (*vdisk_write) (int vunit,int blk,const void *buf);
void (*vdisk_close) (int vunit);
int dll_image_t::open (const char* pathname)
{
if (hlib_vdisk == 0) {
hlib_vdisk = LoadLibrary("vdisk.dll");
if (hlib_vdisk != 0) {
vdisk_read = (void (*)(int,int,void*)) GetProcAddress(hlib_vdisk,"vdisk_read");
vdisk_write = (void (*)(int,int,const void*)) GetProcAddress(hlib_vdisk,"vdisk_write");
vdisk_open = (int (*)(const char *,int,int)) GetProcAddress(hlib_vdisk,"vdisk_open");
vdisk_close = (void (*)(int)) GetProcAddress(hlib_vdisk,"vdisk_close");
}
}
if (hlib_vdisk != 0) {
vunit = vdisk_open(pathname,0x10000,64);
vblk = 0;
} else {
vunit = -2;
}
return vunit;
}
void dll_image_t::close ()
{
if (vunit >= 0 && hlib_vdisk != 0) {
vdisk_close(vunit);
}
}
Bit64s dll_image_t::lseek(Bit64s offset, int whence)
{
vblk = (int)(offset >> 9);
return 0;
}
ssize_t dll_image_t::read (void* buf, size_t count)
{
if (vunit >= 0 && hlib_vdisk != 0) {
vdisk_read(vunit,vblk,buf);
return count;
} else {
return -1;
}
}
ssize_t dll_image_t::write (const void* buf, size_t count)
{
if (vunit >= 0 && hlib_vdisk != 0) {
vdisk_write(vunit,vblk,buf);
return count;
} else {
return -1;
}
}
#endif // DLL_HD_SUPPORT
// redolog implementation
redolog_t::redolog_t()
{
fd = -1;
catalog = NULL;
bitmap = NULL;
extent_index = (Bit32u)0;
extent_offset = (Bit32u)0;
extent_next = (Bit32u)0;
}
void redolog_t::print_header()
{
BX_INFO(("redolog : Standard Header : magic='%s', type='%s', subtype='%s', version = %d.%d",
header.standard.magic, header.standard.type, header.standard.subtype,
dtoh32(header.standard.version)/0x10000,
dtoh32(header.standard.version)%0x10000));
if (dtoh32(header.standard.version) == STANDARD_HEADER_VERSION) {
BX_INFO(("redolog : Specific Header : #entries=%d, bitmap size=%d, exent size = %d disk size = " FMT_LL "d",
dtoh32(header.specific.catalog),
dtoh32(header.specific.bitmap),
dtoh32(header.specific.extent),
dtoh64(header.specific.disk)));
} else if (dtoh32(header.standard.version) == STANDARD_HEADER_V1) {
redolog_header_v1_t header_v1;
memcpy(&header_v1, &header, STANDARD_HEADER_SIZE);
BX_INFO(("redolog : Specific Header : #entries=%d, bitmap size=%d, exent size = %d disk size = " FMT_LL "d",
dtoh32(header_v1.specific.catalog),
dtoh32(header_v1.specific.bitmap),
dtoh32(header_v1.specific.extent),
dtoh64(header_v1.specific.disk)));
}
}
int redolog_t::make_header(const char* type, Bit64u size)
{
Bit32u entries, extent_size, bitmap_size;
Bit64u maxsize;
Bit32u flip=0;
// 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 {
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);
print_header();
catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u));
bitmap = (Bit8u*)malloc(dtoh32(header.specific.bitmap));
if ((catalog == NULL) || (bitmap==NULL))
BX_PANIC(("redolog : could not malloc catalog or bitmap"));
for (Bit32u i=0; i<dtoh32(header.specific.catalog); i++)
catalog[i] = htod32(REDOLOG_PAGE_NOT_ALLOCATED);
bitmap_blocks = 1 + (dtoh32(header.specific.bitmap) - 1) / 512;
extent_blocks = 1 + (dtoh32(header.specific.extent) - 1) / 512;
BX_DEBUG(("redolog : each bitmap is %d blocks", bitmap_blocks));
BX_DEBUG(("redolog : each extent is %d blocks", extent_blocks));
return 0;
}
int redolog_t::create(const char* filename, const char* type, Bit64u size)
{
BX_INFO(("redolog : creating redolog %s", filename));
int filedes = ::open(filename, O_RDWR | O_CREAT | O_TRUNC
#ifdef O_BINARY
| O_BINARY
#endif
, S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP);
return create(filedes, type, size);
}
int redolog_t::create(int filedes, const char* type, Bit64u size)
{
fd = filedes;
if (fd < 0)
{
return -1; // open failed
}
if (make_header(type, size) < 0)
{
return -1;
}
// Write header
::write(fd, &header, dtoh32(header.standard.header));
// Write catalog
// FIXME could mmap
::write(fd, catalog, dtoh32(header.specific.catalog) * sizeof (Bit32u));
return 0;
}
int redolog_t::open(const char* filename, const char *type)
{
fd = ::open(filename, O_RDWR
#ifdef O_BINARY
| O_BINARY
#endif
);
if (fd < 0)
{
BX_INFO(("redolog : could not open image %s", filename));
// open failed.
return -1;
}
BX_INFO(("redolog : open image %s", filename));
int res = ::read(fd, &header, sizeof(header));
if (res != STANDARD_HEADER_SIZE)
{
BX_PANIC(("redolog : could not read header"));
return -1;
}
print_header();
if (strcmp((char*)header.standard.magic, STANDARD_HEADER_MAGIC) != 0)
{
BX_PANIC(("redolog : Bad header magic"));
return -1;
}
if (strcmp((char*)header.standard.type, REDOLOG_TYPE) != 0)
{
BX_PANIC(("redolog : Bad header type"));
return -1;
}
if (strcmp((char*)header.standard.subtype, type) != 0)
{
BX_PANIC(("redolog : Bad header subtype"));
return -1;
}
if ((dtoh32(header.standard.version) != STANDARD_HEADER_VERSION) &&
(dtoh32(header.standard.version) != STANDARD_HEADER_V1))
{
BX_PANIC(("redolog : Bad header version"));
return -1;
}
if (dtoh32(header.standard.version) == STANDARD_HEADER_V1) {
redolog_header_v1_t header_v1;
memcpy(&header_v1, &header, STANDARD_HEADER_SIZE);
header.specific.disk = header_v1.specific.disk;
}
catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u));
// FIXME could mmap
::lseek(fd,dtoh32(header.standard.header),SEEK_SET);
res = ::read(fd, catalog, dtoh32(header.specific.catalog) * sizeof(Bit32u));
if (res != (ssize_t)(dtoh32(header.specific.catalog) * sizeof(Bit32u)))
{
BX_PANIC(("redolog : could not read catalog %d=%d",res, dtoh32(header.specific.catalog)));
return -1;
}
// check last used extent
extent_next = 0;
for (Bit32u i=0; i < dtoh32(header.specific.catalog); i++)
{
if (dtoh32(catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED)
{
if (dtoh32(catalog[i]) >= extent_next)
extent_next = dtoh32(catalog[i]) + 1;
}
}
BX_INFO(("redolog : next extent will be at index %d",extent_next));
// memory used for storing bitmaps
bitmap = (Bit8u *)malloc(dtoh32(header.specific.bitmap));
bitmap_blocks = 1 + (dtoh32(header.specific.bitmap) - 1) / 512;
extent_blocks = 1 + (dtoh32(header.specific.extent) - 1) / 512;
BX_DEBUG(("redolog : each bitmap is %d blocks", bitmap_blocks));
BX_DEBUG(("redolog : each extent is %d blocks", extent_blocks));
imagepos = 0;
return 0;
}
void redolog_t::close()
{
if (fd >= 0)
::close(fd);
if (catalog != NULL)
free(catalog);
if (bitmap != NULL)
free(bitmap);
}
Bit64u redolog_t::get_size()
{
return dtoh64(header.specific.disk);
}
Bit64s redolog_t::lseek(Bit64s offset, int whence)
{
if ((offset % 512) != 0) {
BX_PANIC(("redolog : lseek() offset not multiple of 512"));
return -1;
}
if (whence == SEEK_SET) {
imagepos = offset;
} else if (whence == SEEK_CUR) {
imagepos += offset;
} else {
BX_PANIC(("redolog: lseek() mode not supported yet"));
return -1;
}
if (imagepos > (Bit64s)dtoh64(header.specific.disk)) {
BX_PANIC(("redolog : lseek() to byte %ld failed", (long)offset));
return -1;
}
extent_index = (Bit32u)(imagepos / dtoh32(header.specific.extent));
extent_offset = (Bit32u)((imagepos % dtoh32(header.specific.extent)) / 512);
BX_DEBUG(("redolog : lseeking extent index %d, offset %d",extent_index, extent_offset));
return imagepos;
}
ssize_t redolog_t::read(void* buf, size_t count)
{
Bit64s block_offset, bitmap_offset;
ssize_t ret;
if (count != 512) {
BX_PANIC(("redolog : read() with count not 512"));
return -1;
}
BX_DEBUG(("redolog : reading index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index])));
if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) {
// page not allocated
return 0;
}
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
bitmap_offset += (Bit64s)512 * dtoh32(catalog[extent_index]) * (extent_blocks + bitmap_blocks);
block_offset = bitmap_offset + ((Bit64s)512 * (bitmap_blocks + extent_offset));
BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset));
BX_DEBUG(("redolog : block offset is %x", (Bit32u)block_offset));
// FIXME if same extent_index as before we can skip bitmap read
::lseek(fd, (off_t)bitmap_offset, SEEK_SET);
if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap)) {
BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index));
return -1;
}
if (((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00) {
BX_DEBUG(("read not in redolog"));
// bitmap says block not in redolog
return 0;
}
::lseek(fd, (off_t)block_offset, SEEK_SET);
ret = ::read(fd, buf, count);
if (ret >= 0) lseek(512, SEEK_CUR);
return ret;
}
ssize_t redolog_t::write(const void* buf, size_t count)
{
Bit32u i;
Bit64s block_offset, bitmap_offset, catalog_offset;
ssize_t written;
bx_bool update_catalog = 0;
if (count != 512) {
BX_PANIC(("redolog : write() with count not 512"));
return -1;
}
BX_DEBUG(("redolog : writing index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index])));
if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) {
if (extent_next >= dtoh32(header.specific.catalog)) {
BX_PANIC(("redolog : can't allocate new extent... catalog is full"));
return -1;
}
BX_DEBUG(("redolog : allocating new extent at %d", extent_next));
// Extent not allocated, allocate new
catalog[extent_index] = htod32(extent_next);
extent_next += 1;
char *zerobuffer = (char*)malloc(512);
memset(zerobuffer, 0, 512);
// Write bitmap
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
bitmap_offset += (Bit64s)512 * dtoh32(catalog[extent_index]) * (extent_blocks + bitmap_blocks);
::lseek(fd, (off_t)bitmap_offset, SEEK_SET);
for (i=0; i<bitmap_blocks; i++) {
::write(fd, zerobuffer, 512);
}
// Write extent
for (i=0; i<extent_blocks; i++) {
::write(fd, zerobuffer, 512);
}
free(zerobuffer);
update_catalog = 1;
}
bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
bitmap_offset += (Bit64s)512 * dtoh32(catalog[extent_index]) * (extent_blocks + bitmap_blocks);
block_offset = bitmap_offset + ((Bit64s)512 * (bitmap_blocks + extent_offset));
BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset));
BX_DEBUG(("redolog : block offset is %x", (Bit32u)block_offset));
// Write block
::lseek(fd, (off_t)block_offset, SEEK_SET);
written = ::write(fd, buf, count);
// Write bitmap
// FIXME if same extent_index as before we can skip bitmap read
::lseek(fd, (off_t)bitmap_offset, SEEK_SET);
if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap)) {
BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index));
return 0;
}
// If bloc does not belong to extent yet
if (((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00) {
bitmap[extent_offset/8] |= 1 << (extent_offset%8);
::lseek(fd, (off_t)bitmap_offset, SEEK_SET);
::write(fd, bitmap, dtoh32(header.specific.bitmap));
}
// Write catalog
if (update_catalog) {
// FIXME if mmap
catalog_offset = (Bit64s)STANDARD_HEADER_SIZE + (extent_index * sizeof(Bit32u));
BX_DEBUG(("redolog : writing catalog at offset %x", (Bit32u)catalog_offset));
::lseek(fd, (off_t)catalog_offset, SEEK_SET);
::write(fd, &catalog[extent_index], sizeof(Bit32u));
}
if (written >= 0) lseek(512, SEEK_CUR);
return written;
}
/*** growing_image_t function definitions ***/
growing_image_t::growing_image_t()
{
redolog = new redolog_t();
}
growing_image_t::~growing_image_t()
{
delete redolog;
}
int growing_image_t::open(const char* pathname)
{
int filedes = redolog->open(pathname, REDOLOG_SUBTYPE_GROWING);
hd_size = redolog->get_size();
BX_INFO(("'growing' disk opened, growing file is '%s'", pathname));
return filedes;
}
void growing_image_t::close()
{
redolog->close();
}
Bit64s growing_image_t::lseek(Bit64s offset, int whence)
{
return redolog->lseek(offset, whence);
}
ssize_t growing_image_t::read(void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
memset(buf, 0, count);
while (n < count) {
ret = redolog->read((char*) buf, 512);
if (ret < 0) break;
n += 512;
}
return (ret < 0) ? ret : count;
}
ssize_t growing_image_t::write(const void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
while (n < count) {
ret = redolog->write((char*) buf, 512);
if (ret < 0) break;
n += 512;
}
return (ret < 0) ? ret : count;
}
/*** undoable_image_t function definitions ***/
undoable_image_t::undoable_image_t(const char* _redolog_name)
{
redolog = new redolog_t();
ro_disk = new default_image_t();
redolog_name = NULL;
if (_redolog_name != NULL) {
if (strcmp(_redolog_name,"") != 0) {
redolog_name = strdup(_redolog_name);
}
}
}
undoable_image_t::~undoable_image_t()
{
delete redolog;
delete ro_disk;
}
int undoable_image_t::open(const char* pathname)
{
char *logname=NULL;
if (ro_disk->open(pathname, O_RDONLY)<0)
return -1;
hd_size = ro_disk->hd_size;
// if redolog name was set
if (redolog_name != NULL) {
if (strcmp(redolog_name, "") != 0) {
logname = (char*)malloc(strlen(redolog_name) + 1);
strcpy(logname, redolog_name);
}
}
// Otherwise we make up the redolog filename from the pathname
if (logname == NULL) {
logname = (char*)malloc(strlen(pathname) + UNDOABLE_REDOLOG_EXTENSION_LENGTH + 1);
sprintf(logname, "%s%s", pathname, UNDOABLE_REDOLOG_EXTENSION);
}
if (redolog->open(logname,REDOLOG_SUBTYPE_UNDOABLE) < 0)
{
if (redolog->create(logname, REDOLOG_SUBTYPE_UNDOABLE, hd_size) < 0)
{
BX_PANIC(("Can't open or create redolog '%s'",logname));
return -1;
}
if (hd_size != redolog->get_size())
{
BX_PANIC(("size reported by redolog doesn't match r/o disk size"));
free(logname);
return -1;
}
}
BX_INFO(("'undoable' disk opened: ro-file is '%s', redolog is '%s'", pathname, logname));
free(logname);
return 0;
}
void undoable_image_t::close ()
{
redolog->close();
ro_disk->close();
if (redolog_name!=NULL)
free(redolog_name);
}
Bit64s undoable_image_t::lseek(Bit64s offset, int whence)
{
redolog->lseek(offset, whence);
return ro_disk->lseek(offset, whence);
}
ssize_t undoable_image_t::read(void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
while (n < count) {
if ((size_t)redolog->read((char*) buf, 512) != 512) {
ret = ro_disk->read((char*) buf, 512);
if (ret < 0) break;
}
n += 512;
}
return (ret < 0) ? ret : count;
}
ssize_t undoable_image_t::write(const void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
while (n < count) {
ret = redolog->write((char*) buf, 512);
if (ret < 0) break;
n += 512;
}
return (ret < 0) ? ret : count;
}
/*** volatile_image_t function definitions ***/
volatile_image_t::volatile_image_t(const char* _redolog_name)
{
redolog = new redolog_t();
ro_disk = new default_image_t();
redolog_temp = NULL;
redolog_name = NULL;
if (_redolog_name != NULL) {
if (strcmp(_redolog_name,"") != 0) {
redolog_name = strdup(_redolog_name);
}
}
}
volatile_image_t::~volatile_image_t()
{
delete redolog;
delete ro_disk;
}
int volatile_image_t::open(const char* pathname)
{
int filedes;
const char *logname=NULL;
if (ro_disk->open(pathname, O_RDONLY)<0)
return -1;
hd_size = ro_disk->hd_size;
// if redolog name was set
if (redolog_name != NULL) {
if (strcmp(redolog_name, "") != 0) {
logname = redolog_name;
}
}
// otherwise use pathname as template
if (logname == NULL) {
logname = pathname;
}
redolog_temp = (char*)malloc(strlen(logname) + VOLATILE_REDOLOG_EXTENSION_LENGTH + 1);
sprintf (redolog_temp, "%s%s", logname, VOLATILE_REDOLOG_EXTENSION);
filedes = mkstemp (redolog_temp);
if (filedes < 0)
{
BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
return -1;
}
if (redolog->create(filedes, REDOLOG_SUBTYPE_VOLATILE, hd_size) < 0)
{
BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
return -1;
}
#if (!defined(WIN32)) && !BX_WITH_MACOS
// on unix it is legal to delete an open file
unlink(redolog_temp);
#endif
BX_INFO(("'volatile' disk opened: ro-file is '%s', redolog is '%s'", pathname, redolog_temp));
return 0;
}
void volatile_image_t::close()
{
redolog->close();
ro_disk->close();
#if defined(WIN32) || BX_WITH_MACOS
// on non-unix we have to wait till the file is closed to delete it
unlink(redolog_temp);
#endif
if (redolog_temp!=NULL)
free(redolog_temp);
if (redolog_name!=NULL)
free(redolog_name);
}
Bit64s volatile_image_t::lseek(Bit64s offset, int whence)
{
redolog->lseek(offset, whence);
return ro_disk->lseek(offset, whence);
}
ssize_t volatile_image_t::read(void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
while (n < count) {
if ((size_t)redolog->read((char*) buf, 512) != 512) {
ret = ro_disk->read((char*) buf, 512);
if (ret < 0) break;
}
n += 512;
}
return (ret < 0) ? ret : count;
}
ssize_t volatile_image_t::write(const void* buf, size_t count)
{
size_t n = 0;
ssize_t ret = 0;
while (n < count) {
ret = redolog->write((char*) buf, 512);
if (ret < 0) break;
n += 512;
}
return (ret < 0) ? ret : count;
}