From 7665252790c18158a37d80a0759cc6faf050b15b Mon Sep 17 00:00:00 2001 From: asvitkine <> Date: Tue, 19 Oct 2010 03:21:52 +0000 Subject: [PATCH] [Geoffrey Brown] For my work on digital preservation it's important to have "golden" disk images that are not corrupted by user action. In order to enable this, I've added support for VHD virtual disks (especially snapshots !) to the Linux and OS X versions of BasiliskII and SheepShaver. The support uses the open source libvhd library which is part of xen, available here: http://www.xen.org/products/xen_source.html The piece that's needed is libvhd which is in tools/blktap2 and it can be separately compiled. The vhd-util enables creation of vhd disks and snapshots. Compiling libvhd for OS X is non-trivial and required 1) a new config and 2) a number of small changes to the include files and c files. Compiling for linux is a snap. I use this as follows. 1) create my "golden image" gold.dsk in the usual way 2) create a snapshot: vhd-util snapshot -n gold.vhd -p gold.dsk -m 3) use the snapshot in my prefs file In my work the golden images are in an AFS system which means the golden images can reside at "universal" addresses. The snapshots are initially tiny, so a complete virtual machine configuration -- prefs + snapshot is quick to download for the end user. The snapshots are copy on write which has the pleasant side effect of letting the end user keep any changes. --- BasiliskII/src/Unix/configure.ac | 28 +++++ BasiliskII/src/Unix/sys_unix.cpp | 169 ++++++++++++++++++------------- BasiliskII/src/Unix/vhd_unix.cpp | 124 +++++++++++++++++++++++ BasiliskII/src/Unix/vhd_unix.h | 29 ++++++ 4 files changed, 281 insertions(+), 69 deletions(-) create mode 100644 BasiliskII/src/Unix/vhd_unix.cpp create mode 100644 BasiliskII/src/Unix/vhd_unix.h diff --git a/BasiliskII/src/Unix/configure.ac b/BasiliskII/src/Unix/configure.ac index 63219dc6..1725bf93 100644 --- a/BasiliskII/src/Unix/configure.ac +++ b/BasiliskII/src/Unix/configure.ac @@ -77,6 +77,10 @@ AC_ARG_WITH(mon, [ --with-mon use mon as debugger [def AC_ARG_WITH(bincue, AS_HELP_STRING([--with-bincue], [Allow cdrom image files in bin/cue mode])) +AC_ARG_WITH(libvhd, + AS_HELP_STRING([--with-libvhd], [Enable VHD disk images])) + + dnl Canonical system information. AC_CANONICAL_HOST AC_CANONICAL_TARGET @@ -261,6 +265,22 @@ AS_IF([test "x$have_bincue" = "xyes" ], [ fi ]) +dnl LIBVHD +AS_IF([test "x$with_libvhd" = "xyes" ], [have_libvhd=yes], [have_libvhd=no]) +AS_IF([test "x$have_libvhd" = "xyes" ], [ + CPPFLAGS="$CPPFLAGS -DHAVE_LIBVHD" + LIBS="$LIBS -lvhd" + case $target_os in + linux*) + LIBS="$LIBS -luuid" + esac + AC_CHECK_LIB(vhd, vhd_open) + AC_CHECK_LIB(vhd, vhd_io_read) + AC_CHECK_LIB(vhd, vhd_io_write) + AC_CHECK_LIB(vhd, vhd_close) +]) + + dnl We want pthreads. Try libpthread first, then libc_r (FreeBSD), then PTL. HAVE_PTHREADS=yes @@ -717,6 +737,13 @@ if [[ "x$have_bincue" = "xyes" ]]; then EXTRASYSSRCS="$EXTRASYSSRCS bincue_unix.cpp" fi +dnl libvhd overrides + +if [[ "x$have_libvhd" = "xyes" ]]; then + EXTRASYSSRCS="$EXTRASYSSRCS vhd_unix.cpp" +fi + + dnl Use 68k CPU natively? WANT_NATIVE_M68K=no if [[ "x$HAVE_M68K" = "xyes" -a "x$CAN_NATIVE_M68K" = "xyes" ]]; then @@ -1654,6 +1681,7 @@ echo Basilisk II configuration summary: echo echo SDL support ............................ : $SDL_SUPPORT echo BINCUE support ......................... : $have_bincue +echo LIBVHD support ......................... : $have_libvhd echo XFree86 DGA support .................... : $WANT_XF86_DGA echo XFree86 VidMode support ................ : $WANT_XF86_VIDMODE echo fbdev DGA support ...................... : $WANT_FBDEV_DGA diff --git a/BasiliskII/src/Unix/sys_unix.cpp b/BasiliskII/src/Unix/sys_unix.cpp index 8ac4bc0a..4c29cd92 100644 --- a/BasiliskII/src/Unix/sys_unix.cpp +++ b/BasiliskII/src/Unix/sys_unix.cpp @@ -61,6 +61,11 @@ #include "bincue_unix.h" #endif +#if defined(HAVE_LIBVHD) +#include "vhd_unix.h" +#endif + + #define DEBUG 0 #include "debug.h" @@ -68,15 +73,15 @@ struct file_handle { char *name; // Copy of device/file name int fd; + bool is_file; // Flag: plain file or /dev/something? bool is_floppy; // Flag: floppy device bool is_cdrom; // Flag: CD-ROM device -#if defined(BINCUE) - bool is_bincue; // Flag: BIN CUE file -#endif bool read_only; // Copy of Sys_open() flag - loff_t start_byte; // Size of file header (if any) + + loff_t start_byte; // Size of file header (if any) loff_t file_size; // Size of file data (only valid if is_file is true) + bool is_media_present; // Flag: media is inserted and available #if defined(__linux__) @@ -89,8 +94,14 @@ struct file_handle { #endif #if defined(BINCUE) + bool is_bincue; // Flag: BIN CUE file void *bincue_fd; #endif + +#if defined(HAVE_LIBVHD) + bool is_vhd; // Flag: VHD file + void *vhd_fd; +#endif }; // Open file handles @@ -507,6 +518,19 @@ static bool is_drive_mounted(const char *dev_name, char *mount_name) /* * Open file/device, create new file handle (returns NULL on error) */ + +static file_handle *open_filehandle(const char *name) +{ + file_handle *fh = new file_handle; + memset(fh, 0, sizeof(file_handle)); + fh->name = strdup(name); + fh->fd = -1; +#if defined __MACOSX__ + fh->ioctl_fd = -1; + fh->ioctl_name = NULL; +#endif + return fh; +} void *Sys_open(const char *name, bool read_only) { @@ -562,27 +586,34 @@ void *Sys_open(const char *name, bool read_only) // Open file/device #if defined(BINCUE) - void *binfd = open_bincue(name); if (binfd) { - file_handle *fh = new file_handle; - fh->fd = 0; - fh->is_file = false; - fh->name = strdup(name); + file_handle *fh = open_filehandle(name); + D(bug("opening %s as bincue\n", name)); fh->bincue_fd = binfd; fh->is_bincue = true; - fh->read_only = true; - fh->start_byte = 0; - fh->is_floppy = false; - fh->is_cdrom = false; + fh->read_only = true; fh->is_media_present = true; -#if defined __MACOSX__ - fh->ioctl_fd = -1; - fh->ioctl_name = NULL; + sys_add_file_handle(fh); + return fh; + } #endif - sys_add_file_handle(fh); - return fh; - } + + +#if defined(HAVE_LIBVHD) + int vhdsize; + void *vhdfd = vhd_unix_open(name, &vhdsize, read_only); + if (vhdfd) { + file_handle *fh = open_filehandle(name); + D(bug("opening %s as vnd\n", name)); + fh->is_vhd = true; + fh->vhd_fd = vhdfd; + fh->read_only = read_only; + fh->file_size = vhdsize; + fh->is_media_present = true; + sys_add_file_handle(fh); + return fh; + } #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__) @@ -596,26 +627,12 @@ void *Sys_open(const char *name, bool read_only) fd = open(name, O_RDONLY); } if (fd >= 0 || is_polled_media) { - file_handle *fh = new file_handle; - fh->name = strdup(name); + file_handle *fh = open_filehandle(name); fh->fd = fd; fh->is_file = is_file; fh->read_only = read_only; - fh->start_byte = 0; fh->is_floppy = is_floppy; fh->is_cdrom = is_cdrom; - fh->is_media_present = false; -#if defined __linux__ - fh->cdrom_cap = 0; -#endif -#if defined __MACOSX__ - fh->ioctl_fd = -1; - fh->ioctl_name = NULL; -#endif -#if defined(BINCUE) - fh->is_bincue = false; - fh->bincue_fd = NULL; -#endif if (fh->is_file) { fh->is_media_present = true; // Detect disk image file layout @@ -639,8 +656,6 @@ void *Sys_open(const char *name, bool read_only) if (fh->cdrom_cap < 0) fh->cdrom_cap = 0; } -#else - fh->cdrom_cap = 0; #endif #elif defined(__FreeBSD__) fh->is_floppy = ((st.st_rdev >> 16) == 2); @@ -649,8 +664,6 @@ void *Sys_open(const char *name, bool read_only) if (ioctl(fh->fd, CDIOCCAPABILITY, &fh->cdrom_cap) < 0) memset(&fh->cdrom_cap, 0, sizeof(fh->cdrom_cap)); } -#else - fh->cdrom_cap = 0; #endif #elif defined(__NetBSD__) fh->is_floppy = ((st.st_rdev >> 16) == 2); @@ -689,11 +702,14 @@ void Sys_close(void *arg) sys_remove_file_handle(fh); +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + vhd_unix_close(fh->vhd_fd); +#endif + #if defined(BINCUE) - if (fh->is_bincue) { - close_bincue(fh->bincue_fd); - fh->bincue_fd = NULL; - } + if (fh->is_bincue) + close_bincue(fh->bincue_fd); #endif if (fh->is_cdrom) @@ -718,9 +734,13 @@ size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length) return 0; #if defined(BINCUE) - if (fh->is_bincue) { - return read_bincue(fh->bincue_fd, buffer, offset, length); - } + if (fh->is_bincue) + return read_bincue(fh->bincue_fd, buffer, offset, length); +#endif + +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + return vhd_unix_read(fh->vhd_fd, buffer, offset, length); #endif // Seek to position @@ -743,6 +763,11 @@ size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length) if (!fh) return 0; +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + return vhd_unix_write(fh->vhd_fd, buffer, offset, length); +#endif + // Seek to position if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0) return 0; @@ -762,17 +787,21 @@ loff_t SysGetFileSize(void *arg) if (!fh) return true; +#if defined(BINCUE) + if (fh->is_bincue) + return size_bincue(fh->bincue_fd); +#endif + +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + return fh->file_size; +#endif + if (fh->is_file) return fh->file_size; else { long blocks; -#if defined(BINCUE) - if (fh->is_bincue) { - return size_bincue(fh->bincue_fd); - } -#endif #if defined(__linux__) - if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0) return 0; D(bug(" BLKGETSIZE returns %d blocks\n", blocks)); @@ -901,6 +930,11 @@ bool SysIsFixedDisk(void *arg) if (!fh) return true; +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + return true; +#endif + if (fh->is_file) return true; else if (fh->is_floppy || fh->is_cdrom) @@ -920,6 +954,11 @@ bool SysIsDiskInserted(void *arg) if (!fh) return false; +#if defined(HAVE_LIBVHD) + if (fh->is_vhd) + return true; +#endif + if (fh->is_file) { return true; @@ -1014,9 +1053,8 @@ bool SysCDReadTOC(void *arg, uint8 *toc) return false; #if defined(BINCUE) - if (fh->is_bincue){ + if (fh->is_bincue) return readtoc_bincue(fh->bincue_fd, toc); - } #endif if (fh->is_cdrom) { @@ -1162,10 +1200,8 @@ bool SysCDGetPosition(void *arg, uint8 *pos) return false; #if defined(BINCUE) - if (fh->is_bincue) { - + if (fh->is_bincue) return GetPosition_bincue(fh->bincue_fd, pos); - } #endif if (fh->is_cdrom) { @@ -1234,10 +1270,8 @@ bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end return false; #if defined(BINCUE) - if (fh->is_bincue) { - return CDPlay_bincue(fh->bincue_fd, start_m, start_s, - start_f, end_m, end_s, end_f); - } + if (fh->is_bincue) + return CDPlay_bincue(fh->bincue_fd, start_m, start_s, start_f, end_m, end_s, end_f); #endif if (fh->is_cdrom) { @@ -1278,9 +1312,8 @@ bool SysCDPause(void *arg) return false; #if defined(BINCUE) - if (fh->is_bincue){ - return CDPause_bincue(fh->bincue_fd); - } + if (fh->is_bincue) + return CDPause_bincue(fh->bincue_fd); #endif if (fh->is_cdrom) { @@ -1307,9 +1340,8 @@ bool SysCDResume(void *arg) return false; #if defined(BINCUE) - if (fh->is_bincue) { - return CDResume_bincue(fh->bincue_fd); - } + if (fh->is_bincue) + return CDResume_bincue(fh->bincue_fd); #endif @@ -1337,9 +1369,8 @@ bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f) return false; #if defined(BINCUE) - if (fh->is_bincue) { - return CDStop_bincue(fh->bincue_fd); - } + if (fh->is_bincue) + return CDStop_bincue(fh->bincue_fd); #endif diff --git a/BasiliskII/src/Unix/vhd_unix.cpp b/BasiliskII/src/Unix/vhd_unix.cpp new file mode 100644 index 00000000..5345d3ee --- /dev/null +++ b/BasiliskII/src/Unix/vhd_unix.cpp @@ -0,0 +1,124 @@ +/* + * vhd_unix.cpp -- support for disk images in vhd format + * + * (C) 2010 Geoffrey Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "vhd_unix.h" +#include +#include +#include +#include +extern "C" { +#include +} +// libvhd.h defines DEBUG +#undef DEBUG +#define DEBUG 0 +#include "debug.h" + +void *vhd_unix_open(const char *name, int *size, bool read_only) +{ + int amode = read_only ? R_OK : (R_OK | W_OK); + int fid; + vhd_context_t *vhd; + + D(bug("vhd open %s\n", name)); + + if (access(name, amode)) { + D(bug("vhd open -- incorrect permissions %s\n", name)); + return NULL; + } + + if (! (fid = open(name, O_RDONLY))) { + D(bug("vhd open -- couldn't open file %s\n", name)); + return NULL; + } + else { + char buf[9]; + read(fid, buf, sizeof(buf)-1); + buf[8] = 0; + close(fid); + if (strcmp("conectix", buf) != 0) { + D(bug("vhd open -- not vhd magic = %s\n", buf)); + return NULL; + } + if (vhd = (vhd_context_t *) malloc(sizeof(vhd_context_t))) { + int err; + if (err = vhd_open(vhd, name, read_only ? + VHD_OPEN_RDONLY : VHD_OPEN_RDWR)) { + D(bug("vhd_open failed (%d)\n", err)); + free(vhd); + return NULL; + } + else { + *size = (int) vhd->footer.curr_size; + printf("VHD Open %s\n", name); + return (void *) vhd; + } + } + else { + D(bug("vhd open -- malloc failed\n")); + return NULL; + } + } +} + +int vhd_unix_read(void *arg, void *buffer, loff_t offset, size_t length) +{ + vhd_context_t *ctx = (vhd_context_t *) arg; + int err; + if ((offset % VHD_SECTOR_SIZE) || (length % VHD_SECTOR_SIZE)) { + printf("vhd read only supported on sector boundaries (%d)\n", + VHD_SECTOR_SIZE); + return 0; + } + if (err = vhd_io_read(ctx, (char *) buffer, offset / VHD_SECTOR_SIZE, + length / VHD_SECTOR_SIZE)){ + D(bug("vhd read error %d\n", err)); + return err; + } + else + return length; +} + +int vhd_unix_write(void *arg, void *buffer, loff_t offset, size_t length) +{ + int err; + vhd_context_t *ctx = (vhd_context_t *) arg; + + if ((offset % VHD_SECTOR_SIZE) || (length % VHD_SECTOR_SIZE)) { + printf("vhd write only supported on sector boundaries (%d)\n", + VHD_SECTOR_SIZE); + return 0; + } + if (err = vhd_io_write(ctx, (char *) buffer, offset/VHD_SECTOR_SIZE, + length/VHD_SECTOR_SIZE)) { + D(bug("vhd write error %d\n", err)); + return err; + } + else + return length; +} + +void vhd_unix_close(void *arg) +{ + D(bug("vhd close\n")); + vhd_close((vhd_context_t *) arg); + free(arg); +} diff --git a/BasiliskII/src/Unix/vhd_unix.h b/BasiliskII/src/Unix/vhd_unix.h new file mode 100644 index 00000000..a791b7ae --- /dev/null +++ b/BasiliskII/src/Unix/vhd_unix.h @@ -0,0 +1,29 @@ +/* + * vhd_unix.h -- support for disk images in vhd format + * + * (C) 2010 Geoffrey Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VHD_H +#define VHD_H + +void *vhd_unix_open(const char *name, int *size, bool read_only); +int vhd_unix_read(void *arg, void *buffer, loff_t offset, size_t length); +int vhd_unix_write(void *arg, void *buffer, loff_t offset, size_t length); +void vhd_unix_close(void *arg); + +#endif