diff --git a/BasiliskII/src/Unix/disk_sparsebundle.cpp b/BasiliskII/src/Unix/disk_sparsebundle.cpp new file mode 100644 index 00000000..d4b415d9 --- /dev/null +++ b/BasiliskII/src/Unix/disk_sparsebundle.cpp @@ -0,0 +1,154 @@ +/* + * 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 + +// TODO +// - Factory needs to actually check stuff +// - Add to basilisk build +// - Add to 'make links' + +typedef ssize_t (band_func)(int fd, void *buf, size_t len); + +static ssize_t band_read(int fd, void *buf, size_t len) { + ssize_t err = (fd == -1 ? 0 : ::read(fd, buf, len)); + if (err == -1) + return err; + if (err < len) + memset((char*)buf + err, 0, len - err); + return len; +} + +static ssize_t band_write(int fd, void *buf, size_t len) { + return ::write(fd, buf, len); +} + +struct disk_sparsebundle : disk_generic { + disk_sparsebundle(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(bands), band_cur(-1), band_fd(0) { + } + + virtual ~disk_sparsebundle() { + if (band_fd) + 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(&band_read, buf, offset, length); + } + + virtual size_t write(void *buf, loff_t offset, size_t length) { + return band_do(&band_write, buf, offset, length); + } + +protected: + int token_fd; + bool read_only; + loff_t band_size, total_size; + char *band_dir; + + loff_t band_cur; + int band_fd; + + 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 (!open_band(band)) + break; + + size_t start = offset % band_size; + size_t segment = band_size - start; + if (segment > length) + segment = length; + + if (band_fd != -1 && lseek(band_fd, start, SEEK_SET) == -1) + return done; + ssize_t err = func(band_fd, buf, segment); + if (err > 0) + done += err; + if (err < segment) + break; + + b += segment; + offset += segment; + length -= segment; + ++band; + } + return done; + } + + // Open band index 'band', return false on error + bool open_band(loff_t band) { + if (band_cur == band) + return true; + + char path[PATH_MAX + 1]; + if (snprintf(path, PATH_MAX, "%s/%lx", band_dir, + (unsigned long)band) >= PATH_MAX) { + return false; + } + + int oflags = read_only ? O_RDONLY : (O_RDWR | O_CREAT); + band_fd = open(path, oflags, 0644); + if (band_fd == -1) { + if (read_only) { + band_cur == -1; + return true; + } else { + return false; + } + } + + band_cur = band; + return true; + } +}; + +disk_generic *disk_sparsebundle_factory(const char *path, bool read_only) { + if (strstr(path, ".sparsebundle") == NULL) + return NULL; // FIXME: real test + + // FIXME: actually read these + loff_t band_size = 1048576; + loff_t total_size = 53687091200; + + // FIXME: check for double-mount, writable + int token = 0; + read_only = false; + + char buf[PATH_MAX + 1]; + if (snprintf(buf, PATH_MAX, "%s/%s", path, "bands") >= PATH_MAX) + return NULL; + char *bands = strdup(buf); + + return new disk_sparsebundle(bands, token, read_only, band_size, + total_size); +} diff --git a/BasiliskII/src/Unix/disk_unix.h b/BasiliskII/src/Unix/disk_unix.h new file mode 100644 index 00000000..446d7da1 --- /dev/null +++ b/BasiliskII/src/Unix/disk_unix.h @@ -0,0 +1,40 @@ +/* + * disk_unix.h - Generic disk interface + * + * 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 + */ + +#ifndef DISK_UNIX_H +#define DISK_UNIX_H + +#include "sysdeps.h" + +struct disk_generic { + 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 *(disk_factory)(const char *path, bool read_only); + +extern disk_factory disk_sparsebundle_factory; + +#endif diff --git a/BasiliskII/src/Unix/sys_unix.cpp b/BasiliskII/src/Unix/sys_unix.cpp index b3801149..b7a69ecb 100644 --- a/BasiliskII/src/Unix/sys_unix.cpp +++ b/BasiliskII/src/Unix/sys_unix.cpp @@ -56,6 +56,7 @@ #include "prefs.h" #include "user_strings.h" #include "sys.h" +#include "disk_unix.h" #if defined(BINCUE) #include "bincue_unix.h" @@ -83,6 +84,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) @@ -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; @@ -615,6 +618,18 @@ void *Sys_open(const char *name, bool read_only) return fh; } #endif + + // FIXME + disk_generic *generic = disk_sparsebundle_factory(name, read_only); + if (generic) { + 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; + } int open_flags = (read_only ? O_RDONLY : O_RDWR); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__) @@ -727,6 +742,8 @@ void Sys_close(void *arg) 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); @@ -759,6 +776,9 @@ size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length) 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; @@ -784,6 +804,9 @@ size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length) 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) return 0; @@ -813,6 +836,9 @@ loff_t SysGetFileSize(void *arg) return fh->file_size; #endif + if (fh->generic_disk) + return fh->file_size; + if (fh->is_file) return fh->file_size; else { @@ -950,6 +976,9 @@ bool SysIsFixedDisk(void *arg) if (fh->is_vhd) return true; #endif + + if (fh->generic_disk) + return true; if (fh->is_file) return true; @@ -975,6 +1004,9 @@ bool SysIsDiskInserted(void *arg) return true; #endif + if (fh->generic_disk) + return true; + if (fh->is_file) { return true; diff --git a/SheepShaver/src/Unix/Makefile.in b/SheepShaver/src/Unix/Makefile.in index f845e24f..e0fd84ff 100644 --- a/SheepShaver/src/Unix/Makefile.in +++ b/SheepShaver/src/Unix/Makefile.in @@ -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 \ 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)