added SDK support, better validation/sniffing

git-svn-id: https://profuse.googlecode.com/svn/branches/v2@381 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
ksherlock 2011-03-09 05:02:04 +00:00
parent 5f893f78fc
commit 32bb5d79b9
12 changed files with 491 additions and 111 deletions

View File

@ -19,6 +19,7 @@
#include <Device/DiskCopy42Image.h>
#include <Device/DavexDiskImage.h>
#include <Device/RawDevice.h>
#include <Device/SDKImage.h>
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?

View File

@ -12,6 +12,8 @@
#include <File/File.h>
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);

View File

@ -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)

View File

@ -2,6 +2,7 @@
#define __DAVEXDISKIMAGE_H__
#include <string>
#include <new>
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
@ -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;

View File

@ -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)

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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 *);
};

249
Device/SDKImage.cpp Normal file
View File

@ -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 <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#include <NufxLib.h>
#include <File/File.h>
#include <File/MappedFile.h>
#include <ProFUSE/Exception.h>
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;
}

30
Device/SDKImage.h Normal file
View File

@ -0,0 +1,30 @@
//
// SDKImage.h
// profuse
//
// Created by Kelvin Sherlock on 3/6/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
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 &);
};
}

View File

@ -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;
}

View File

@ -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;