Merge pull request #22 from vasi/dmg-sparsebundle

Sparsebundle support
This commit is contained in:
asvitkine 2013-03-11 18:53:54 -07:00
commit 3b177f141f
14 changed files with 4504 additions and 74 deletions

View File

@ -50,7 +50,8 @@ SRCS = ../main.cpp ../prefs.cpp ../prefs_items.cpp \
timer_unix.cpp ../adb.cpp ../serial.cpp ../ether.cpp \
../sony.cpp ../disk.cpp ../cdrom.cpp ../scsi.cpp ../video.cpp \
video_blit.cpp \
vm_alloc.cpp sigsegv.cpp ../audio.cpp ../extfs.cpp \
vm_alloc.cpp sigsegv.cpp ../audio.cpp ../extfs.cpp disk_sparsebundle.cpp \
tinyxml2.cpp \
../user_strings.cpp user_strings_unix.cpp sshpty.c strlcpy.c rpc_unix.cpp \
$(SYSSRCS) $(CPUSRCS) $(SLIRP_SRCS)
APP_FLAVOR ?=

View File

@ -0,0 +1,315 @@
/*
* disk_sparsebundle.cpp - Apple sparse bundle implementation
*
* Basilisk II (C) Dave Vasilevsky
*
* 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 "disk_unix.h"
#include "tinyxml2.h"
#include <errno.h>
#include <limits.h>
#include <algorithm>
#if defined __APPLE__ && defined __MACH__
#define __MACOSX__ 1
#endif
struct disk_sparsebundle : disk_generic {
disk_sparsebundle(const char *bands, int fd, bool read_only,
loff_t band_size, loff_t total_size)
: token_fd(fd), read_only(read_only), band_size(band_size),
total_size(total_size), band_dir(strdup(bands)),
band_cur(-1), band_fd(-1), band_alloc(-1) {
}
virtual ~disk_sparsebundle() {
if (band_fd != -1)
close(band_fd);
close(token_fd);
free(band_dir);
}
virtual bool is_read_only() { return read_only; }
virtual loff_t size() { return total_size; }
virtual size_t read(void *buf, loff_t offset, size_t length) {
return band_do(&disk_sparsebundle::band_read, buf, offset, length);
}
virtual size_t write(void *buf, loff_t offset, size_t length) {
return band_do(&disk_sparsebundle::band_write, buf, offset, length);
}
protected:
int token_fd; // lockfile
bool read_only;
loff_t band_size, total_size;
char *band_dir; // directory containing band files
// Currently open band
loff_t band_cur; // index of the band
int band_fd; // -1 if not open
loff_t band_alloc; // how much space is already used?
typedef ssize_t (disk_sparsebundle::*band_func)(char *buf, loff_t band,
size_t offset, size_t len);
// Split an (offset, length) operation into bands.
size_t band_do(band_func func, void *buf, loff_t offset, size_t length) {
char *b = (char*)buf;
loff_t band = offset / band_size;
size_t done = 0;
while (length) {
if (offset >= total_size)
break;
size_t start = offset % band_size;
size_t segment = std::min((size_t)band_size - start, length);
ssize_t err = (this->*func)(b, band, start, segment);
if (err > 0)
done += err;
if (err < segment)
break;
b += segment;
offset += segment;
length -= segment;
++band;
}
return done;
}
// Open a band by index. It's ok if the band is already open.
enum open_ret {
OPEN_FAILED = 0,
OPEN_NOENT, // Band doesn't exist yet
OPEN_OK,
};
open_ret open_band(loff_t band, bool create) {
if (band_cur == band)
return OPEN_OK;
char path[PATH_MAX + 1];
if (snprintf(path, PATH_MAX, "%s/%lx", band_dir,
(unsigned long)band) >= PATH_MAX) {
return OPEN_FAILED;
}
if (band_fd != -1)
close(band_fd);
band_alloc = -1;
band_cur = -1;
int oflags = read_only ? O_RDONLY : O_RDWR;
if (create)
oflags |= O_CREAT;
band_fd = open(path, oflags, 0644);
if (band_fd == -1) {
return (!create && errno == ENOENT) ? OPEN_NOENT : OPEN_FAILED;
}
// Get the allocated size
if (!read_only) {
band_alloc = lseek(band_fd, 0, SEEK_END);
if (band_alloc == -1)
band_alloc = band_size;
}
band_cur = band;
return OPEN_OK;
}
ssize_t band_read(char *buf, loff_t band, size_t off, size_t len) {
open_ret st = open_band(band, false);
if (st == OPEN_FAILED)
return -1;
// Unallocated bytes
size_t want = (st == OPEN_NOENT || off >= band_alloc) ? 0
: std::min(len, (size_t)band_alloc - off);
if (want) {
if (lseek(band_fd, off, SEEK_SET) == -1)
return -1;
ssize_t err = ::read(band_fd, buf, want);
if (err < want)
return err;
}
memset(buf + want, 0, len - want);
return len;
}
ssize_t band_write(char *buf, loff_t band, size_t off, size_t len) {
// If space is unused, don't needlessly fill it with zeros
// Find min length such that all trailing chars are zero:
size_t nz = len;
for (; nz > 0 && !buf[nz-1]; --nz)
; // pass
open_ret st = open_band(band, nz);
if (st != OPEN_OK)
return st == OPEN_NOENT ? len : -1;
if (lseek(band_fd, off, SEEK_SET) == -1)
return -1;
size_t space = (off >= band_alloc ? 0 : band_alloc - off);
size_t want = std::max(nz, std::min(space, len));
ssize_t err = ::write(band_fd, buf, want);
if (err >= 0)
band_alloc = std::max(band_alloc, loff_t(off + err));
if (err < want)
return err;
return len;
}
};
using tinyxml2::XML_NO_ERROR;
using tinyxml2::XMLElement;
// Simplistic plist parser
struct plist {
plist() : doc(true, tinyxml2::COLLAPSE_WHITESPACE) { }
bool open(const char *path) {
if (doc.LoadFile(path) != XML_NO_ERROR)
return false;
tinyxml2::XMLHandle hnd(&doc);
dict = hnd.FirstChildElement("plist").FirstChildElement("dict")
.ToElement();
return dict;
}
const char *str_val(const char *key) {
return value(key, "string");
}
bool int_val(const char *key, loff_t *i) {
const char *v = value(key, "integer");
if (!v || !*v)
return false;
char *endp;
long long ll = strtoll(v, &endp, 10);
if (*endp)
return false;
*i = ll;
return true;
}
protected:
tinyxml2::XMLDocument doc;
XMLElement *dict;
const char *value(const char *key, const char *type) {
// Assume it's a flat plist
XMLElement *cur = dict->FirstChildElement();
bool found_key = false;
while (cur) {
if (found_key) {
if (strcmp(cur->Name(), type) != 0)
return NULL;
return cur->GetText();
}
found_key = strcmp(cur->Name(), "key") == 0
&& strcmp(cur->GetText(), key) == 0;
cur = cur->NextSiblingElement();
}
return NULL;
}
};
static int try_open(const char *path, bool read_only, bool *locked) {
int oflags = (read_only ? O_RDONLY : O_RDWR);
int lockflags = 0;
#if defined(__MACOSX__)
lockflags = O_NONBLOCK | (read_only ? O_SHLOCK : O_EXLOCK);
#endif
int fd = open(path, oflags | lockflags);
#if defined(__MACOSX__)
if (fd == -1) {
if (errno == EOPNOTSUPP) { // no locking support, try again
fd = open(path, oflags);
} else if (errno == EAGAIN) { // locked
*locked = true;
}
}
#endif
return fd;
}
disk_generic::status disk_sparsebundle_factory(const char *path,
bool read_only, disk_generic **disk) {
// Does it look like a sparsebundle?
char buf[PATH_MAX + 1];
if (snprintf(buf, PATH_MAX, "%s/%s", path, "Info.plist") >= PATH_MAX)
return disk_generic::DISK_UNKNOWN;
plist pl;
if (!pl.open(buf))
return disk_generic::DISK_UNKNOWN;
const char *type;
if (!(type = pl.str_val("diskimage-bundle-type")))
return disk_generic::DISK_UNKNOWN;
if (strcmp(type, "com.apple.diskimage.sparsebundle") != 0)
return disk_generic::DISK_UNKNOWN;
// Find the sparsebundle parameters
loff_t version, band_size, total_size;
if (!pl.int_val("bundle-backingstore-version", &version) || version != 1) {
fprintf(stderr, "sparsebundle: Bad version\n");
return disk_generic::DISK_UNKNOWN;
}
if (!pl.int_val("band-size", &band_size)
|| !pl.int_val("size", &total_size)) {
fprintf(stderr, "sparsebundle: Can't find size\n");
return disk_generic::DISK_INVALID;
}
// Check if we can open it
if (snprintf(buf, PATH_MAX, "%s/%s", path, "token") >= PATH_MAX)
return disk_generic::DISK_INVALID;
bool locked = false;
int token = try_open(buf, read_only, &locked);
if (token == -1 && !read_only) { // try again, read-only
token = try_open(buf, true, &locked);
if (token != -1 && !read_only)
fprintf(stderr, "sparsebundle: Can only mount read-only\n");
read_only = true;
}
if (token == -1) {
if (locked)
fprintf(stderr, "sparsebundle: Refusing to double-mount\n");
else
perror("sparsebundle: open failed:");
return disk_generic::DISK_INVALID;
}
// We're good to go!
if (snprintf(buf, PATH_MAX, "%s/%s", path, "bands") >= PATH_MAX)
return disk_generic::DISK_INVALID;
*disk = new disk_sparsebundle(buf, token, read_only, band_size,
total_size);
return disk_generic::DISK_VALID;
}

View File

@ -1,7 +1,7 @@
/*
* vhd_unix.h -- support for disk images in vhd format
* disk_unix.h - Generic disk interface
*
* (C) 2010 Geoffrey Brown
* Basilisk II (C) Dave Vasilevsky
*
* 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
@ -18,12 +18,31 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef VHD_H
#define VHD_H
#ifndef DISK_UNIX_H
#define DISK_UNIX_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);
#include "sysdeps.h"
struct disk_generic {
enum status {
DISK_UNKNOWN,
DISK_INVALID,
DISK_VALID,
};
disk_generic() { }
virtual ~disk_generic() { };
virtual bool is_read_only() = 0;
virtual size_t read(void *buf, loff_t offset, size_t length) = 0;
virtual size_t write(void *buf, loff_t offset, size_t length) = 0;
virtual loff_t size() = 0;
};
typedef disk_generic::status (disk_factory)(const char *path, bool read_only,
disk_generic **disk);
extern disk_factory disk_sparsebundle_factory;
extern disk_factory disk_vhd_factory;
#endif

View File

@ -56,19 +56,25 @@
#include "prefs.h"
#include "user_strings.h"
#include "sys.h"
#include "disk_unix.h"
#if defined(BINCUE)
#include "bincue_unix.h"
#endif
#if defined(HAVE_LIBVHD)
#include "vhd_unix.h"
#endif
#define DEBUG 0
#include "debug.h"
static disk_factory *disk_factories[] = {
disk_sparsebundle_factory,
#if defined(HAVE_LIBVHD)
disk_vhd_factory,
#endif
NULL
};
// File handles are pointers to these structures
struct mac_file_handle {
char *name; // Copy of device/file name
@ -83,6 +89,7 @@ struct mac_file_handle {
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
disk_generic *generic_disk;
#if defined(__linux__)
int cdrom_cap; // CD-ROM capability flags (only valid if is_cdrom is true)
@ -97,11 +104,6 @@ struct mac_file_handle {
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
@ -525,6 +527,7 @@ static mac_file_handle *open_filehandle(const char *name)
memset(fh, 0, sizeof(mac_file_handle));
fh->name = strdup(name);
fh->fd = -1;
fh->generic_disk = NULL;
#if defined __MACOSX__
fh->ioctl_fd = -1;
fh->ioctl_name = NULL;
@ -600,21 +603,22 @@ void *Sys_open(const char *name, bool read_only)
#endif
#if defined(HAVE_LIBVHD)
int vhdsize;
void *vhdfd = vhd_unix_open(name, &vhdsize, read_only);
if (vhdfd) {
mac_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_mac_file_handle(fh);
return fh;
for (int i = 0; disk_factories[i]; ++i) {
disk_factory *f = disk_factories[i];
disk_generic *generic;
disk_generic::status st = f(name, read_only, &generic);
if (st == disk_generic::DISK_INVALID)
return NULL;
if (st == disk_generic::DISK_VALID) {
mac_file_handle *fh = open_filehandle(name);
fh->generic_disk = generic;
fh->file_size = generic->size();
fh->read_only = generic->is_read_only();
fh->is_media_present = true;
sys_add_mac_file_handle(fh);
return fh;
}
}
#endif
int open_flags = (read_only ? O_RDONLY : O_RDWR);
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__)
@ -718,15 +722,12 @@ void Sys_close(void *arg)
sys_remove_mac_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);
#endif
if (fh->generic_disk)
delete fh->generic_disk;
if (fh->is_cdrom)
cdrom_close(fh);
@ -754,11 +755,9 @@ size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
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
if (fh->generic_disk)
return fh->generic_disk->read(buffer, offset, length);
// Seek to position
if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
return 0;
@ -779,10 +778,8 @@ 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
if (fh->generic_disk)
return fh->generic_disk->write(buffer, offset, length);
// Seek to position
if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
@ -808,10 +805,8 @@ loff_t SysGetFileSize(void *arg)
return size_bincue(fh->bincue_fd);
#endif
#if defined(HAVE_LIBVHD)
if (fh->is_vhd)
if (fh->generic_disk)
return fh->file_size;
#endif
if (fh->is_file)
return fh->file_size;
@ -946,10 +941,8 @@ bool SysIsFixedDisk(void *arg)
if (!fh)
return true;
#if defined(HAVE_LIBVHD)
if (fh->is_vhd)
if (fh->generic_disk)
return true;
#endif
if (fh->is_file)
return true;
@ -970,11 +963,9 @@ bool SysIsDiskInserted(void *arg)
if (!fh)
return false;
#if defined(HAVE_LIBVHD)
if (fh->is_vhd)
if (fh->generic_disk)
return true;
#endif
if (fh->is_file) {
return true;

2095
BasiliskII/src/Unix/tinyxml2.cpp Executable file

File diff suppressed because it is too large Load Diff

1968
BasiliskII/src/Unix/tinyxml2.h Executable file

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
*/
#include "sysdeps.h"
#include "vhd_unix.h"
#include "disk_unix.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -32,7 +32,8 @@ extern "C" {
#define DEBUG 0
#include "debug.h"
void *vhd_unix_open(const char *name, int *size, bool read_only)
static disk_generic::status vhd_unix_open(const char *name, int *size,
bool read_only, vhd_context_t **ctx)
{
int amode = read_only ? R_OK : (R_OK | W_OK);
int fid;
@ -42,12 +43,12 @@ void *vhd_unix_open(const char *name, int *size, bool read_only)
if (access(name, amode)) {
D(bug("vhd open -- incorrect permissions %s\n", name));
return NULL;
return disk_generic::DISK_UNKNOWN;
}
if (! (fid = open(name, O_RDONLY))) {
D(bug("vhd open -- couldn't open file %s\n", name));
return NULL;
return disk_generic::DISK_UNKNOWN;
}
else {
char buf[9];
@ -56,7 +57,7 @@ void *vhd_unix_open(const char *name, int *size, bool read_only)
close(fid);
if (strcmp("conectix", buf) != 0) {
D(bug("vhd open -- not vhd magic = %s\n", buf));
return NULL;
return disk_generic::DISK_UNKNOWN;
}
if (vhd = (vhd_context_t *) malloc(sizeof(vhd_context_t))) {
int err;
@ -64,24 +65,25 @@ void *vhd_unix_open(const char *name, int *size, bool read_only)
VHD_OPEN_RDONLY : VHD_OPEN_RDWR)) {
D(bug("vhd_open failed (%d)\n", err));
free(vhd);
return NULL;
return disk_generic::DISK_INVALID;
}
else {
*size = (int) vhd->footer.curr_size;
printf("VHD Open %s\n", name);
return (void *) vhd;
*ctx = vhd;
return disk_generic::DISK_VALID;
}
}
else {
D(bug("vhd open -- malloc failed\n"));
return NULL;
return disk_generic::DISK_INVALID;
}
}
}
int vhd_unix_read(void *arg, void *buffer, loff_t offset, size_t length)
static int vhd_unix_read(vhd_context_t *ctx, 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",
@ -97,10 +99,10 @@ int vhd_unix_read(void *arg, void *buffer, loff_t offset, size_t length)
return length;
}
int vhd_unix_write(void *arg, void *buffer, loff_t offset, size_t length)
static int vhd_unix_write(vhd_context_t *ctx, 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",
@ -116,9 +118,43 @@ int vhd_unix_write(void *arg, void *buffer, loff_t offset, size_t length)
return length;
}
void vhd_unix_close(void *arg)
static void vhd_unix_close(vhd_context_t *ctx)
{
D(bug("vhd close\n"));
vhd_close((vhd_context_t *) arg);
free(arg);
vhd_close(ctx);
free(ctx);
}
struct disk_vhd : disk_generic {
disk_vhd(vhd_context_t *ctx, bool read_only, loff_t size)
: ctx(ctx), read_only(read_only), file_size(size) { }
virtual ~disk_vhd() { vhd_unix_close(ctx); }
virtual bool is_read_only() { return read_only; }
virtual loff_t size() { return file_size; }
virtual size_t read(void *buf, loff_t offset, size_t length) {
return vhd_unix_read(ctx, buf, offset, length);
}
virtual size_t write(void *buf, loff_t offset, size_t length) {
return vhd_unix_write(ctx, buf, offset, length);
}
protected:
vhd_context_t *ctx;
bool read_only;
loff_t file_size;
};
disk_generic::status disk_vhd_factory(const char *path,
bool read_only, disk_generic **disk) {
int size;
vhd_context_t *ctx = NULL;
disk_generic::status st = vhd_unix_open(path, &size, read_only, &ctx);
if (st == disk_generic::DISK_VALID)
*disk = new disk_vhd(ctx, read_only, size);
return st;
}

View File

@ -61,7 +61,7 @@ links:
BeOS/serial_beos.cpp BeOS/sys_beos.cpp BeOS/timer_beos.cpp \
BeOS/xpram_beos.cpp BeOS/SheepDriver BeOS/SheepNet \
Unix/audio_oss_esd.cpp Unix/bincue_unix.cpp Unix/bincue_unix.h \
Unix/vhd_unix.cpp Unix/vhd_unix.h \
Unix/vhd_unix.cpp \
Unix/extfs_unix.cpp Unix/serial_unix.cpp \
Unix/sshpty.h Unix/sshpty.c Unix/strlcpy.h Unix/strlcpy.c \
Unix/sys_unix.cpp Unix/timer_unix.cpp Unix/xpram_unix.cpp \
@ -70,7 +70,9 @@ links:
Unix/video_blit.cpp Unix/config.sub Unix/config.guess Unix/m4 \
Unix/keycodes Unix/tunconfig Unix/clip_unix.cpp Unix/Irix/audio_irix.cpp \
Unix/Linux/scsi_linux.cpp Unix/Linux/NetDriver Unix/ether_unix.cpp \
Unix/rpc.h Unix/rpc_unix.cpp Unix/ldscripts Unix/Darwin/mkstandalone \
Unix/rpc.h Unix/rpc_unix.cpp Unix/ldscripts \
Unix/tinyxml2.h Unix/tinyxml2.cpp Unix/disk_unix.h \
Unix/disk_sparsebundle.cpp Unix/Darwin/mkstandalone \
Unix/Darwin/lowmem.c Unix/Darwin/pagezero.c Unix/Darwin/testlmem.sh \
dummy/audio_dummy.cpp dummy/clip_dummy.cpp dummy/serial_dummy.cpp \
dummy/prefs_editor_dummy.cpp dummy/scsi_dummy.cpp SDL slirp \

View File

@ -59,7 +59,7 @@ SRCS = ../main.cpp main_unix.cpp ../prefs.cpp ../prefs_items.cpp prefs_unix.cpp
../macos_util.cpp ../timer.cpp timer_unix.cpp ../xpram.cpp xpram_unix.cpp \
../adb.cpp ../sony.cpp ../disk.cpp ../cdrom.cpp ../scsi.cpp \
../gfxaccel.cpp ../video.cpp video_blit.cpp ../audio.cpp ../ether.cpp ../thunks.cpp \
../serial.cpp ../extfs.cpp \
../serial.cpp ../extfs.cpp disk_sparsebundle.cpp tinyxml2.cpp \
about_window_unix.cpp ../user_strings.cpp user_strings_unix.cpp \
vm_alloc.cpp sigsegv.cpp rpc_unix.cpp \
sshpty.c strlcpy.c $(SYSSRCS) $(CPUSRCS) $(MONSRCS) $(SLIRP_SRCS)

View File

@ -0,0 +1 @@
../../../BasiliskII/src/Unix/disk_sparsebundle.cpp

View File

@ -0,0 +1 @@
../../../BasiliskII/src/Unix/disk_unix.h

View File

@ -0,0 +1 @@
../../../BasiliskII/src/Unix/tinyxml2.cpp

View File

@ -0,0 +1 @@
../../../BasiliskII/src/Unix/tinyxml2.h

View File

@ -1 +0,0 @@
../../../BasiliskII/src/Unix/vhd_unix.h