diff --git a/Device/BlockDevice.cpp b/Device/BlockDevice.cpp index c114a00..9418d89 100644 --- a/Device/BlockDevice.cpp +++ b/Device/BlockDevice.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using namespace Device; @@ -26,6 +27,30 @@ using ProFUSE::Exception; using ProFUSE::POSIXException; +unsigned BlockDevice::ImageType(MappedFile *f, unsigned defv) +{ +#undef __METHOD__ +#define __METHOD__ "BlockDevice::ImageType" + + + if (UniversalDiskImage::Validate(f, std::nothrow)) + return '2IMG'; + + if (DiskCopy42Image::Validate(f, std::nothrow)) + return 'DC42'; + + if (SDKImage::Validate(f, std::nothrow)) + return 'SDK_'; + + if (ProDOSOrderDiskImage::Validate(f, std::nothrow)) + return 'PO__'; + + if (DOSOrderDiskImage::Validate(f, std::nothrow)) + return 'DO__'; + + return defv; + +} unsigned BlockDevice::ImageType(const char *type, unsigned defv) { @@ -51,7 +76,9 @@ unsigned BlockDevice::ImageType(const char *type, unsigned defv) return '2IMG'; if (::strcasecmp(type, "2img") == 0) return '2IMG'; - + + if (::strcasecmp(type, "dc") == 0) + return 'DC42'; if (::strcasecmp(type, "dc42") == 0) return 'DC42'; @@ -74,6 +101,8 @@ unsigned BlockDevice::ImageType(const char *type, unsigned defv) // not supported yet. if (::strcasecmp(type, "sdk") == 0) return 'SDK_'; + if (::strcasecmp(type, "shk") == 0) + return 'SDK_'; return defv; } @@ -91,23 +120,20 @@ BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, un { throw POSIXException(__METHOD__ ": stat error", errno); } + + // /dev/xxx ignore the type. + if (S_ISBLK(st.st_mode)) + return RawDevice::Open(name, flags); + + MappedFile file(name, flags); + if (!imageType) { - // /dev/xxxx - if (S_ISBLK(st.st_mode)) - return RawDevice::Open(name, flags); - - - imageType = ImageType(name, 'PO__'); + imageType = ImageType(&file, 'PO__'); } - - // TODO -- if no image type, guess based on file size? - - MappedFile file(name, flags); - - + switch (imageType) { case '2IMG': @@ -125,6 +151,10 @@ BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, un case 'DVX_': return DavexDiskImage::Open(&file); + case 'SDK_': + return SDKImage::Open(name); + + } // throw an error? diff --git a/Device/BlockDevice.h b/Device/BlockDevice.h index c047912..4b31e71 100644 --- a/Device/BlockDevice.h +++ b/Device/BlockDevice.h @@ -12,6 +12,8 @@ #include +class MappedFile; + namespace Device { class BlockDevice : public ENABLE_SHARED_FROM_THIS(BlockDevice) { @@ -20,7 +22,9 @@ public: // static methods. static unsigned ImageType(const char *type, unsigned defv = 0); - + static unsigned ImageType(MappedFile *, unsigned defv = 0); + + static BlockDevicePointer Open(const char *name, File::FileFlags flags, unsigned imageType = 0); static BlockDevicePointer Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 0); diff --git a/Device/DavexDiskImage.cpp b/Device/DavexDiskImage.cpp index 51270bc..f9e44ab 100644 --- a/Device/DavexDiskImage.cpp +++ b/Device/DavexDiskImage.cpp @@ -47,42 +47,48 @@ DavexDiskImage::~DavexDiskImage() // scan and update usedBlocks? } -void DavexDiskImage::Validate(MappedFile *f) +bool DavexDiskImage::Validate(MappedFile *f, const std::nothrow_t &) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Validate" + + size_t size = f->length(); + const void * data = f->address(); + unsigned blocks = (size / 512) - 1; + + + if (size < 512) return false; + if (size % 512) return false; + + // identity. + if (std::memcmp(data, IdentityCheck, 16)) + return false; + + // file format. + if (Read8(data, 0x10) != 0) + return false; + + // total blocks + if (Read32(data, 33) != blocks) + return false; + + // file number -- must be 1 + if (Read8(data, 64) != 1) + return false; + + return true; +} + +bool DavexDiskImage::Validate(MappedFile *f) { #undef __METHOD__ #define __METHOD__ "DavexDiskImage::Validate" - size_t size = f->length(); - const void * data = f->address(); - bool ok = false; - unsigned blocks = (size / 512) - 1; - - do { - if (size < 512) break; - if (size % 512) break; - - // identity. - if (std::memcmp(data, IdentityCheck, 16)) - break; - - // file format. - if (Read8(data, 0x10) != 0) - break; - - // total blocks - if (Read32(data, 33) != blocks) - break; - - // file number -- must be 1 - if (Read8(data, 64) != 1) - break; - - ok = true; - } while (false); - - - if (!ok) + + if (!Validate(f, std::nothrow)) throw Exception(__METHOD__ ": Invalid file format."); + + return true; } BlockDevicePointer DavexDiskImage::Open(MappedFile *file) diff --git a/Device/DavexDiskImage.h b/Device/DavexDiskImage.h index b7a3d36..0c25f04 100644 --- a/Device/DavexDiskImage.h +++ b/Device/DavexDiskImage.h @@ -2,6 +2,7 @@ #define __DAVEXDISKIMAGE_H__ #include +#include #include #include @@ -22,12 +23,15 @@ public: virtual BlockCachePointer createBlockCache(); + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + private: DavexDiskImage(); DavexDiskImage(MappedFile *); - static void Validate(MappedFile *); + bool _changed; std::string _volumeName; diff --git a/Device/DiskCopy42Image.cpp b/Device/DiskCopy42Image.cpp index 7c0956c..9498720 100644 --- a/Device/DiskCopy42Image.cpp +++ b/Device/DiskCopy42Image.cpp @@ -170,41 +170,38 @@ BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, cons return MAKE_SHARED(DiskCopy42Image, file); } -void DiskCopy42Image::Validate(MappedFile *file) +bool DiskCopy42Image::Validate(MappedFile *file, const std::nothrow_t &) { #undef __METHOD__ #define __METHOD__ "DiskCopy42Image::Validate" - + + size_t bytes = 0; size_t size = file->length(); const void *data = file->address(); - bool ok = false; uint32_t checksum = 0; - do { - if (size < oUserData) break; - - // name must be < 64 - if (Read8(data, 0) > 63) break; - - if (Read16(data, oPrivate) != 0x100) - break; - - // bytes, not blocks. - bytes = Read32(data, oDataSize); - - if (bytes % 512) break; - - if (size < oUserData + bytes) break; - - // todo -- checksum. - checksum = Read32(data, oDataChecksum); - - ok = true; - } while (false); + if (size < oUserData) + return false; - if (!ok) - throw Exception(__METHOD__ ": Invalid file format."); + // name must be < 64 + if (Read8(data, 0) > 63) + return false; + + if (Read16(data, oPrivate) != 0x100) + return false; + + // bytes, not blocks. + bytes = Read32(data, oDataSize); + + if (bytes % 512) + return false; + + if (size < oUserData + bytes) + return false; + + // todo -- checksum. + checksum = Read32(data, oDataChecksum); uint32_t cs = Checksum(oUserData + (uint8_t *)data, bytes); @@ -212,7 +209,21 @@ void DiskCopy42Image::Validate(MappedFile *file) { fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n"); } + + return true; +} + +bool DiskCopy42Image::Validate(MappedFile *file) +{ +#undef __METHOD__ +#define __METHOD__ "DiskCopy42Image::Validate" + + + if (!Validate(file, std::nothrow)) + throw Exception(__METHOD__ ": Invalid file format."); + + return true; } void DiskCopy42Image::write(unsigned block, const void *bp) diff --git a/Device/DiskCopy42Image.h b/Device/DiskCopy42Image.h index df5c95f..a165b1a 100644 --- a/Device/DiskCopy42Image.h +++ b/Device/DiskCopy42Image.h @@ -19,6 +19,11 @@ public: static uint32_t Checksum(void *data, size_t size); + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + + + virtual void write(unsigned block, const void *bp); @@ -29,7 +34,6 @@ private: DiskCopy42Image(); DiskCopy42Image(MappedFile *); - static void Validate(MappedFile *); bool _changed; }; diff --git a/Device/DiskImage.cpp b/Device/DiskImage.cpp index 6025e5d..a1c15b9 100644 --- a/Device/DiskImage.cpp +++ b/Device/DiskImage.cpp @@ -126,18 +126,33 @@ BlockDevicePointer ProDOSOrderDiskImage::Open(MappedFile *file) return MAKE_SHARED(ProDOSOrderDiskImage, file); } -void ProDOSOrderDiskImage::Validate(MappedFile *f) + +bool ProDOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &) +{ +#undef __METHOD__ +#define __METHOD__ "ProDOSOrderDiskImage::Validate" + + + size_t size = f->length(); + + if (size % 512) + return false; + + return true; + +} + +bool ProDOSOrderDiskImage::Validate(MappedFile *f) { #undef __METHOD__ #define __METHOD__ "ProDOSOrderDiskImage::Validate" if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set."); - - size_t size = f->length(); - - if (size % 512) + + if (!Validate(f, std::nothrow)) throw Exception(__METHOD__ ": Invalid file format."); + return true; } BlockCachePointer ProDOSOrderDiskImage::createBlockCache() @@ -157,6 +172,8 @@ DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) : } */ + + DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) : DiskImage(file) { @@ -182,16 +199,30 @@ BlockDevicePointer DOSOrderDiskImage::Open(MappedFile *file) } -void DOSOrderDiskImage::Validate(MappedFile *f) +bool DOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &) +{ +#undef __METHOD__ +#define __METHOD__ "DOSOrderDiskImage::Validate" + + size_t size = f->length(); + + if (size % 512) + return false; + + return true; + +} + +bool DOSOrderDiskImage::Validate(MappedFile *f) { #undef __METHOD__ #define __METHOD__ "DOSOrderDiskImage::Validate" if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set."); - size_t size = f->length(); - if (size % 512) + if (!Validate(f, std::nothrow)) throw Exception(__METHOD__ ": Invalid file format."); + return true; } diff --git a/Device/DiskImage.h b/Device/DiskImage.h index 35034f5..25a5c39 100644 --- a/Device/DiskImage.h +++ b/Device/DiskImage.h @@ -62,13 +62,14 @@ public: virtual BlockCachePointer createBlockCache(); - + + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + private: ProDOSOrderDiskImage(); - ProDOSOrderDiskImage(MappedFile *); - static void Validate(MappedFile *); }; class DOSOrderDiskImage : public DiskImage { @@ -78,11 +79,13 @@ public: static BlockDevicePointer Create(const char *name, size_t blocks); static BlockDevicePointer Open(MappedFile *); + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + private: DOSOrderDiskImage(); DOSOrderDiskImage(MappedFile *); - static void Validate(MappedFile *); }; diff --git a/Device/SDKImage.cpp b/Device/SDKImage.cpp new file mode 100644 index 0000000..4667589 --- /dev/null +++ b/Device/SDKImage.cpp @@ -0,0 +1,249 @@ +// +// SDKImage.cpp +// profuse +// +// Created by Kelvin Sherlock on 3/6/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#include "SDKImage.h" + +#include +#include +#include +#include + +#include + + +#include +#include + +#include + + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + + +class NuFXException : public Exception +{ +public: + + NuFXException(const char *cp, NuError error); + NuFXException(const std::string& string, NuError error); + + virtual const char *errorString(); + +}; + + +inline NuFXException::NuFXException(const char *cp, NuError error) : +Exception(cp, error) +{ +} + +inline NuFXException::NuFXException(const std::string& string, NuError error) : +Exception(string, error) +{ +} + +const char *NuFXException::errorString() +{ + return ::NuStrError((NuError)error()); +} + + +using namespace Device; + +struct record_thread +{ + NuRecordIdx record_index; + NuThreadIdx thread_index; +}; + + +static record_thread FindDiskImageThread(NuArchive *archive) +{ +#undef __METHOD__ +#define __METHOD__ "SDKImage::FindThread" + + record_thread rt; + NuError e; + NuAttr recordCount; + + e = NuGetAttr(archive, kNuAttrNumRecords, &recordCount); + if (e) + { + throw NuFXException(__METHOD__ ": NuGetAttr", e); + } + + for (unsigned position = 0; position < recordCount; ++position) + { + NuRecordIdx rIndex; + const NuRecord *record; + + e = NuGetRecordIdxByPosition(archive, position, &rIndex); + if (e) + { + throw NuFXException(__METHOD__ ": NuGetRecordIdxByPosition", e); + } + + e = NuGetRecord(archive, rIndex, &record); + if (e) + { + throw NuFXException(__METHOD__ ": NuGetRecord", e); + } + + for (unsigned i = 0; i < NuRecordGetNumThreads(record); ++i) + { + const NuThread *thread = NuGetThread(record, i); + + if (NuGetThreadID(thread) == kNuThreadIDDiskImage) + { + rt.thread_index = thread->threadIdx; + rt.record_index = record->recordIdx; + return rt; + } + } + } + + throw Exception(__METHOD__ ": not a disk image"); +} + + + +/* + * helper function to extract SDK image to /tmp and return a + * ProDOSDiskImage of the /tmp file. + * + */ +BlockDevicePointer SDKImage::Open(const char *name) +{ +#undef __METHOD__ +#define __METHOD__ "SDKImage::Open" + + + char tmp[] = "/tmp/pfuse.XXXXXXXX"; + + int fd = -1; + FILE *fp = NULL; + NuArchive *archive = NULL; + //const NuThread *thread = NULL; + //const NuRecord *record = NULL; + NuDataSink *sink = NULL; + //NuRecordIdx rIndex; + //NuThreadIdx tIndex; + + NuError e; + + + record_thread rt = {0, 0}; + + try { + + + e = NuOpenRO(name, &archive); + if (e) + { + throw NuFXException(__METHOD__ ": NuOpenRO", e); + } + + rt = FindDiskImageThread(archive); + + fd = mkstemp(tmp); + if (fd < 0) + { + throw POSIXException(__METHOD__ ": mkstemp", errno); + } + + fp = fdopen(fd, "w"); + if (!fp) + { + ::close(fd); + throw POSIXException(__METHOD__ ": fdopen", errno); + } + + e = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &sink); + if (e) + { + throw NuFXException(__METHOD__ ": NuCreateDataSinkForFP", e); + } + + + e = NuExtractThread(archive, rt.thread_index, sink); + if (e) + { + throw NuFXException(__METHOD__ ": NuExtractThread", e); + } + + fprintf(stderr, "Extracted disk image to %s\n", tmp); + + fclose(fp); + NuClose(archive); + NuFreeDataSink(sink); + fp = NULL; + archive = NULL; + sink = NULL; + } + catch(...) + { + if (fp) fclose(fp); + if (archive) NuClose(archive); + if (sink) NuFreeDataSink(sink); + + throw; + } + + // todo -- maybe SDKImage should extend ProDOSOrderDiskImage, have destructor + // that unklinks the temp file. + + MappedFile file(tmp, File::ReadOnly); + + return ProDOSOrderDiskImage::Open(&file); + +} + + + +bool SDKImage::Validate(MappedFile * f, const std::nothrow_t &) +{ + + // NuFile, alternating ASCII. + static const char IdentityCheck[6] = { 0x4E, 0xF5, 0x46, 0xE9, 0x6C, 0xE5 }; + static const char BXYIdentityCheck[3] = { 0x0A, 0x47, 0x4C }; + + uint8_t *address = (uint8_t *)f->address(); + size_t length = f->length(); + + // check for a BXY header + if (length >= 128 + && std::memcmp(address, BXYIdentityCheck, sizeof(BXYIdentityCheck)) == 0) + { + length -= 128; + address += 128; + } + + + if (length > sizeof(IdentityCheck) + && std::memcmp(address, IdentityCheck, sizeof(IdentityCheck)) == 0) + return true; + + + return false; + +} + +bool SDKImage::Validate(MappedFile * f) +{ +#undef __METHOD__ +#define __METHOD__ "SDKImage::Validate" + + if (!Validate(f, std::nothrow)) + throw Exception(__METHOD__ ": Invalid file format."); + + return true; +} + + + diff --git a/Device/SDKImage.h b/Device/SDKImage.h new file mode 100644 index 0000000..fb51fd2 --- /dev/null +++ b/Device/SDKImage.h @@ -0,0 +1,30 @@ +// +// SDKImage.h +// profuse +// +// Created by Kelvin Sherlock on 3/6/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#include +#include + +namespace Device { + + class SDKImage : public DiskImage + { + public: + + static BlockDevicePointer Open(const char *name); + + + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + + private: + SDKImage(); + SDKImage(const SDKImage &); + ~SDKImage(); + SDKImage & operator=(const SDKImage &); + }; +} \ No newline at end of file diff --git a/Device/UniversalDiskImage.cpp b/Device/UniversalDiskImage.cpp index 96ad4ef..18cec76 100644 --- a/Device/UniversalDiskImage.cpp +++ b/Device/UniversalDiskImage.cpp @@ -100,42 +100,47 @@ BlockDevicePointer UniversalDiskImage::Open(MappedFile *file) * TODO -- honor read-only flag. * */ -void UniversalDiskImage::Validate(MappedFile *file) + +bool UniversalDiskImage::Validate(MappedFile *file, const std::nothrow_t &) { #undef __METHOD__ #define __METHOD__ "UniversalDiskImage::Validate" - + const void *data = file->address(); size_t size = file->length(); - bool ok = false; + unsigned blocks = 0; unsigned offset = 0; - do { - - if (size < 64) break; - - if (std::memcmp(data, "2IMG", 4)) break; - - // only prodos supported, for now... - // TODO -- Dos Order, Nibble support. - if (Read32(data, 0x0c) != 1) break; - - blocks = Read32(data, 0x14); - offset = Read32(data, 0x18); - - // file size == blocks * 512 - if (Read32(data, 0x1c) != blocks * 512) break; - - if (offset + blocks * 512 > size) break; - - ok = true; - } while (false); + + if (size < 64) return false; - if (!ok) + if (std::memcmp(data, "2IMG", 4)) return false; + + // only prodos supported, for now... + // TODO -- Dos Order, Nibble support. + if (Read32(data, 0x0c) != 1) return false; + + blocks = Read32(data, 0x14); + offset = Read32(data, 0x18); + + // file size == blocks * 512 + if (Read32(data, 0x1c) != blocks * 512) return false; + + if (offset + blocks * 512 > size) return false; + + return true; +} + +bool UniversalDiskImage::Validate(MappedFile *file) +{ +#undef __METHOD__ +#define __METHOD__ "UniversalDiskImage::Validate" + + if (!Validate(file, std::nothrow)) throw Exception(__METHOD__ ": Invalid file format."); - + return true; } diff --git a/Device/UniversalDiskImage.h b/Device/UniversalDiskImage.h index 23b5b9d..fc54026 100644 --- a/Device/UniversalDiskImage.h +++ b/Device/UniversalDiskImage.h @@ -21,13 +21,16 @@ public: virtual BlockCachePointer createBlockCache(); + + static bool Validate(MappedFile *, const std::nothrow_t &); + static bool Validate(MappedFile *); + private: UniversalDiskImage(); UniversalDiskImage(MappedFile *); - static void Validate(MappedFile *); uint32_t _format; uint32_t _flags;