From 9505a80b7e8df26ca3ee8f9aecb294c5c9794076 Mon Sep 17 00:00:00 2001 From: Christophe Bothamy Date: Wed, 29 Jan 2003 18:20:19 +0000 Subject: [PATCH] - add "sparse hard drive" patch by justinsb --- bochs/patches/patch.sparsedisk-justinsb | 1282 +++++++++++++++++++++++ 1 file changed, 1282 insertions(+) create mode 100644 bochs/patches/patch.sparsedisk-justinsb diff --git a/bochs/patches/patch.sparsedisk-justinsb b/bochs/patches/patch.sparsedisk-justinsb new file mode 100644 index 000000000..fd7c1b68d --- /dev/null +++ b/bochs/patches/patch.sparsedisk-justinsb @@ -0,0 +1,1282 @@ +---------------------------------------------------------------------- +Patch name: patch.sparsedisk-justinsb +Author: Justin SB +Date: 27 Jan 2003 + +Detailed description: + + This patch enables "sparse hard drive" support: + + 1) A large hard drive can be created, and only used space will be stored + in the file. In practice this is not a large gain as Unix does this + anyway. + 2) Multiple sparse drive images can be mounted on top of each other. + Writes go to the top image. This allows several similar configurations + to share a master "base" file, and also allows filesystem rollback or + no-write options. Up to 10 disk images can be layered on top of each other. + + There is a need for supporting utilities: + 1) to merge two sparse disk images into a single image + 2) to defragment a sparse disk image and remove unused space + + Examples: + Space Saving + 1) Create a sparse disk image using bximage. Set size to eg 10GB. Only allocated space will be stored, + so your drive image should be only about as large as the files stored on it. + + Disk Rollback: + 1) Create a sparse disk image called "c.img.0". Point .bochsrc at "c.img.0". + In bochs, install your favourite OS. Switch off bochs. + 2) Create a sparse disk image (of the same size) and name it "c.img.1". Point .bochsrc at "c.img.1" + "c.img.0" is visible, but all writes go to "c.img.1". After using bochs, you can simply delete + "c.img.1" to undo changes and go back to a clean OS install. + + Disk Optional Commit: + 1) Create a sparse disk image called "c.img.0". Point .bochsrc at "c.img.0". + In bochs, install your favourite OS. Switch off bochs. + 2) Create a sparse disk image (of the same size) and name it "c.img.1". Point .bochsrc at "c.img.1" + "c.img.0" is visible, but all writes go to "c.img.1". After using bochs, if you want to keep the + changes, use the (currently non-existant) merge utility to make a single unified drive image. + Alternatively simply create a new partition on top called "c.img.3". + + Common Base: + 1) Create a sparse disk image called "base.img". Point .bochsrc at "base.img". + In bochs, install your favourite OS. Switch off bochs. + 2) Create a sparse disk image (of the same size) and name it "www.img.1". Make "wwww.img.0" a symlink to + "base.img". Point .bochsrc at "www.img.1". Using bochs, install a webserver. + 3) Create a symlink to "base.img" called "db.img.0". Create a sparse disk image (of the same size) + and name it "db.img.1". Point .bochsrc at "db.img.1". Using bochs, install a database server. + + Now both a database server and webserver can be run in separate virtual machines, but they share + the common OS image, saving drive space. + + + + Disk Rollback and Optional Commit will probably be the most used of these options. + + +Patch was created with: + cvs diff -u +Apply patch to what version: + cvs checked out on 27 Jan 2003 +Instructions: + To patch, go to main bochs directory. + Type "patch -p0 < patch.sparsedisk". +---------------------------------------------------------------------- + +Index: bochs.h +=================================================================== +RCS file: /cvsroot/bochs/bochs/bochs.h,v +retrieving revision 1.117 +diff -u -r1.117 bochs.h +--- bochs.h 10 Jan 2003 22:43:51 -0000 1.117 ++++ bochs.h 27 Jan 2003 12:37:19 -0000 +@@ -49,6 +49,8 @@ + #include + #include + #include ++#include ++ + #ifndef WIN32 + # include + #else +Index: config.h.in +=================================================================== +RCS file: /cvsroot/bochs/bochs/config.h.in,v +retrieving revision 1.104 +diff -u -r1.104 config.h.in +--- config.h.in 23 Jan 2003 19:31:25 -0000 1.104 ++++ config.h.in 27 Jan 2003 12:37:21 -0000 +@@ -294,6 +294,16 @@ + // [[Provide example of partitioning]] + #define BX_SPLIT_HD_SUPPORT 0 + ++// This option enables "sparse hard drive" support, which means that ++// 1) A large hard drive can be created, and only used space will be stored ++// in the file. In practice this is not a large gain as Unix does this ++// anyway. ++// 2) Multiple sparse drive images can be mounted on top of each other. ++// Writes go to the top image. This allows several similar configurations ++// to share a master "base" file, and also allows filesystem rollback or ++// no-write options ++#define BX_SPARSE_HD_SUPPORT 0 ++ + // This option defines the number of supported ATA channels. + // There are up to two drives per ATA channel. + #define BX_MAX_ATA_CHANNEL 4 +Index: configure +=================================================================== +RCS file: /cvsroot/bochs/bochs/configure,v +retrieving revision 1.198 +diff -u -r1.198 configure +--- configure 20 Jan 2003 20:12:59 -0000 1.198 ++++ configure 27 Jan 2003 12:37:44 -0000 +@@ -1015,6 +1015,7 @@ + --enable-cpu-level select cpu level (3,4,5,6) + --enable-apic enable APIC support + --enable-split-hd allows split hard disk image ++ --enable-sparse-hd allows sparse hard disk image + --enable-ne2000 enable limited ne2000 support + --enable-pci enable limited i440FX PCI support + --enable-dc2300-vlb-ide enable Promise DC2300 VLB-IDE support +@@ -4208,7 +4209,7 @@ + case $host in + *-*-irix6*) + # Find out which ABI we are using. +- echo '#line 4211 "configure"' > conftest.$ac_ext ++ echo '#line 4212 "configure"' > conftest.$ac_ext + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? +@@ -4758,7 +4759,7 @@ + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -o out/conftest2.$ac_objext" + compiler_c_o=no +-if { (eval echo configure:4761: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then ++if { (eval echo configure:4762: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s out/conftest.err; then +@@ -6589,7 +6590,7 @@ + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext <&5 ++echo $ECHO_N "checking for sparse hard disk image support... $ECHO_C" >&6 ++# Check whether --enable-sparse-hd or --disable-sparse-hd was given. ++if test "${enable_sparse_hd+set}" = set; then ++ enableval="$enable_sparse_hd" ++ if test "$enableval" = yes; then ++ echo "$as_me:$LINENO: result: yes" >&5 ++echo "${ECHO_T}yes" >&6 ++ cat >>confdefs.h <<\_ACEOF ++#define BX_SPARSE_HD_SUPPORT 1 ++_ACEOF ++ ++ else ++ echo "$as_me:$LINENO: result: no" >&5 ++echo "${ECHO_T}no" >&6 ++ cat >>confdefs.h <<\_ACEOF ++#define BX_SPARSE_HD_SUPPORT 0 ++_ACEOF ++ ++ fi ++else ++ ++ echo "$as_me:$LINENO: result: no" >&5 ++echo "${ECHO_T}no" >&6 ++ cat >>confdefs.h <<\_ACEOF ++#define BX_SPARSE_HD_SUPPORT 0 ++_ACEOF ++ ++ ++ ++fi; ++ ++ + echo "$as_me:$LINENO: checking for NE2000 support" >&5 + echo $ECHO_N "checking for NE2000 support... $ECHO_C" >&6 + # Check whether --enable-ne2000 or --disable-ne2000 was given. +@@ -23453,6 +23487,7 @@ + s,@IOAPIC_OBJS@,$IOAPIC_OBJS,;t t + s,@APIC_OBJS@,$APIC_OBJS,;t t + s,@BX_SPLIT_HD_SUPPORT@,$BX_SPLIT_HD_SUPPORT,;t t ++s,@BX_SPARSE_HD_SUPPORT@,$BX_SPARSE_HD_SUPPORT,;t t + s,@NE2K_OBJS@,$NE2K_OBJS,;t t + s,@NETLOW_OBJS@,$NETLOW_OBJS,;t t + s,@PCI_OBJ@,$PCI_OBJ,;t t +Index: configure.in +=================================================================== +RCS file: /cvsroot/bochs/bochs/configure.in,v +retrieving revision 1.198 +diff -u -r1.198 configure.in +--- configure.in 20 Jan 2003 20:10:25 -0000 1.198 ++++ configure.in 27 Jan 2003 12:37:46 -0000 +@@ -701,6 +701,23 @@ + ) + AC_SUBST(BX_SPLIT_HD_SUPPORT) + ++AC_MSG_CHECKING(for sparse hard disk image support) ++AC_ARG_ENABLE(sparse-hd, ++ [ --enable-sparse-hd allows sparse hard disk image], ++ [if test "$enableval" = yes; then ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(BX_SPARSE_HD_SUPPORT, 1) ++ else ++ AC_MSG_RESULT(no) ++ AC_DEFINE(BX_SPARSE_HD_SUPPORT, 0) ++ fi], ++ [ ++ AC_MSG_RESULT(no) ++ AC_DEFINE(BX_SPARSE_HD_SUPPORT, 0) ++ ] ++ ) ++AC_SUBST(BX_SPARSE_HD_SUPPORT) ++ + AC_MSG_CHECKING(for NE2000 support) + AC_ARG_ENABLE(ne2000, + [ --enable-ne2000 enable limited ne2000 support], +Index: iodev/harddrv.cc +=================================================================== +RCS file: /cvsroot/bochs/bochs/iodev/harddrv.cc,v +retrieving revision 1.94 +diff -u -r1.94 harddrv.cc +--- iodev/harddrv.cc 5 Jan 2003 03:22:03 -0000 1.94 ++++ iodev/harddrv.cc 27 Jan 2003 12:37:52 -0000 +@@ -31,12 +31,14 @@ + + + // Define BX_PLUGGABLE in files that can be compiled into plugins. For +-// platforms that require a special tag on exported symbols, BX_PLUGGABLE ++// 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 "bochs.h" + ++#include ++ + #define LOG_THIS theHardDrive-> + + // WARNING: dangerous options! +@@ -118,31 +120,43 @@ + #if DLL_HD_SUPPORT + # error code must be fixed to use DLL_HD_SUPPORT and 4 ata channels + #endif +- ++ + for (Bit8u channel=0; channelstr); // 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); ++} ++#endif ++ + #if BX_SPLIT_HD_SUPPORT + /*** concat_image_t function definitions ***/ + +@@ -3171,13 +3199,7 @@ + + void concat_image_t::increment_string (char *str) + { +- // 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); // increment to next ascii code. +- BX_DEBUG(("concat_image.increment string returning '%s'", str)); ++ ::increment_string(str, +1); + } + + int concat_image_t::open (const char* pathname0) +@@ -3287,7 +3309,7 @@ + // 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) ++ if (!seek_was_last_op) + BX_PANIC( ("no seek before read")); + return ::read(fd, (char*) buf, count); + } +@@ -3303,6 +3325,566 @@ + return ::write(fd, (char*) buf, count); + } + #endif /* BX_SPLIT_HD_SUPPORT */ ++ ++#if BX_SPARSE_HD_SUPPORT ++/* Do these utility functions maybe already exist elsewhere? ++*/ ++ ++inline uint32 little_to_local_endian(uint32 value) ++{ ++#ifdef BX_LITTLE_ENDIAN ++ // Already in local endian ++ return value; ++#else /* BX_BIG_ENDIAN */ ++ return read_32bit(&value); ++#endif ++} ++ ++ ++inline uint32 local_to_little_endian(uint32 value) ++{ ++#ifdef BX_LITTLE_ENDIAN ++ // Already in local endian ++ return value; ++#else /* BX_BIG_ENDIAN */ ++ uint32 ret = (((value >> 0) & 0xff) << 24) + (((value >> 8) & 0xff) << 16) + (((value >> 16) & 0xff) << 8) + (((value >> 24) & 0xff) << 0); ++ ++ return ret; ++#endif ++} ++#endif // BX_SPARSE_HD_SUPPORT ++ ++#if BX_SPARSE_HD_SUPPORT ++ ++/*** 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; ++} ++ ++ ++/* ++void showpagetable(uint32 * 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) == 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 (little_to_local_endian(header.magic) != SPARSE_HEADER_MAGIC) ++ { ++ panic("failed header magic check"); ++ } ++ ++ if (little_to_local_endian(header.version) != 1) ++ { ++ panic("unknown version in header"); ++ } ++ ++ pagesize = little_to_local_endian(header.pagesize); ++ uint32 numpages = little_to_local_endian(header.numpages); ++ ++ total_size = pagesize; ++ total_size *= numpages; ++ ++ pagesize_shift = 0; ++ while ((pagesize >> pagesize_shift) > 1) pagesize_shift++; ++ ++ if ((1 << pagesize_shift) != pagesize) ++ { ++ panic("failed block size header check"); ++ } ++ ++ pagesize_mask = pagesize - 1; ++ ++ size_t preamble_size = (sizeof(uint32) * numpages) + sizeof(header); ++ data_start = 0; ++ while (data_start < preamble_size) data_start += pagesize; ++ ++ bool did_mmap = false; ++ ++#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 = true; ++ pagetable = ((uint32 *) (((uint8 *) mmap_header) + sizeof(header))); ++ ++// system_pagesize = getpagesize(); ++ system_pagesize_mask = getpagesize() - 1; ++ } ++#endif ++ ++ if (!did_mmap) ++ { ++ pagetable = new uint32[numpages]; ++ ++ if (pagetable == NULL) ++ { ++ panic("could not allocate memory for sparse disk block table"); ++ } ++ ++ ret = ::read(fd, pagetable, sizeof(uint32) * numpages); ++ ++ if (-1 == ret) ++ { ++ panic(strerror(errno)); ++ } ++ ++ if ((sizeof(uint32) * 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) ++ { ++ // open failed. ++ return -1; ++ } ++ BX_DEBUG(("sparse_image: open image %s", pathname)); ++ ++ read_header(); ++ ++ struct stat stat_buf; ++ if (0 != fstat(fd, &stat_buf)) 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 == lstat(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); ++ ++ 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; ++ } ++} ++ ++off_t sparse_image_t::lseek (off_t 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(offset >> pagesize_shift); ++ position_page_offset = offset & pagesize_mask; ++ ++ return 0; ++} ++ ++inline off_t sparse_image_t::get_physical_offset() ++{ ++ off_t physical_offset = data_start; ++ physical_offset += (position_physical_page << pagesize_shift); ++ physical_offset += position_page_offset; ++ ++ return physical_offset; ++} ++ ++inline void sparse_image_t::set_virtual_page(uint32 new_virtual_page) ++{ ++ position_virtual_page = new_virtual_page; ++ ++ position_physical_page = little_to_local_endian(pagetable[position_virtual_page]); ++} ++ ++ssize_t sparse_image_t::read_page_fragment(uint32 read_virtual_page, uint32 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 == 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, read_size, 0); ++ } ++ } ++ else ++ { ++ off_t physical_offset = get_physical_offset(); ++ ++ if (physical_offset != underlying_current_filepos) ++ { ++ int ret = ::lseek(fd, 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 (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; ++ ++ if (bx_dbg.disk) ++ 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); ++ ++ size_t was_read = read_page_fragment(position_virtual_page, position_page_offset, can_read, buf); ++ ++ 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 = (((uint8 *) 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((buffer)); ++} ++ ++ssize_t sparse_image_t::write (const void* buf, size_t count) ++{ ++ //showpagetable(pagetable, header.numpages); ++ ++ ssize_t total_written = 0; ++ ++ uint32 update_pagetable_start = position_virtual_page; ++ uint32 update_pagetable_count = 0; ++ ++ if (bx_dbg.disk) ++ 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 == PAGE_NOT_ALLOCATED) ++ { ++ // We just add on another page at the end of the file ++ // Reclamation, compaction etc should currently be done off-line ++ ++ size_t data_size = underlying_filesize - data_start; ++ BX_ASSERT((data_size % pagesize) == 0); ++ ++ ++ uint32 data_size_pages = data_size / pagesize; ++ uint32 next_data_page = data_size_pages; ++ ++ pagetable[position_virtual_page] = local_to_little_endian(next_data_page); ++ position_physical_page = next_data_page; ++ ++ off_t page_file_start = data_start + (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 = ((uint8 *) writebuffer) + position_page_offset; ++ memcpy(dest_start, buf, can_write); ++ } ++ ++ int ret; ++ ret = ::lseek(fd, page_file_start, SEEK_SET); ++ // underlying_current_filepos update deferred ++ if (-1 == ret) panic(strerror(errno)); ++ ++ ret = ::write(fd, writebuffer, pagesize); ++ ++ if (-1 == ret) panic(strerror(errno)); ++ ++ if (pagesize != 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; ++ ret = ::lseek(fd, page_file_start + pagesize - 4, SEEK_SET); ++ // underlying_current_filepos update deferred ++ if (-1 == ret) panic(strerror(errno)); ++ ++ uint32 zero = 0; ++ ret = ::write(fd, &zero, 4); ++ ++ if (-1 == ret) panic(strerror(errno)); ++ ++ if (4 != ret) 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 != PAGE_NOT_ALLOCATED); ++ ++ off_t physical_offset = get_physical_offset(); ++ ++ if (physical_offset != underlying_current_filepos) ++ { ++ int ret = ::lseek(fd, 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 (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 = (((uint8 *) buf) + can_write); ++ count -= can_write; ++ } ++ ++ if (update_pagetable_count != 0) ++ { ++ bool done = false; ++ off_t pagetable_write_from = sizeof(header) + (sizeof(uint32) * update_pagetable_start); ++ size_t write_bytecount = update_pagetable_count * sizeof(uint32); ++ ++#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 = ((uint8 *) 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 = true; ++ } ++#endif ++ ++ if (!done) ++ { ++ int ret = ::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 (ret != write_bytecount) panic("could not write entire updated block header"); ++ ++ underlying_current_filepos = pagetable_write_from + write_bytecount; ++ } ++ } ++ ++ return total_written; ++} ++#endif /* BX_SPARSE_HD_SUPPORT */ + + #if DLL_HD_SUPPORT + /*** dll_image_t function definitions ***/ +Index: iodev/harddrv.h +=================================================================== +RCS file: /cvsroot/bochs/bochs/iodev/harddrv.h,v +retrieving revision 1.18 +diff -u -r1.18 harddrv.h +--- iodev/harddrv.h 25 Oct 2002 11:44:40 -0000 1.18 ++++ iodev/harddrv.h 27 Jan 2003 12:37:53 -0000 +@@ -24,6 +24,32 @@ + // License along with this library; if not, write to the Free Software + // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ++ ++#if BX_SPARSE_HD_SUPPORT ++ ++// Format of a sparse file: ++// 256 byte header, containing details such as page size and number of pages ++// Page indirection table, mapping virtual pages to physical pages within file ++// Physical pages till end of file ++ ++#define SPARSE_HEADER_MAGIC (0x02468ace) ++#define SPARSE_HEADER_VERSION 1 ++#define HEADER_SIZE (256) // Plenty of room for later ++#define PAGE_NOT_ALLOCATED (0xffffffff) ++ ++ typedef struct ++ { ++ uint32 magic; ++ uint32 version; ++ uint32 pagesize; ++ uint32 numpages; ++ ++ uint32 padding[60]; ++ } sparse_header_t; ++#endif /* BX_SPARSE_HD_SUPPORT */ ++ ++#ifndef INCLUDE_ONLY_SPARSE_HEADER ++ + typedef enum _sense { + SENSE_NONE = 0, SENSE_NOT_READY = 2, SENSE_ILLEGAL_REQUEST = 5, + SENSE_UNIT_ATTENTION = 6 +@@ -87,7 +113,7 @@ + + private: + int fd; +- ++ + }; + + #if BX_SPLIT_HD_SUPPORT +@@ -137,6 +163,77 @@ + }; + #endif /* BX_SPLIT_HD_SUPPORT */ + ++#if BX_SPARSE_HD_SUPPORT ++ ++class sparse_image_t : public device_image_t ++{ ++ public: ++ // Default constructor ++ sparse_image_t(); ++ ++ // Open a image. Returns non-negative if successful. ++ int open (const char* pathname); ++ ++ // Close the image. ++ void close (); ++ ++ // Position ourselves. Return the resulting offset from the ++ // beginning of the file. ++ off_t lseek (off_t offset, int whence); ++ ++ // Read count bytes to the buffer buf. Return the number of ++ // bytes read (count). ++ ssize_t read (void* buf, size_t count); ++ ++ // Write count bytes from buf. Return the number of bytes ++ // written (count). ++ ssize_t write (const void* buf, size_t count); ++ ++ private: ++ int fd; ++ ++#ifdef _POSIX_MAPPED_FILES ++ void * mmap_header; ++ size_t mmap_length; ++ size_t system_pagesize_mask; ++#endif ++ uint32 * pagetable; ++ ++ // Header is written to disk in little-endian (x86) format ++ // Thus needs to be converted on big-endian systems before read ++ // The pagetable is also kept little endian ++ ++ sparse_header_t header; ++ ++ uint32 pagesize; ++ int pagesize_shift; ++ uint32 pagesize_mask; ++ ++ off_t data_start; ++ off_t underlying_filesize; ++ ++ char * pathname; ++ ++ off_t position; ++ ++ uint32 position_virtual_page; ++ uint32 position_physical_page; ++ uint32 position_page_offset; ++ ++ off_t underlying_current_filepos; ++ ++ off_t total_size; ++ ++ void panic(const char * message); ++ off_t sparse_image_t::get_physical_offset(); ++ void sparse_image_t::set_virtual_page(uint32 new_virtual_page); ++ void read_header(); ++ ssize_t read_page_fragment(uint32 read_virtual_page, uint32 read_page_offset, size_t read_size, void * buf); ++ ++ sparse_image_t * parent_image; ++}; ++#endif /* BX_SPARSE_HD_SUPPORT */ ++ + #if EXTERNAL_DISK_SIMULATOR + #include "external-disk-simulator.h" + #endif +@@ -367,3 +464,5 @@ + #endif + + }; ++ ++#endif +Index: misc/bximage.c +=================================================================== +RCS file: /cvsroot/bochs/bochs/misc/bximage.c,v +retrieving revision 1.17 +diff -u -r1.17 bximage.c +--- misc/bximage.c 26 Nov 2002 11:21:31 -0000 1.17 ++++ misc/bximage.c 27 Jan 2003 12:37:53 -0000 +@@ -1,4 +1,4 @@ +-/* ++/* + * misc/bximage.c + * $Id: patch.sparsedisk-justinsb,v 1.1 2003-01-29 18:20:19 cbothamy Exp $ + * +@@ -16,6 +16,18 @@ + #endif + #include "config.h" + ++#if BX_SPARSE_HD_SUPPORT ++#include ++ ++#define uint8 Bit8u ++#define uint16 Bit16u ++#define uint32 Bit32u ++ ++#define INCLUDE_ONLY_SPARSE_HEADER 1 ++#include "../iodev/harddrv.h" ++ ++#endif // BX_SPARSE_HD_SUPPORT ++ + char *EOF_ERR = "ERROR: End of input"; + char *rcsid = "$Id: patch.sparsedisk-justinsb,v 1.1 2003-01-29 18:20:19 cbothamy Exp $"; + char *divider = "========================================================================"; +@@ -190,12 +202,41 @@ + return 0; + } + ++#if BX_SPARSE_HD_SUPPORT ++// fileset is like memset but for a file handle ++void fileset(FILE * fp, int c, size_t n) ++{ ++#define BLOCK_SIZE (1024) ++ int block[BLOCK_SIZE]; ++ size_t left_to_write = n; ++ ++ memset(block, c, sizeof(block)); ++ ++ while (left_to_write > 0) ++ { ++ size_t write = sizeof(block); ++ if (write > left_to_write) write = left_to_write; ++ ++ if (1 != fwrite(block, write, 1, fp)) ++ { ++ fclose (fp); ++ fatal ("ERROR: The disk image is not complete - could not write data block!"); ++ } ++ ++ left_to_write -= write; ++ } ++ ++} ++#endif ++ + /* produce the image file */ +-int make_image (Bit64u sec, char *filename) ++int make_image (Bit64u sec, char *filename, int sparse) + { +- FILE *fp; ++ FILE *fp; + char buffer[1024]; +- ++#if BX_SPARSE_HD_SUPPORT ++ Bit64u numpages; ++#endif + // check if it exists before trashing someone's disk image + fp = fopen (filename, "r"); + if (fp) { +@@ -203,7 +244,7 @@ + sprintf (buffer, "\nThe disk image '%s' already exists. Are you sure you want to replace it?\nPlease type yes or no. [no] ", filename); + if (ask_yn (buffer, 0, &confirm) < 0) + fatal (EOF_ERR); +- if (!confirm) ++ if (!confirm) + fatal ("ERROR: Aborted"); + fclose (fp); + } +@@ -221,27 +262,69 @@ + + printf ("\nWriting: ["); + +- /* ++#if BX_SPARSE_HD_SUPPORT ++ if (sparse) ++ { ++ sparse_header_t header; ++ size_t sizesofar; ++ size_t padtopagesize; ++ ++ memset(&header, 0, sizeof(header)); ++ header.magic = SPARSE_HEADER_MAGIC; ++ header.version = SPARSE_HEADER_VERSION; ++ ++ header.pagesize = (1 << 10) * 32; // Use 32 KB Pages - could be configurable ++ numpages = (sec / (header.pagesize / 512)) + 1; ++ ++ header.numpages = numpages; ++ ++ if (numpages != header.numpages) ++ { ++ fclose (fp); ++ 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! ++ } ++ ++ if (fwrite(&header, sizeof(header), 1, fp) != 1) ++ { ++ fclose (fp); ++ fatal ("ERROR: The disk image is not complete - could not write header!"); ++ } ++ ++ fileset(fp, 0xff, 4 * header.numpages); ++ ++ sizesofar = HEADER_SIZE + (4 * header.numpages); ++ padtopagesize = header.pagesize - (sizesofar & (header.pagesize - 1)); ++ ++ fileset(fp, 0, padtopagesize); ++ } ++#endif ++ ++ if (!sparse) ++ { ++ /* + * seek to sec*512-1 and write a single character. + * can't just do: fseek(fp, 512*sec-1, SEEK_SET) + * because 512*sec may be too large for signed int. + */ +- while (sec > 0) +- { +- /* temp <-- min(sec, 4194303) ++ while (sec > 0) ++ { ++ /* temp <-- min(sec, 4194303) + * 4194303 is (int)(0x7FFFFFFF/512) + */ +- long temp = ((sec < 4194303) ? sec : 4194303); +- fseek(fp, 512*temp, SEEK_CUR); +- sec -= temp; +- } +- +- fseek(fp, -1, SEEK_CUR); +- if (fputc('\0', fp) == EOF) +- { +- fclose (fp); +- fatal ("ERROR: The disk image is not complete!"); +- } ++ long temp = ((sec < 4194303) ? sec : 4194303); ++ fseek(fp, 512*temp, SEEK_CUR); ++ sec -= temp; ++ } ++ ++ fseek(fp, -1, SEEK_CUR); ++ if (fputc('\0', fp) == EOF) ++ { ++ fclose (fp); ++ fatal ("ERROR: The disk image is not complete!"); ++ } ++ } + + printf ("] Done.\n"); + fclose (fp); +@@ -254,6 +337,7 @@ + Bit64s sectors = 0; + char filename[256]; + char bochsrc_line[256]; ++ int sparse = 0; + print_banner (); + filename[0] = 0; + if (ask_menu (fdhd_menu, fdhd_n_choices, fdhd_choices, 1, &hd) < 0) +@@ -273,6 +357,10 @@ + printf (" total size=%.2f megabytes\n", (float)sectors*512.0/1024.0/1024.0); + if (ask_string ("\nWhat should I name the image?\n[c.img] ", "c.img", filename) < 0) + fatal (EOF_ERR); ++#if BX_SPARSE_HD_SUPPORT ++ if (ask_yn ("\nShould I create a sparse image?\nPlease type yes or no. [no] ", 0, &sparse) < 0) ++ fatal (EOF_ERR); ++#endif //BX_SPARSE_HD_SUPPORT + sprintf (bochsrc_line, "ata0-master: type=disk, path=\"%s\", cylinders=%d, heads=%d, spt=%d", filename, cyl, heads, spt); + } else { + int fdsize, cyl=0, heads=0, spt=0; +@@ -285,7 +373,7 @@ + case 2: name="1_2"; cyl=80; heads=2; spt=15; break; /* 1.2 meg */ + case 3: name="1_44"; cyl=80; heads=2; spt=18; break; /* 1.44 meg */ + case 4: name="2_88"; cyl=80; heads=2; spt=36; break; /* 2.88 meg */ +- default: ++ default: + fatal ("ERROR: fdsize out of range"); + } + sectors = cyl*heads*spt; +@@ -303,13 +391,13 @@ + fatal ("ERROR: Illegal disk size!"); + if (strlen (filename) < 1) + fatal ("ERROR: Illegal filename"); +- make_image (sectors, filename); ++ make_image (sectors, filename, sparse); + printf ("\nI wrote %lld bytes to %s.\n", sectors*512, filename); + printf ("\nThe following line should appear in your bochsrc:\n"); + printf (" %s\n", bochsrc_line); + myexit(0); + + // make picky compilers (c++, gcc) happy, +- // even though we leave via 'myexit' just above ++ // even though we leave via 'myexit' just above + return 0; + }