1 Commits

Author SHA1 Message Date
ksherlock
996dae131e copy before block cache integration.
git-svn-id: https://profuse.googlecode.com/svn/tags/v2 2010-03-18@203 aa027e90-d47c-11dd-86d7-074df07e0730
2010-03-18 22:38:48 +00:00
102 changed files with 3144 additions and 14611 deletions

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
*.o
o/

View File

@@ -1,11 +0,0 @@
language: cpp
# trusty has gcc 4.8
sudo: required
dist: trusty
compiler:
- clang++
- g++
script: make
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y fuse libfuse-dev

View File

@@ -1,10 +1,11 @@
#include <cstring>
#include <ProDOS/Bitmap.h>
#include <Device/BlockDevice.h>
#include <Cache/BlockCache.h>
#include "Bitmap.h"
#include "BlockDevice.h"
#include "auto.h"
using namespace ProDOS;
using namespace ProFUSE;
// returns # of 1-bits set (0-8)
inline static unsigned popCount(uint8_t x)
@@ -33,25 +34,26 @@ Bitmap::Bitmap(unsigned blocks)
_freeIndex = 0;
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
_bitmap.reserve(bitmapSize);
auto_array<uint8_t> bitmap(new uint8_t[bitmapSize]);
// mark overflow in use, everything else free.
std::memset(bitmap, 0xff, blocks / 8);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
// mark blocks as free..
_bitmap.resize(blocks / 8, 0xff);
// edge case
unsigned tmp = blocks & 0x07;
// edge case...
bitmap[blocks / 8] = ~(0xff >> tmp);
if (blocks & 0x0f)
{
_bitmap.push_back( ~(0xff >> (blocks & 0x0f)) );
}
_bitmap = bitmap.release();
// mark any trailing blocks as in use.
_bitmap.resize(bitmapSize, 0x00);
}
Bitmap::Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks)
Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks)
{
_blocks = blocks;
_freeBlocks = 0;
@@ -62,68 +64,47 @@ Bitmap::Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks)
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
_bitmap.reserve(bitmapSize);
auto_array<uint8_t> bitmap(new uint8_t[bitmapSize]);
// load the full block(s).
for (unsigned i = 0; i < blockSize; ++i)
{
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
_bitmap.insert(_bitmap.end(), buffer, buffer + 512);
cache->release(keyPointer);
keyPointer++;
device->read(keyPointer + i, bitmap + 512 * i);
}
// and any remaining partial block.
// make sure all trailing bits are marked in use.
// edge case
unsigned tmp = blocks & 0x07;
if (blocks & 4095)
bitmap[blocks / 8] &= ~(0xff >> tmp);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
// set _freeBlocks and _freeIndex;
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
{
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
unsigned bits = blocks & 4095;
unsigned bytes = bits / 8;
//for (unsigned i = 0; i < bits / 8; ++i) _bitmap.push_back(buffer[i]);
_bitmap.insert(_bitmap.end(), buffer, buffer + bytes);
// partial...
if (blocks & 0x0f)
_freeBlocks += popCount(bitmap[i]);
}
if (_freeBlocks)
{
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
{
uint8_t tmp = buffer[bytes];
tmp &= ~(0xff >> (blocks & 0x0f));
_bitmap.push_back(tmp);
if (bitmap[i])
{
_freeIndex = i;
break;
}
}
// remainder set to in use.
_bitmap.resize(bitmapSize, 0x00);
}
cache->release(keyPointer);
keyPointer++;
}
// now set _freeBlocks and _freeIndex;
std::vector<uint8_t>::iterator iter;
_freeIndex = -1;
for (iter = _bitmap.begin(); iter != _bitmap.end(); ++iter)
{
_freeBlocks += popCount(*iter);
if (_freeIndex == -1 && *iter)
_freeIndex = std::distance(_bitmap.begin(), iter);
}
_bitmap = bitmap.release();
}
Bitmap::~Bitmap()
{
if (_bitmap) delete []_bitmap;
}

View File

@@ -2,25 +2,20 @@
#define __BITMAP_H__
#include <stdint.h>
#include <vector>
namespace Device
{
class BlockDevice;
class BlockCache;
}
namespace ProFUSE {
namespace ProDOS {
class BlockDevice;
class Bitmap {
public:
Bitmap(unsigned blocks);
Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks);
Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks);
//todo -- constructor by loading fro, block device...
~Bitmap();
int allocBlock();
@@ -33,7 +28,7 @@ public:
unsigned blocks() const { return _blocks; }
unsigned bitmapBlocks() const { return _bitmapBlocks; }
unsigned bitmapSize() const { return _bitmapBlocks * 512; }
const void *bitmap() const { return &_bitmap[0]; }
const void *bitmap() const { return _bitmap; }
private:
@@ -43,7 +38,7 @@ private:
unsigned _blocks;
unsigned _bitmapBlocks;
std::vector<uint8_t> _bitmap;
uint8_t *_bitmap;
};

177
BlockCache.cpp Normal file
View File

@@ -0,0 +1,177 @@
#include "BlockCache.h"
struct FileBlockCache::Entry {
FileBlockCache::Entry *next;
unsigned block;
unsigned count;
bool dirty;
uint8_t buffer[512];
};
FileBlockCache::FileBlockCache(Device *device)
{
_device = device;
// allocate initial buffer.
for (unsigned i = 0; i < 10; ++i)
{
Entry *e = new Entry;
std::memset(e, 0, sizeof(Entry));
_pages.push_back(e);
_unused.push_back(e);
}
}
FileBlockCache::~FileBlockCache()
{
std::vector<Entry *>::iterator iter;
// todo -- check if dirty.
// deallocate everything that was allocated.
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
{
Entrty *e = *iter;
if (e->dirty) sync(e);
delete e;
}
}
uint8_t *FileBlockCache::acquire(unsigned block)
{
Entry *e = findEntry(block);
if (e)
{
if (++e->count == 1)
_unused.remove(e);
return e->buffer;
}
if (_unused.empty())
{
e = new Entry;
_pages.push_back(e);
}
else
{
e = _unused.pop_front();
removeEntry(e);
}
std::memset(e, 0, sizeof(Entry));
unsigned hash = hashFunction(block);
e->block = block;
e->count = 1;
e->dirty = 0;
_device->read(block, e->buffer);
_e->next = _hashMap[hash];
_hashMap[hash] = e;
return e->buffer;
}
void FileBlockCache::release(unsigned block, bool dirty)
{
Entry *e = findEntry(block);
// throw error?
if (!e) return;
if (dirty) e->dirty = true;
if (e->count == 0) return;
if (--e->count == 0)
{
_unused.push_back(e);
}
// sync if dirty??
}
void FileBlockCache::markDirty(unsigned block)
{
Entry *e = findEntry(e);
if (e && e->count) e->dirty = true;
}
Entry *FileBlockCache::findEntry(unsigned block)
{
unsigned hash = hashFunction(block);
Entry *e;
e = _hashMap[hash];
while (e && e->block != block)
{
e = e->next;
}
return e;
}
void FileBlockCache::removeEntry(Entry *e)
{
unsigned hash;
Entry *curr;
Entry *prev;
if (!e) return;
hash = hashFunction(e->block);
curr = _hashMap[hash];
if (curr == e)
{
_hashMap[hash] = e->next;
return;
}
for (;;)
{
prev = curr;
curr = curr->next;
if (!curr) break;
if (e == curr)
{
prev->next = e->next;
return;
}
}
}
unsigned FileBlockCache::hashFunction(unsigned block)
{
return block % HashEntries;
}
void FileBlockCache::sync(Entry *e)
{
if (!e) return;
if (!e->dirty) return;
_device->write(e->block, e->buffer);
e->dirty = false;
}
void FileBlockCache::sync()
{
std::vector<Entry *>::iterator iter;
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
{
Entry *e = *iter;
if (e && e->dirty) sync(e);
}
}

81
BlockCache.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef __BLOCK_CACHE_H__
#define __BLOCK_CACHE__
#include <stdint.h>
#include <vector>
#include <list>
//#include <ext/hash_map>
//typedef std::__gnu_cxx::hash_map hash_map;
namespace ProFUSE {
class BlockCache {
public:
virtual ~BlockCache();
virtual uint8_t *acquire(unsigned block) = 0;
virtual void release(unsigned block, bool dirty = false) = 0;
virtual void markDirty(unsigned block) = 0;
virtual void sync() = 0;
};
class FileBlockCache : public BlockCache {
FileBlockCache(Device *);
virtual ~FileBlockCache();
virtual uint8_t *acquire(unsigned block);
virtual void release(unsigned block, bool dirty = false);
virtual void markDirty(unsigned block);
virtual void sync();
private:
struct Entry;
enum { HashEntries = 23 };
unsigned hashFunction(unsigned);
void removeEntry(Entry *);
Entry *findEntry(unsigned block);
void sync(Entry *);
std::vector<Entry *> _pages;
std::list<Entry *> _unused;
Entry *_hashMap[HashEntries];
Device *_device;
};
class MappedBlockCache : public BlockCache
{
public:
MappedBlockCache(Device *);
virtual ~MappedBlockCache();
virtual uint8_t *acquire(unsigned block);
virtual void release(unsigned block, bool dirty = false);
virtual void markDirty(unsigned block);
virtual void sync();
private:
Device *_device;
};
}
#endif

View File

@@ -1,71 +0,0 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Cache/BlockCache.h>
#include <Device/BlockDevice.h>
#include <Common/Exception.h>
#include <Common/auto.h>
using namespace Device;
BlockCache::BlockCache(BlockDevicePointer device) :
_device(device)
{
_blocks = device->blocks();
_readOnly = device->readOnly();
}
BlockCache::~BlockCache()
{
}
void BlockCache::write(unsigned block, const void *bp)
{
void *address = acquire(block);
std::memcpy(address, bp, 512);
release(block, true);
}
void BlockCache::read(unsigned block, void *bp)
{
void *address = acquire(block);
std::memcpy(bp, address, 512);
release(block, false);
}
BlockCachePointer BlockCache::Create(BlockDevicePointer device)
{
// this just calls the device virtual function to create a cache.
if (!device) return BlockCachePointer();
return device->createBlockCache();
}
void BlockCache::zeroBlock(unsigned block)
{
/*
void *address = acquire(block);
std::memset(address, 0, 512);
release(block, true);
*/
uint8_t buffer[512];
std::memset(buffer, 0, 512);
write(block, buffer);
}

View File

@@ -1,62 +0,0 @@
#ifndef __BLOCKCACHE_H__
#define __BLOCKCACHE_H__
#include <stdint.h>
#include <vector>
#include <Device/Device.h>
class MappedFile;
namespace Device {
enum BlockReleaseFlags {
kBlockDirty = 1,
kBlockCommitNow = 2,
kBlockReuse = 3
};
class BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device);
virtual ~BlockCache();
bool readOnly() { return _readOnly; }
unsigned blocks() { return _blocks; }
BlockDevicePointer device() { return _device; }
virtual void sync() = 0;
virtual void write(unsigned block, const void *bp);
virtual void read(unsigned block, void *bp);
virtual void *acquire(unsigned block) = 0;
virtual void release(unsigned block, int flags) = 0 ;
virtual void markDirty(unsigned block) = 0;
virtual void zeroBlock(unsigned block);
void release(unsigned block) { release(block, 0); }
void release(unsigned block, bool dirty)
{
release(block, dirty ? kBlockDirty : 0);
}
protected:
BlockCache(BlockDevicePointer device);
BlockDevicePointer _device;
private:
unsigned _blocks;
bool _readOnly;
};
} // namespace
#endif

View File

@@ -1,77 +0,0 @@
#ifndef __CONCRETE_BLOCK_CACHE_H__
#define __CONCRETE_BLOCK_CACHE_H__
#include <vector>
#include <Cache/BlockCache.h>
namespace Device {
class ConcreteBlockCache : public BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device, unsigned size = 16);
virtual ~ConcreteBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, int flags);
virtual void markDirty(unsigned block);
// public so make_shared can access it.
ConcreteBlockCache(BlockDevicePointer device, unsigned size);
private:
struct Entry {
unsigned block;
unsigned count;
bool dirty;
struct Entry *next;
struct Entry *prev;
struct Entry *nextHash;
uint8_t buffer[512];
};
typedef std::vector<Entry *>::iterator EntryIter;
enum { HashTableSize = 23 };
std::vector<Entry *>_buffers;
Entry *_hashTable[HashTableSize];
Entry *_first;
Entry *_last;
unsigned hashFunction(unsigned block);
Entry *findEntry(unsigned block);
void removeEntry(unsigned block);
void addEntry(Entry *);
Entry *newEntry(unsigned block);
void pushEntry(Entry *);
void setLast(Entry *);
void setFirst(Entry *);
void incrementCount(Entry *);
void decrementCount(Entry *);
};
}
#endif

View File

@@ -1,139 +0,0 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <cstddef>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Cache/MappedBlockCache.h>
#include <Device/BlockDevice.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
using namespace Device;
BlockCachePointer MappedBlockCache::Create(BlockDevicePointer device, void *data)
{
//return BlockCachePointer(new MappedBlockCache(device, data));
return MAKE_SHARED(MappedBlockCache, device, data);
}
MappedBlockCache::MappedBlockCache(BlockDevicePointer device, void *data) :
BlockCache(device)
{
_data = (uint8_t *)data;
_dirty = false;
}
MappedBlockCache::~MappedBlockCache()
{
if (_dirty) sync();
}
void *MappedBlockCache::acquire(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::load"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
return _data + block * 512;
}
void MappedBlockCache::release(unsigned block, int flags)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::unload"
// kBlockCommitNow implies kBlockDirty.
if (flags & kBlockCommitNow)
{
sync(block);
return;
}
if (flags & kBlockDirty) _dirty = true;
}
void MappedBlockCache::write(unsigned block, const void *vp)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::write"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
_dirty = true;
std::memcpy(_data + block * 512, vp, 512);
}
void MappedBlockCache::zeroBlock(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::zeroBlock"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
_dirty = true;
std::memset(_data + block * 512, 0, 512);
}
// sync everything.
void MappedBlockCache::sync()
{
_device->sync();
_dirty = false;
}
/*
*
* sync an individual page.
*
*/
void MappedBlockCache::sync(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::sync"
int pagesize = ::getpagesize();
void *start = _data + block * 512;
void *end = _data + 512 + block * 512;
start = (void *)((ptrdiff_t)start / pagesize * pagesize);
end = (void *)((ptrdiff_t)end / pagesize * pagesize);
if (::msync(start, pagesize, MS_SYNC) != 0)
throw POSIX::Exception(__METHOD__ ": msync", errno);
if (start != end)
{
if (::msync(end, pagesize, MS_SYNC) != 0)
throw POSIX::Exception(__METHOD__ ": msync", errno);
}
}
void MappedBlockCache::markDirty(unsigned block)
{
_dirty = true;
}

View File

@@ -1,40 +0,0 @@
#ifndef __MAPPED_BLOCK_CACHE_H__
#define __MAPPED_BLOCK_CACHE_H__
#include <Cache/BlockCache.h>
namespace Device {
class MappedBlockCache : public BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device, void *data);
virtual ~MappedBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp);
virtual void zeroBlock(unsigned block);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, int flags);
virtual void markDirty(unsigned block);
// public so make_shared can access it.
MappedBlockCache(BlockDevicePointer device, void *data);
private:
void sync(unsigned block);
uint8_t *_data;
bool _dirty;
};
} // namespace
#endif

View File

@@ -1,44 +0,0 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
Exception::Exception(const char *cp):
_error(0),
_string(cp)
{
}
Exception::Exception(const std::string& string):
_error(0),
_string(string)
{
}
Exception::Exception(const char *cp, int error):
_error(error),
_string(cp)
{
}
Exception::Exception(const std::string& string, int error):
_error(error),
_string(string)
{
}
Exception::~Exception() throw()
{
}
const char *Exception::what() const throw()
{
return _string.c_str();
}
const char *Exception::errorString()
{
return "";
}

View File

@@ -1,36 +0,0 @@
#ifndef __COMMON_EXCEPTION_H__
#define __COMMON_EXCEPTION_H__
#include <string>
#include <exception>
class Exception : public std::exception
{
public:
Exception(const char *cp);
Exception(const std::string &str);
virtual ~Exception() throw ();
virtual const char *what() const throw();
virtual const char *errorString();
int error() const { return _error; }
protected:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
private:
int _error;
std::string _string;
};
#endif

View File

@@ -1,35 +0,0 @@
#ifndef __COMMON_SMART_POINTERS_H__
#define __COMMON_SMART_POINTERS_H__
#if 1
//C++0x
#include <memory>
#define SHARED_PTR(T) std::shared_ptr<T>
#define WEAK_PTR(T) std::weak_ptr<T>
#define MAKE_SHARED(T, ...) std::make_shared<T>(__VA_ARGS__)
#define ENABLE_SHARED_FROM_THIS(T) std::enable_shared_from_this<T>
#define STATIC_POINTER_CAST(T, ARG) std::static_pointer_cast<T>(ARG)
#define DYNAMIC_POINTER_CAST(T, ARG) std::dynamic_pointer_cast<T>(ARG)
#else
// tr1
#include <tr1/memory>
#define SHARED_PTR(T) std::tr1::shared_ptr<T>
#define WEAK_PTR(T) std::tr1::weak_ptr<T>
#define MAKE_SHARED(T, ...) std::tr1::shared_ptr<T>(new T(__VA_ARGS__))
#define ENABLE_SHARED_FROM_THIS(T) std::tr1::enable_shared_from_this<T>
#define STATIC_POINTER_CAST(T, ARG) std::tr1::static_pointer_cast<T>(ARG)
#define DYNAMIC_POINTER_CAST(T, ARG) std::tr1::dynamic_pointer_cast<T>(ARG)
#endif
#endif

View File

@@ -1,14 +0,0 @@
#ifndef __COMMON_UNORDERED_MAP_H__
#define __COMMON_UNORDERED_MAP_H__
#if 1
//c++0x
#include <unordered_map>
#define UNORDERED_MAP(...) std::unordered_map(__VA_ARGS__)
#else
// tr1
#include <tr1/unordered_map>
#define UNORDERED_MAP(...) std::tr1::unordered_map(__VA_ARGS__)
#endif
#endif

98
DataWriter.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include "DataWriter.h"
#include "Endian.h"
#include <cstring>
using namespace ProFUSE;
DataWriter::DataWriter(unsigned size)
{
_size = size;
_release = true;
_offset = 0;
_buffer = new uint8_t[size];
}
DataWriter::DataWriter(unsigned size, void *buffer)
{
_size = size;
_buffer = (uint8_t *)buffer;
_release = false;
_offset = 0;
}
DataWriter::~DataWriter()
{
if (_release && _buffer) delete[] _buffer;
}
void DataWriter::write8(uint8_t data)
{
_buffer[_offset] = data;
_offset += 1;
}
void DataWriter::write(const void *data, unsigned size)
{
std::memcpy(pointer(), data, size);
_offset += size;
}
DataWriterLE::DataWriterLE(unsigned size) :
DataWriter(size)
{}
DataWriterLE::DataWriterLE(unsigned size, void *buffer) :
DataWriter(size, buffer)
{}
void DataWriterLE::write16(uint16_t data)
{
LittleEndian::Write16(pointer(), data);
_offset += 2;
}
void DataWriterLE::write24(uint32_t data)
{
LittleEndian::Write24(pointer(), data);
_offset += 3;
}
void DataWriterLE::write32(uint32_t data)
{
LittleEndian::Write32(pointer(), data);
_offset += 4;
}
DataWriterBE::DataWriterBE(unsigned size) :
DataWriter(size)
{}
DataWriterBE::DataWriterBE(unsigned size, void *buffer) :
DataWriter(size, buffer)
{}
void DataWriterBE::write16(uint16_t data)
{
BigEndian::Write16(pointer(), data);
_offset += 2;
}
void DataWriterBE::write24(uint32_t data)
{
BigEndian::Write24(pointer(), data);
_offset += 3;
}
void DataWriterBE::write32(uint32_t data)
{
BigEndian::Write32(pointer(), data);
_offset += 4;
}

72
DataWriter.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef __DATAWRITER_H__
#define __DATAWRITER_H__
#include <stdint.h>
namespace ProFUSE {
class DataWriter {
public:
DataWriter(unsigned size);
DataWriter(unsigned size, void *data);
virtual ~DataWriter();
void write8(uint8_t);
virtual void write16(uint16_t) = 0;
virtual void write24(uint32_t) = 0;
virtual void write32(uint32_t) = 0;
void write(const void *data, unsigned size);
void setOffset(unsigned o) { _offset = o; }
unsigned offset() const { return _offset; }
void forward(unsigned count) { _offset += count; }
void rewind(unsigned count) { _offset -= count; }
void *data() const { return _buffer; }
unsigned size() const { return _size; }
protected:
uint8_t *pointer() const { return _offset + _buffer; }
bool _release;
unsigned _size;
unsigned _offset;
uint8_t *_buffer;
};
class DataWriterLE : public DataWriter {
public:
DataWriterLE(unsigned);
DataWriterLE(unsigned, void *);
virtual void write8(uint8_t);
virtual void write16(uint16_t);
virtual void write24(uint32_t);
virtual void write32(uint32_t);
};
class DataWriterBE : public DataWriter {
public:
DataWriterBE(unsigned);
DataWriterBE(unsigned, void *);
virtual void write8(uint8_t);
virtual void write16(uint16_t);
virtual void write24(uint32_t);
virtual void write32(uint32_t);
};
}
#endif

View File

@@ -1,486 +0,0 @@
#include <cstring>
#include <cstdio>
#include <Device/Adaptor.h>
#include <Common/Exception.h>
using namespace Device;
Adaptor::~Adaptor()
{
}
POAdaptor::POAdaptor(void *address)
{
_address = (uint8_t *)address;
}
void POAdaptor::readBlock(unsigned block, void *bp)
{
std::memcpy(bp, _address + block * 512, 512);
}
void POAdaptor::writeBlock(unsigned block, const void *bp)
{
std::memcpy(_address + block * 512, bp, 512);
}
unsigned DOAdaptor::Map[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
DOAdaptor::DOAdaptor(void *address)
{
_address = (uint8_t *)address;
}
void DOAdaptor::readBlock(unsigned block, void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t offset = track | (Map[sector+i] << 8);
std::memcpy(bp, _address + offset, 256);
bp = (uint8_t *)bp + 256;
}
}
void DOAdaptor::writeBlock(unsigned block, const void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t offset = track | (Map[sector+i] << 8);
std::memcpy(_address + offset, bp, 256);
bp = (uint8_t *)bp + 256;
}
}
#pragma mark -
#pragma mark NibbleAdaptor
class CircleBuffer {
public:
CircleBuffer(void *address, unsigned length)
{
_address = (uint8_t *)address;
_length = length;
}
uint8_t operator[](unsigned i) const
{
if (i >= _length) i %= _length;
return _address[i];
}
uint8_t& operator[](unsigned i)
{
if (i >= _length) i %= _length;
return _address[i];
}
private:
uint8_t *_address;
unsigned _length;
};
uint8_t NibbleAdaptor::decode44(uint8_t x, uint8_t y)
{
return ((x << 1) | 0x01) & y;
}
std::pair<uint8_t, uint8_t> NibbleAdaptor::encode44(uint8_t val)
{
uint8_t x = (val >> 1) | 0xaa;
uint8_t y = val | 0xaa;
return std::make_pair(x,y);
}
uint8_t NibbleAdaptor::encode62(uint8_t val)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::encode62"
static uint8_t table[64] = {
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
if (val > 0x3f)
throw ::Exception(__METHOD__ ": Invalid 6-2 value.");
return table[val];
}
uint8_t NibbleAdaptor::decode62(uint8_t val)
{
#undef __METHOD__
#define __METHOD__ "decode62"
// auto-generated via perl.
static uint8_t table[] = {
-1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, 3, -1, 4, 5, 6,
-1, -1, -1, -1, -1, -1, 7, 8, -1, -1, -1, 9, 10, 11, 12, 13,
-1, -1, 14, 15, 16, 17, 18, 19, -1, 20, 21, 22, 23, 24, 25, 26,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, 28, 29, 30,
-1, -1, -1, 31, -1, -1, 32, 33, -1, 34, 35, 36, 37, 38, 39, 40,
-1, -1, -1, -1, -1, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50,
-1, -1, 51, 52, 53, 54, 55, 56, -1, 57, 58, 59, 60, 61, 62, 63
};
if ((val < 0x90) || (table[val - 0x90] == 0xff))
throw ::Exception(__METHOD__ ": Invalid 6-2 encoding.");
return table[val - 0x90];
}
static int FindByte(void *address, uint8_t c, unsigned length, unsigned offset = 0)
{
for (unsigned i = offset; i < length; ++i)
{
if ( ((uint8_t *)address)[i] == c) return i;
}
return -1;
}
/*
* Address Field:
* prologue volume track sector checksum epilogue
* D5 AA 96 XX YY XX YY XX YY XX YY DE AA EB
*/
/*
* Data Field:
* prologue user data checksum epilogue
* D5 AA AD [6+2 encoded] XX DE AA EB
*/
NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::NibbleAdaptor"
_address = (uint8_t *)address;
_length = length;
// build a map of track/sectors.
unsigned state = 0;
_index.resize(35 * 16, -1);
int offset = 0;
unsigned track = 0;
unsigned sector = 0;
unsigned volume = 0;
unsigned checksum = 0;
CircleBuffer buffer(_address, _length);
for (;;)
{
offset = FindByte(address, 0xd5, length, offset);
if (offset < 0) break;
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0x96 && buffer[offset + 11] == 0xde && buffer[offset + 12] == 0xaa)
{
volume = decode44(buffer[offset + 3], buffer[offset + 4]);
track = decode44(buffer[offset + 5], buffer[offset + 6]);
sector = decode44(buffer[offset + 7], buffer[offset + 8]);
checksum = decode44(buffer[offset + 9], buffer[offset + 10]);
if (volume ^ track ^ sector ^ checksum)
throw ::Exception(__METHOD__ ": Invalid address checksum.");
if (track > 35 || sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
offset += 3 + 8 + 3;
state = 1;
continue;
}
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad && state == 1)
{
if (_index[track * 16 + sector] != -1)
{
std::fprintf(stderr, "track %u sector %u duplicated.\n", track, sector);
}
_index[track * 16 + sector] = (offset + 3) % _length;
//offset += 3 + 342 + 1 + 3;
offset++;
state = 0;
continue;
}
offset++; //???
// ????
}
// possible wraparound.
if (state == 1)
{
offset = FindByte(address, 0xd5, length, 0);
if (offset >= 0)
{
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad)
{
_index[track * 16 + sector] = (offset + 3) % _length;
}
}
}
// now check _index for offset = -1, which means the sector/track wasn't found.
for (std::vector<unsigned>::iterator iter = _index.begin(); iter != _index.end(); ++iter)
{
if (*iter == -1)
{
int offset = distance(_index.begin(), iter);
std::fprintf(stderr, "Error: track %u sector %u missing.\n", offset / 16, offset % 16);
//throw ::Exception(__METHOD__ ": Sector missing.");
}
}
}
NibbleAdaptor::~NibbleAdaptor()
{
}
void NibbleAdaptor::readBlock(unsigned block, void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned b = (block & 0x07) << 1;
/*
* block sectors
* 0 0, 2
* 1 4, 6
* 2 8, 10
* 3 12,14
* 4 1, 3
* 5 5, 7
* 6 9, 11
* 7 13, 15
*/
unsigned sector = b >> 2;
if (sector >= 16) sector -= 15;
readTrackSector(TrackSector(track, sector), bp);
readTrackSector(TrackSector(track, sector + 1), (uint8_t *)bp + 256);
}
void NibbleAdaptor::writeBlock(unsigned block, const void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned b = (block & 0x07) << 1;
/*
* block sectors
* 0 0, 2
* 1 4, 6
* 2 8, 10
* 3 12,14
* 4 1, 3
* 5 5, 7
* 6 9, 11
* 7 13, 15
*/
unsigned sector = b >> 2;
if (sector >= 16) sector -= 15;
writeTrackSector(TrackSector(track, sector), bp);
writeTrackSector(TrackSector(track, sector + 1), (const uint8_t *)bp + 256);
}
void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::readTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
CircleBuffer buffer(_address, _length);
uint8_t bits[86 * 3];
uint8_t checksum = 0;
unsigned offset = _index[ts.track * 16 + ts.sector];
if (offset == -1)
{
throw ::Exception(__METHOD__ ": Missing track/sector.");
}
// first 86 bytes are in the auxbuffer, backwards.
unsigned index = offset;
for (unsigned i = 0; i < 86; ++i)
{
uint8_t x = buffer[index++];
x = decode62(x);
checksum ^= x;
uint8_t y = checksum;
/*
for (unsigned j = 0; j < 3; ++j)
{
//bits[i + j * 86] = ((y & 0x01) << 1) | ((y & 0x02) >> 1);
bits[i + j * 86] = "\x00\x01\x02\x03"[y & 0x03];
y >>= 2;
}
*/
bits[i + 86 * 0] = "\x00\x02\x01\x03"[y & 0x03];
bits[i + 86 * 1] = "\x00\x02\x01\x03"[(y >> 2) & 0x03];
bits[i + 86 * 2] = "\x00\x02\x01\x03"[(y >> 4) & 0x03];
}
for (unsigned i = 0; i < 256; ++i)
{
uint8_t x = buffer[index++];
x = decode62(x);
checksum ^= x;
uint8_t y = (checksum << 2) | bits[i];
((uint8_t *)bp)[i] = y;
}
if (checksum != decode62(buffer[index++]))
std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector);
//throw ::Exception(__METHOD__ ": Invalid field checksum.");
}
void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::writeTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
uint8_t auxBuffer[86];
uint8_t checksum = 0;
// create the aux buffer.
std::memset(auxBuffer, 0, sizeof(auxBuffer));
for (unsigned i = 0, j = 0, shift = 0; i < 256; ++i)
{
uint8_t x = ((const uint8_t *)bp)[i];
// grab the bottom 2 bytes and reverse them.
//uint8_t y = ((x & 0x01) << 1) | ((x & 0x02) >> 1);
uint8_t y = "\x00\x02\x01\x03"[x & 0x03];
auxBuffer[j++] |= (y << shift);
if (j == 86)
{
j = 0;
shift += 2;
}
}
unsigned offset = _index[ts.track * 16 + ts.sector];
CircleBuffer buffer(_address, _length);
// create the checksum while writing to disk..
// aux buffer
for (unsigned i = 0; i < 86; ++i)
{
uint8_t x = auxBuffer[i];
buffer[offset + i] = encode62(x ^ checksum);
checksum = x;
}
for (unsigned i = 0; i < 256; ++i)
{
uint8_t x = ((const uint8_t *)bp)[i];
x >>= 2;
buffer[offset + 86 + i] = encode62(x ^ checksum);
checksum = x;
}
buffer[offset + 342] = encode62(checksum);
}

View File

@@ -1,81 +0,0 @@
#ifndef __DEVICE_ADAPTOR_H__
#define __DEVICE_ADAPTOR_H__
#include <utility>
#include <vector>
#include <stdint.h>
#include <Device/TrackSector.h>
namespace Device {
class Adaptor
{
public:
virtual ~Adaptor();
virtual void readBlock(unsigned block, void *bp) = 0;
virtual void writeBlock(unsigned block, const void *bp) = 0;
};
class POAdaptor : public Adaptor
{
public:
POAdaptor(void *address);
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
private:
uint8_t *_address;
};
class DOAdaptor : public Adaptor
{
public:
DOAdaptor(void *address);
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
static unsigned Map[];
private:
uint8_t *_address;
};
// TODO -- nibble adaptor.
class NibbleAdaptor : public Adaptor
{
public:
NibbleAdaptor(void *address, unsigned length);
virtual ~NibbleAdaptor();
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
virtual void readTrackSector(TrackSector ts, void *bp);
virtual void writeTrackSector(TrackSector ts, const void *bp);
static std::pair<uint8_t, uint8_t>encode44(uint8_t);
static uint8_t decode44(uint8_t, uint8_t);
static uint8_t encode62(uint8_t);
static uint8_t decode62(uint8_t);
private:
uint8_t *_address;
unsigned _length;
std::vector<unsigned> _index;
};
}
#endif

View File

@@ -1,17 +1,16 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Device/BlockDevice.h>
#include <Cache/ConcreteBlockCache.h>
#include <Device/BlockCache.h>
#include <Common/Exception.h>
#include <Common/auto.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
/*
@@ -51,20 +50,91 @@
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#pragma mark -
#pragma mark BlockCache
//typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
BlockCachePointer ConcreteBlockCache::Create(BlockDevicePointer device, unsigned size)
BlockCache::~BlockCache(BlockDevice *device)
{
//return BlockCachePointer(new ConcreteBlockCache(device, size));
// constructor must be accessible to std::make_shared...
return MAKE_SHARED(ConcreteBlockCache, device, size);
_device = _device;
_blocks = device->blocks();
_readOnly = device->readOnly();
}
ConcreteBlockCache::ConcreteBlockCache(BlockDevicePointer device, unsigned size) :
BlockCache::~BlockCache()
{
delete _device;
}
void BlockCache::write(unsigned block, const void *bp)
{
void *address = acquire(block);
std::memcpy(address, bp, 512);
release(block, true);
}
void BlockCache::read(unsigned block, void *bp)
{
void *address = acquire(block);
std::memcpy(bp, address, 512);
release(block, false);
}
#pragma mark -
#pragma mark MappedBlockCache
MappedBlockCache::MappedBlockCache(void *data, unsigned blocks)
{
_blocks = blocks;
_data = (uint8_t *)data;
}
void MappedBlockCache::write()
{
// TODO...
}
void *MappedBlockCache::load(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::load"
if (block >= _blocks)
throw Exception(__METHOD__ ": Invalid block.");
return _data + block * 512;
}
void MappedBlockCache::unload(unsigned block, bool dirty)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::unload"
if (!dirty) return;
// msync must be page-size aligned.
unsigned pagesize = ::getpagesize();
unsigned offset = block * 512;
void *address = _data + offset / pagesize * pagesize;
unsigned length = offset % pagesize + 512;
if (::msync(address, length, MS_ASYNC) < 0)
{
throw POSIXException(__METHOD__ ": msync failed.", errno);
}
}
#pragma mark -
#pragma mark ConcreteBlockCache
typedef std::vector<BlockCache::Entry *>::iterator EntryIter;
ConcreteBlockCache::ConcreteBlockCache(BlockDevice *device, unsigned size) :
BlockCache(device)
{
if (size < 16) size = 16;
@@ -112,7 +182,7 @@ ConcreteBlockCache::~ConcreteBlockCache()
}
void ConcreteBlockCache::sync()
ConcreteBlockCache::sync()
{
EntryIter iter;
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
@@ -131,7 +201,7 @@ void ConcreteBlockCache::sync()
void ConcreteBlockCache::write(unsigned block, const void *bp)
{
Entry *e = findEntry(block);
FileEntry *e = findEntry();
if (e)
{
@@ -153,8 +223,6 @@ void ConcreteBlockCache::write(unsigned block, const void *bp)
setLast(e);
}
void ConcreteBlockCache::markDirty(unsigned block)
{
Entry *e = findEntry(block);
@@ -164,23 +232,15 @@ void ConcreteBlockCache::markDirty(unsigned block)
}
void ConcreteBlockCache::release(unsigned block, int flags)
void ConcreteBlockCache::release(unsigned block, bool dirty)
{
Entry *e = findEntry(block);
bool dirty = flags & (kBlockDirty | kBlockCommitNow);
if (e)
{
if (dirty) e->dirty = true;
if (dirty) e->dirty = true;
decrementCount(e);
if (flags & kBlockCommitNow)
{
_device->write(block, e->buffer);
e->dirty = false;
}
}
// error otherwise?
}
@@ -210,29 +270,11 @@ unsigned ConcreteBlockCache::hashFunction(unsigned block)
return block % HashTableSize;
}
ConcreteBlockCache::Entry *ConcreteBlockCache::findEntry(unsigned block)
{
Entry *e;
unsigned hash = hashFunction(block);
e = _hashTable[hash];
while ((e) && (e->block != block))
e = e->nextHash;
return e;
}
/*
* remove a block from the hashtable
* and write to dick if dirty.
*/
void ConcreteBlockCache::removeEntry(unsigned block)
void removeEntry(unsigned block)
{
Entry *e;
Entry *prev;
@@ -253,7 +295,7 @@ void ConcreteBlockCache::removeEntry(unsigned block)
for(;;)
{
prev = e;
e = e->nextHash;
e = e->next;
if (!e) break;
if (e->block == block)
@@ -305,11 +347,18 @@ void ConcreteBlockCache::decrementCount(Entry *e)
e->count = e->count - 1;
if (e->count == 0)
{
setLast(e);
if (_last == NULL)
{
_first = _last = e;
return;
}
e->prev = _last;
_last = e;
}
}
ConcreteBlockCache::Entry *ConcreteBlockCache::newEntry(unsigned block)
Entry *ConcreteBlockCache::newEntry(unsigned block)
{
Entry *e;
@@ -362,19 +411,4 @@ void ConcreteBlockCache::setLast(Entry *e)
e->prev = _last;
_last->next = e;
_last = e;
}
void ConcreteBlockCache::setFirst(Entry *e)
{
if (_first == NULL)
{
_first = _last = e;
return;
}
e->next = _first;
_first->prev = e;
_first = e;
}
}

120
Device/BlockCache.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef __BLOCKCACHE_H__
#define __BLOCKCACHE_H__
#include <stdint.h>
#include <vector>
namespace Device {
class BlockDevice;
class MappedFile;
class BlockCache {
public:
BlockCache *Create(BlockDevice *device, unsigned size = 16);
virtual ~BlockCache();
bool readOnly() { return _readOnly; }
unsigned blocks() { return _blocks; }
BlockDevice *device() { return _device; }
virtual void sync() = 0;
virtual void write(unsigned block, const void *bp);
virtual void read(unsigned block, void *bp);
virtual void *acquire(unsigned block) = 0;
virtual void release(unsigned block, bool dirty) = 0;
virtual void markDirty(unsigned block) = 0;
void release(unsigned block) { release(block, false); }
protected:
BlockCache(BlockDevice *device);
BlockDevice *_device;
private
unsigned _blocks;
bool _readOnly;
};
class ConcreteBlockCache : public BlockCache {
public:
ConcreteBlockCache(BlockDevice *device, unsigned size = 16);
virtual ~ConcreteBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp) = 0;
virtual void *acquire(unsigned block);
virtual void release(unsigned block, bool dirty);
virtual void markDirty(unsigned block);
private:
struct Entry {
unsigned block;
unsigned count;
bool dirty;
struct Entry *next;
struct Entry *prev;
struct Entry *nextHash;
uint8_t buffer[512];
};
enum { HashTableSize = 23 };
std::vector<Entry *>_buffers;
Entry *_hashTable[HashTableSize];
Entry *_first;
Entry *_last;
unsigned hashFunction(unsigned block);
Entry *findEntry(unsigned block);
void removeEntry(unsigned block);
void addEntry(Entry *);
Entry *newEntry(unsigned block);
void pushEntry(Entry *);
void setLast(Entry *);
incrementCount(Entry *);
decrementCount(Entry *);
};
class MappedBlockCache : public BlockCache {
public:
MappedBlockCache(BlockDevice *, void *data);
virtual ~MappedBlockCache();
virtual void sync() = 0;
virtual void write(unsigned block, const void *vp);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, bool dirty);
virtual void markDirty(unsigned block);
private:
void *_data;
};
} // namespace
#endif

View File

@@ -6,241 +6,30 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <Device/BlockDevice.h>
#include <Cache/ConcreteBlockCache.h>
#include <Device/BlockCache.h>
#include <Device/MappedFile.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <ProFUSE/Exception.h>
#include <Device/DiskImage.h>
#include <Device/UniversalDiskImage.h>
#include <Device/DiskCopy42Image.h>
#include <Device/DavexDiskImage.h>
#include <Device/RawDevice.h>
#ifdef HAVE_NUFX
#include <Device/SDKImage.h>
#endif
using namespace Device;
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';
#ifdef HAVE_NUFX
if (SDKImage::Validate(f, std::nothrow))
return 'SDK_';
#endif
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)
{
const char *tmp;
if (type == 0 || *type == 0) return defv;
// type could be a path, eg images/file, disk.images/file
// unix-specifix.
// basename alters the input string
tmp = std::strrchr(type, '/');
if (tmp) type = tmp + 1;
// type could be a filename, in which case we check the extension.
tmp = std::strrchr(type, '.');
if (tmp) type = tmp + 1;
if (*type == 0) return defv;
if (::strcasecmp(type, "2mg") == 0)
return '2IMG';
if (::strcasecmp(type, "2img") == 0)
return '2IMG';
if (::strcasecmp(type, "dc") == 0)
return 'DC42';
if (::strcasecmp(type, "dc42") == 0)
return 'DC42';
if (::strcasecmp(type, "po") == 0)
return 'PO__';
if (::strcasecmp(type, "dmg") == 0)
return 'PO__';
if (::strcasecmp(type, "dsk") == 0)
return 'DO__';
if (::strcasecmp(type, "do") == 0)
return 'DO__';
if (::strcasecmp(type, "dvx") == 0)
return 'DVX_';
if (::strcasecmp(type, "davex") == 0)
return 'DVX_';
#ifdef HAVE_NUFX
if (::strcasecmp(type, "sdk") == 0)
return 'SDK_';
if (::strcasecmp(type, "shk") == 0)
return 'SDK_';
#endif
return defv;
}
BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType)
{
#undef __METHOD__
#define __METHOD__ "BlockDevice::Open"
struct stat st;
std::memset(&st, 0, sizeof(st));
if (::stat(name, &st) != 0)
{
throw POSIX::Exception(__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)
{
imageType = ImageType(&file, 'PO__');
}
switch (imageType)
{
case '2IMG':
return UniversalDiskImage::Open(&file);
case 'DC42':
return DiskCopy42Image::Open(&file);
case 'DO__':
return DOSOrderDiskImage::Open(&file);
case 'PO__':
return ProDOSOrderDiskImage::Open(&file);
case 'DVX_':
return DavexDiskImage::Open(&file);
#if HAVE_NUFX
case 'SDK_':
return SDKImage::Open(name);
#endif
}
// throw an error?
return BlockDevicePointer();
}
// return the basename, without an extension.
static std::string filename(const std::string& src)
{
unsigned start;
unsigned end;
if (src.empty()) return std::string("");
start = end = 0;
for(unsigned i = 0, l = src.length(); i < l; ++i)
{
char c = src[i];
if (c == '/') start = end = i + 1;
if (c == '.') end = i;
}
if (start == src.length()) return std::string("");
if (start == end) return src.substr(start);
return src.substr(start, end - start);
}
BlockDevicePointer BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
{
std::string xname;
if (!imageType) imageType = ImageType(fname, 'PO__');
if (vname == NULL)
{
xname = filename(std::string(fname));
vname = xname.c_str();
}
switch(imageType)
{
case '2IMG':
return UniversalDiskImage::Create(fname, blocks);
case 'DC42':
return DiskCopy42Image::Create(fname, blocks, vname);
case 'DO__':
return DOSOrderDiskImage::Create(fname, blocks);
case 'PO__':
return ProDOSOrderDiskImage::Create(fname, blocks);
case 'DVX_':
return DavexDiskImage::Create(fname, blocks, vname);
}
return BlockDevicePointer();
}
#pragma mark -
#pragma mark BlockDevice
BlockDevice::BlockDevice()
{
_cache = NULL;
}
BlockDevice::~BlockDevice()
{
delete _cache;
}
void BlockDevice::zeroBlock(unsigned block)
@@ -251,7 +40,19 @@ void BlockDevice::zeroBlock(unsigned block)
write(block, bp);
}
AbstractBlockCache *BlockDevice::blockCache()
{
if (!_cache)
{
_cache = createBlockCache();
}
return _cache;
}
AbstractBlockCache *BlockDevice::createBlockCache()
{
return new BlockCache(this);
}
bool BlockDevice::mapped()
{
@@ -263,16 +64,226 @@ void BlockDevice::sync(unsigned block)
sync();
}
/*
void BlockDevice::sync(TrackSector ts)
{
sync();
}
*/
BlockCachePointer BlockDevice::createBlockCache()
void *BlockDevice::read(unsigned block)
{
unsigned b = blocks();
unsigned size = std::max(16u, b / 16);
return ConcreteBlockCache::Create(shared_from_this(), size);
return NULL;
}
void *BlockDevice::read(TrackSector ts)
{
return NULL;
}
#pragma mark -
#pragma mark DiskImage
unsigned DiskImage::ImageType(const char *type, unsigned defv)
{
const char *tmp;
if (type == 0 || *type == 0) return defv;
// type could be a filename, in which case we check the extension.
tmp = std::strrchr(type, '.');
if (tmp) type = tmp + 1;
if (*type == 0) return defv;
if (::strcasecmp(type, "2mg") == 0)
return '2IMG';
if (::strcasecmp(type, "2img") == 0)
return '2IMG';
if (::strcasecmp(type, "dc42") == 0)
return 'DC42';
if (::strcasecmp(type, "po") == 0)
return 'PO__';
if (::strcasecmp(type, "dmg") == 0)
return 'PO__';
if (::strcasecmp(type, "dsk") == 0)
return 'DO__';
if (::strcasecmp(type, "do") == 0)
return 'DO__';
if (::strcasecmp(type, "davex") == 0)
return 'DVX_';
/*
// not supported yet.
if (::strcasecmp(tmp, "sdk") == 0)
return 'SDK_';
*/
return defv;
}
DiskImage::DiskImage(const char *name, bool readOnly) :
_file(NULL)
{
_file = new MappedFile(name, readOnly);
}
DiskImage::DiskImage(MappedFile *file) :
_file(file)
{
}
DiskImage::~DiskImage()
{
delete _file;
}
bool DiskImage::readOnly()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::readOnly"
if (_file) return _file->readOnly();
throw Exception(__METHOD__ ": File not set.");
}
unsigned DiskImage::blocks()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::blocks"
if (_file) return _file->blocks();
throw Exception(__METHOD__ ": File not set.");
}
void DiskImage::read(unsigned block, void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::read"
if (_file) return _file->readBlock(block, bp);
throw Exception(__METHOD__ ": File not set.");
}
void DiskImage::write(unsigned block, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::write"
if (_file) return _file->writeBlock(block, bp);
throw Exception(__METHOD__ ": File not set.");
}
void DiskImage::sync()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::sync"
if (_file) return _file->sync();
throw Exception(__METHOD__ ": File not set.");
}
AbstractBlockCache *DiskImage::createBlockCache()
{
if (_file->encoding() == MappedFile::ProDOSOrder)
return new MappedBlockCache(_file->imageData(), _file->blocks());
return BlockDevice::createBlockCache();
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = new MappedFile(name, blocks * 512);
file->setBlocks(blocks);
return new ProDOSOrderDiskImage(file);
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new ProDOSOrderDiskImage(file);
}
void ProDOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
if (!f) throw Exception(__METHOD__ ": File not set.");
size_t size = f->fileSize();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
f->reset();
f->setBlocks(size / 512);
}
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
}
DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = new MappedFile(name, blocks * 512);
file->setEncoding(MappedFile::DOSOrder);
file->setBlocks(blocks);
return new DOSOrderDiskImage(file);
}
DOSOrderDiskImage *DOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new DOSOrderDiskImage(file);
}
void DOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DOSOrderDiskImage::Validate"
if (!f) throw Exception(__METHOD__ ": File not set.");
size_t size = f->fileSize();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
f->reset();
f->setEncoding(MappedFile::DOSOrder);
f->setBlocks(size / 512);
}

View File

@@ -4,45 +4,31 @@
#include <stdint.h>
#include <sys/types.h>
#include <Device/Device.h>
#include <ProFUSE/Exception.h>
#include <Device/TrackSector.h>
#include <Common/Exception.h>
#include <Common/smart_pointers.h>
#include <File/File.h>
class MappedFile;
namespace Device {
class BlockDevice : public ENABLE_SHARED_FROM_THIS(BlockDevice) {
class MappedFile;
class AbstractBlockCache;
class BlockDevice {
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);
virtual ~BlockDevice();
virtual BlockCachePointer createBlockCache();
virtual void read(unsigned block, void *bp) = 0;
//virtual void read(TrackSector ts, void *bp) = 0
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp) = 0;
//virtual void write(TrackSector ts, const void *bp) = 0;
virtual void write(TrackSector ts, const void *bp);
// direct access to mapped memory (not always possible).
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
virtual unsigned blocks() = 0;
@@ -52,18 +38,82 @@ public:
virtual void sync() = 0;
virtual void sync(unsigned block);
//virtual void sync(TrackSector ts);
virtual void sync(TrackSector ts);
void zeroBlock(unsigned block);
AbstractBlockCache *blockCache();
protected:
BlockDevice();
virtual AbstractBlockCache *createBlockCache();
private:
AbstractBlockCache *_cache;
};
class DiskImage : public BlockDevice {
public:
static unsigned ImageType(const char *type, unsigned defv = 0);
virtual ~DiskImage();
virtual void read(unsigned block, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void sync();
virtual bool readOnly();
virtual unsigned blocks();
protected:
virtual AbstractBlockCache *createBlockCache();
DiskImage(MappedFile * = 0);
DiskImage(const char *name, bool readOnly);
MappedFile *file() { return _file; }
void setFile(MappedFile *);
private:
MappedFile *_file;
};
class ProDOSOrderDiskImage : public DiskImage {
public:
ProDOSOrderDiskImage(const char *name, bool readOnly);
static ProDOSOrderDiskImage *Create(const char *name, size_t blocks);
static ProDOSOrderDiskImage *Open(MappedFile *);
private:
ProDOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
};
class DOSOrderDiskImage : public DiskImage {
public:
DOSOrderDiskImage(const char *name, bool readOnly);
static DOSOrderDiskImage *Create(const char *name, size_t blocks);
static DOSOrderDiskImage *Open(MappedFile *);
private:
DOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
};
}
#endif
#endif

View File

@@ -8,17 +8,17 @@
#include <algorithm>
#include <Device/DavexDiskImage.h>
#include <File/MappedFile.h>
#include <Device/Adaptor.h>
#include <Device/MappedFile.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Cache/MappedBlockCache.h>
using namespace Device;
using namespace LittleEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
/*
http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004
@@ -26,17 +26,16 @@ using namespace LittleEndian;
static const char *IdentityCheck = "\x60VSTORE [Davex]\x00";
DavexDiskImage::DavexDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
// private, validation already performed.
DavexDiskImage::DavexDiskImage(MappedFile *file) :
DiskImage(file)
DavexDiskImage::DavexDiskImage(MappedFile *f) :
DiskImage(f)
{
// at this point, file is no longer valid.
// 512-bytes header
setBlocks((length() / 512) - 1);
setAdaptor(new POAdaptor(512 + (uint8_t *)address()));
}
@@ -45,66 +44,62 @@ DavexDiskImage::~DavexDiskImage()
// scan and update usedBlocks?
}
bool DavexDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
void DavexDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Validate"
size_t size = f->length();
const void * data = f->address();
size_t size = f->fileSize();
const void * data = f->fileData();
bool ok = false;
unsigned blocks = (size / 512) - 1;
if (size < 512) return false;
if (size % 512) return false;
do {
if (size < 512) break;
if (size % 512) break;
// identity.
if (std::memcmp(data, IdentityCheck, 16))
break;
// identity.
if (std::memcmp(data, IdentityCheck, 16))
return false;
// 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);
// file format.
if (Read8(data, 0x10) != 0)
return false;
// total blocks
if (Read32(data, 33) != blocks)
return false;
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
// file number -- must be 1
if (Read8(data, 64) != 1)
return false;
return true;
f->reset();
f->setBlocks(blocks);
f->setOffset(512);
}
bool DavexDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Validate"
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}
BlockDevicePointer DavexDiskImage::Open(MappedFile *file)
DavexDiskImage *DavexDiskImage::Open(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Open"
Validate(file);
//return BlockDevicePointer(new DavexDiskImage(file));
return MAKE_SHARED(DavexDiskImage, file);
return new DavexDiskImage(file);
}
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks)
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks)
{
return Create(name, blocks, "Untitled");
}
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Create"
@@ -114,9 +109,9 @@ BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks, const
IOBuffer header(tmp,512);
MappedFile *file = MappedFile::Create(name, blocks * 512 + 512);
MappedFile *file = new MappedFile(name, blocks * 512 + 512);
data = (uint8_t *)file->address();
data = (uint8_t *)file->fileData();
header.writeBytes(IdentityCheck, 16);
// file Format
@@ -157,18 +152,10 @@ BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks, const
header.setOffset(512, true);
std::memcpy(file->address(), header.buffer(), 512);
std::memcpy(file->fileData(), header.buffer(), 512);
file->sync();
//return BlockDevicePointer(new DavexDiskImage(file));
return MAKE_SHARED(DavexDiskImage, file);
}
BlockCachePointer DavexDiskImage::createBlockCache()
{
// need a smart pointer, but only have this....
return MappedBlockCache::Create(shared_from_this(), 512 + (uint8_t *)address());
file->setOffset(512);
file->setBlocks(blocks);
return new DavexDiskImage(file);
}

View File

@@ -2,11 +2,8 @@
#define __DAVEXDISKIMAGE_H__
#include <string>
#include <new>
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
namespace Device {
@@ -15,21 +12,18 @@ namespace Device {
class DavexDiskImage : public DiskImage {
public:
DavexDiskImage(const char *, bool readOnly);
virtual ~DavexDiskImage();
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
static BlockDevicePointer Open(MappedFile *);
static DavexDiskImage *Create(const char *name, size_t blocks);
static DavexDiskImage *Create(const char *name, size_t blocks, const char *vname);
static DavexDiskImage *Open(MappedFile *);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
private:
DavexDiskImage(MappedFile *);
private:
DavexDiskImage();
static void Validate(MappedFile *);
bool _changed;
std::string _volumeName;

View File

@@ -1,24 +0,0 @@
//
// Device.h
// profuse
//
// Created by Kelvin Sherlock on 2/19/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#ifndef __DEVICE_DEVICE_H__
#define __DEVICE_DEVICE_H__
#include <Common/smart_pointers.h>
namespace Device {
class BlockDevice;
class BlockCache;
typedef SHARED_PTR(BlockDevice) BlockDevicePointer;
typedef SHARED_PTR(BlockCache) BlockCachePointer;
}
#endif

204
Device/DeviceReader.cpp Normal file
View File

@@ -0,0 +1,204 @@
#include <Device/DeviceReader.h>
using namespace Device;
DeviceReader::~DeviceReader()
{
}
void *DeviceReader::read(unsigned block)
{
return NULL;
}
void DeviceReader::read(TrackSector ts)
{
return NULL;
}
void DeviceReader::mapped()
{
return false;
}
#pragma mark -
#pragma mark ProDOS Order
void ProDOSOrderDeviceReader::read(unsigned block, void *bp)
{
std::memcpy(bp, read(block), 512);
}
void ProDOSOrderDeviceReader::read(TrackSector ts, void *bp)
{
std::memcpy(bp, read(ts), 256);
}
void *ProDOSOrderDeviceReader::read(unsigned block)
{
if (block > _blocks)
{
throw ProFUSE::Exception("Invalid block.");
}
return block * 512 + (uint8_t *)_data;
}
void *ProDOSOrderDeviceReader::read(TrackSector ts)
{
unsigned block = (ts.track * 16 + ts.sector) / 2;
if (block > _blocks)
{
throw ProFUSE::Exception("Invalid track/sector.");
}
return (ts.track * 16 + ts.sector) * 256 + (uint8_t *)_data;
}
void ProDOSOrderDeviceReader::write(unsigned block, const void *bp)
{
std::memcpy(read(block), bp, 512);
}
void ProDOSOrderDeviceReader::write(TrackSector ts, const void *bp)
{
std::memcpy(read(ts), bp, 256);
}
#pragma mark -
#pragma mark DOS Order
const unsigned DOSMap[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
void *DOSOrderDeviceReader::read(TrackSector ts)
{
if (ts.track > _tracks || ts.sector > 16)
{
throw ProFUSE::Exception("Invalid track/sector.");
}
return (ts.track * 16 + ts.sector) * 256 + (uint8_t *)_data;
}
void DOSOrderDeviceReader::read(unsigned block, void *bp)
{
TrackSector ts(block >> 3, 0);
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
ts.sector = DOSMap[sector];
std::memcpy(bp, read(ts), 256);
bp = 256 + (uint8_t *)bp;
++sector;
}
}
void DOSOrderDeviceReader::read(TrackSector ts, void *bp)
{
std::memcpy(bp, read(ts), 256);
}
void DOSOrderDeviceReader::write(unsigned block, const void *bp)
{
TrackSector ts(block >> 3, 0);
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
ts.sector = DOSMap[sector];
std::memcpy(read(ts), bp, 256);
bp = 256 + (const uint8_t *)bp;
++sector;
}
}
void DOSOrderDeviceReader::write(TrackSector ts, const void *bp)
{
std::memcpy(read(ts), bp, 256);
}
#pragma mark -
#pragma mark FileDeviceReader
FileDeviceReader::FileDeviceReader(int fd, unsigned blocks, bool readOnly)
{
_fd = fd;
_readOnly = readOnly;
_blocks = blocks;
}
bool FileDeviceReader::readOnly()
{
return _readOnly;
}
void FileDeviceReader::write(unsigned block, const void *bp)
{
off_t offset = block * 512;
size_t ok = ::pwrite(_fd, bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
void FileDeviceReader::write(TrackSector ts, const void *bp)
{
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pwrite(_fd, bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
void FileDeviceReader::read(unsigned block, void *bp)
{
off_t offset = block * 512;
size_t ok = ::pread(_fd, bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}
void FileDeviceReader::read(TrackSector ts, void *bp)
{
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pread(_fd, bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}

109
Device/DeviceReader.h Normal file
View File

@@ -0,0 +1,109 @@
#ifndef __DEVICEREADER_H__
#define __DEVICEREADER_H__
#include <Device/TrackSector.h>
namespace Device {
class DeviceReader {
public:
virtual ~DeviceReader();
virtual void read(unsigned block, void *bp) = 0;
virtual void read(TrackSector ts, void *bp) = 0;
virtual void write(unsigned block, const void *bp) = 0;
virtual void write(TrackSector ts, const void *bp) = 0;
// direct access -- not always available.
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
virtual bool readOnly() = 0;
virtual bool mapped();
};
class FileDeviceReader : public DeviceReader {
// does not assume ownership of fd.
FileDeviceReader(int fd, unsigned blocks, bool readOnly);
//virtual ~FileDeviceReader();
public:
virtual bool readOnly();
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
private:
int _fd;
unsigned _blocks;
bool _readOnly;
}
class MappedFileDeviceReader : public DeviceReader {
protected:
MappedFileDeviceReader(MappedFile *f, unsigned offset);
void *_data;
private:
MappedFile *_file
};
class ProDOSOrderDeviceReader : public MappedFileDeviceReader {
public:
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
private:
unsigned blocks;
};
// 16 sectors only.
class DOSOrderDeviceReader : public MappedFileDeviceReader {
public:
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
virtual void *read(TrackSector ts);
private:
unsigned _tracks;
};
class NibbleDeviceReader : public MappedFileDeviceReader {
private:
std::vector<unsigned> _map;
};
}
#endif

View File

@@ -5,49 +5,44 @@
#include <algorithm>
#include <Device/DiskCopy42Image.h>
#include <Device/MappedFile.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Cache/MappedBlockCache.h>
using namespace Device;
using namespace BigEndian;
enum {
oDataSize = 64,
oDataChecksum = 72,
oPrivate = 82,
oUserData = 84
};
using ProFUSE::Exception;
using ProFUSE::POSIXException;
DiskCopy42Image::DiskCopy42Image(MappedFile *f) :
DiskImage(f),
_changed(false)
{
setAdaptor(new POAdaptor(oUserData + (uint8_t *)address()));
setBlocks(Read32(address(), oDataSize) / 512);
}
DiskCopy42Image::DiskCopy42Image(const char *name, bool readOnly) :
DiskImage(name, readOnly),
_changed(false)
{
Validate(file());
}
DiskCopy42Image::~DiskCopy42Image()
{
if (_changed)
{
MappedFile *f = file();
void *data = f->fileData();
if (f)
{
void *data = f->address();
uint32_t cs = Checksum(oUserData + (uint8_t *)data, Read32(data, oDataSize));
uint32_t cs = Checksum(f->offset() + (uint8_t *)data,
f->blocks() * 512);
Write32(data, oDataChecksum, cs);
Write32(data, 72, cs);
f->sync();
}
// TODO -- checksum
@@ -72,12 +67,10 @@ uint32_t DiskCopy42Image::Checksum(void *data, size_t size)
return rv;
}
BlockDevicePointer DiskCopy42Image::Open(MappedFile *f)
DiskCopy42Image *DiskCopy42Image::Open(MappedFile *f)
{
Validate(f);
//return BlockDevicePointer(new DiskCopy42Image(f));
return MAKE_SHARED(DiskCopy42Image, f);
return new DiskCopy42Image(f);
}
static uint8_t DiskFormat(size_t blocks)
@@ -100,19 +93,19 @@ static uint8_t FormatByte(size_t blocks)
default: return 0x22;
}
}
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks)
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks)
{
return Create(name, blocks, "Untitled");
}
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
{
MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData);
MappedFile *file = new MappedFile(name, blocks * 512 + 84);
file->setOffset(84);
file->setBlocks(blocks);
uint8_t tmp[oUserData];
IOBuffer header(tmp, oUserData);
uint8_t tmp[84];
IOBuffer header(tmp, 84);
// name -- 64byte pstring.
@@ -122,7 +115,7 @@ BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, cons
header.writeBytes(vname, std::min(l, 63u));
//header.resize(64);
header.setOffset(oDataSize, true);
header.setOffset(64, true);
// data size -- number of bytes
header.write32(blocks * 512);
@@ -159,82 +152,61 @@ BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, cons
// private
header.write16(0x100);
std::memcpy(file->address(), header.buffer(), oUserData);
std::memcpy(file->fileData(), header.buffer(), 84);
file->sync();
//return BlockDevicePointer(new DiskCopy42Image(file));
return MAKE_SHARED(DiskCopy42Image, file);
return new DiskCopy42Image(file);
}
bool DiskCopy42Image::Validate(MappedFile *file, const std::nothrow_t &)
void DiskCopy42Image::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DiskCopy42Image::Validate"
size_t bytes = 0;
size_t size = file->length();
const void *data = file->address();
size_t size = file->fileSize();
const void *data = file->fileData();
bool ok = false;
uint32_t checksum = 0;
if (size < oUserData)
return false;
do {
if (size < 84) break;
// name must be < 64
if (Read8(data, 0) > 63) break;
if (Read32(data, 82) != 0x100)
break;
// bytes, not blocks.
bytes = Read32(data, 64);
if (bytes % 512) break;
if (size < 84 + bytes) break;
// todo -- checksum.
checksum = Read32(data, 72);
ok = true;
} while (false);
// name must be < 64
if (Read8(data, 0) > 63)
return false;
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
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);
uint32_t cs = Checksum(64 + (uint8_t *)data, bytes);
if (cs != checksum)
{
fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n");
fprintf(stderr, "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;
file->reset();
file->setOffset(64);
file->setBlocks(bytes / 512);
}
void DiskCopy42Image::write(unsigned block, const void *bp)
{
DiskImage::write(block, bp);
_changed = true;
}
BlockCachePointer DiskCopy42Image::createBlockCache()
{
// if not readonly, mark changed so crc will be updated at close.
if (!readOnly()) _changed = true;
return MappedBlockCache::Create(shared_from_this(), address());
}

View File

@@ -2,7 +2,6 @@
#define __DISKCOPY42IMAGE_H__
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
#include <stdint.h>
@@ -10,31 +9,23 @@ namespace Device {
class DiskCopy42Image : public DiskImage {
public:
DiskCopy42Image(const char *name, bool readOnly);
virtual ~DiskCopy42Image();
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
static DiskCopy42Image *Create(const char *name, size_t blocks);
static DiskCopy42Image *Create(const char *name, size_t blocks, const char *vname);
static BlockDevicePointer Open(MappedFile *);
static DiskCopy42Image *Open(MappedFile *);
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);
virtual BlockCachePointer createBlockCache();
DiskCopy42Image();
DiskCopy42Image(MappedFile *);
private:
DiskCopy42Image(MappedFile *);
static void Validate(MappedFile *);
bool _changed;
};

View File

@@ -1,226 +0,0 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <Device/DiskImage.h>
#include <File/MappedFile.h>
#include <Cache/MappedBlockCache.h>
#include <Common/Exception.h>
using namespace Device;
/*
DiskImage::DiskImage(const char *name, bool readOnly)
{
File fd(name, readOnly ? O_RDONLY : O_RDWR);
MappedFile mf(fd, readOnly);
_file.adopt(mf);
_blocks = 0;
_readOnly = readOnly;
_adaptor = NULL;
}
*/
DiskImage::DiskImage(MappedFile *file)
{
_file.adopt(*file);
_blocks = 0;
_readOnly = _file.readOnly();
_adaptor = NULL;
}
DiskImage::~DiskImage()
{
delete _adaptor;
}
bool DiskImage::readOnly()
{
return _readOnly;
}
unsigned DiskImage::blocks()
{
return _blocks;
}
void DiskImage::setAdaptor(Adaptor *adaptor)
{
delete _adaptor;
_adaptor = adaptor;
}
void DiskImage::read(unsigned block, void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::read"
if (block >= _blocks)
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->readBlock(block, bp);
}
void DiskImage::write(unsigned block, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::write"
if (block >= _blocks)
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->writeBlock(block, bp);
}
void DiskImage::sync()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::sync"
if (_file.isValid()) return _file.sync();
throw ::Exception(__METHOD__ ": File not set.");
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
setBlocks(length() / 512);
setAdaptor(new POAdaptor(address()));
}
BlockDevicePointer ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
BlockDevicePointer ProDOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
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.");
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}
BlockCachePointer ProDOSOrderDiskImage::createBlockCache()
{
return MappedBlockCache::Create(shared_from_this(), address());
}
#pragma mark -
#pragma mark DOS Order Disk Image
/*
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
*/
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
setBlocks(length() / 512);
setAdaptor(new DOAdaptor(address()));
}
BlockDevicePointer DOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
BlockDevicePointer DOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
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.");
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}

View File

@@ -1,97 +0,0 @@
#ifndef __DISKIMAGE_H__
#define __DISKIMAGE_H__
#include <stdint.h>
#include <sys/types.h>
#include <Device/BlockDevice.h>
#include <Device/Adaptor.h>
#include <File/MappedFile.h>
namespace Device {
class DiskImage : public BlockDevice {
public:
virtual ~DiskImage();
virtual void read(unsigned block, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void sync();
virtual bool readOnly();
virtual unsigned blocks();
protected:
DiskImage();
DiskImage(MappedFile *file = 0);
void setBlocks(unsigned blocks) { _blocks = blocks; }
void setAdaptor(Adaptor *);
void *address() const { return _file.address(); }
size_t length() const { return _file.length(); }
MappedFile *file() { return &_file; }
private:
MappedFile _file;
Adaptor *_adaptor;
bool _readOnly;
unsigned _blocks;
};
class ProDOSOrderDiskImage : public DiskImage {
public:
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
ProDOSOrderDiskImage(MappedFile *);
private:
ProDOSOrderDiskImage();
};
class DOSOrderDiskImage : public DiskImage {
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 *);
DOSOrderDiskImage(MappedFile *);
private:
DOSOrderDiskImage();
};
}
#endif

253
Device/MappedFile.cpp Normal file
View File

@@ -0,0 +1,253 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <Device/MappedFile.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
using namespace Device;
using ProFUSE::POSIXException;
using ProFUSE::Exception;
MappedFile::MappedFile(const char *name, bool readOnly)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
// if unable to open as read/write, open as read-only.
ProFUSE::auto_fd fd;
if (!readOnly)
{
fd.reset(::open(name, O_RDWR));
}
if (fd < 0)
{
fd.reset(::open(name, O_RDONLY));
readOnly = true;
}
if (fd < 0)
{
throw POSIXException(__METHOD__ ": Unable to open file.", errno);
}
// init may throw; auto_fd guarantees the file will be closed if that happens.
init(fd, readOnly);
fd.release();
}
// does NOT close fd on failure.
MappedFile::MappedFile(int fd, bool readOnly)
{
init(fd, readOnly);
}
MappedFile::MappedFile(const char *name, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
_fd = -1;
_map = MAP_FAILED;
_size = size;
_readOnly = false;
_encoding = ProDOSOrder;
ProFUSE::auto_fd fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644));
if (fd < 0)
throw POSIXException(__METHOD__ ": Unable to create file.", errno);
// TODO -- is ftruncate portable?
if (::ftruncate(fd, _size) < 0)
{
throw POSIXException(__METHOD__ ": Unable to truncate file.", errno);
}
//_map = ::mmap(NULL, _size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, _fd, 0);
ProFUSE::auto_map map(
NULL,
_size,
PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED,
fd,
0
);
if (map == MAP_FAILED) throw POSIXException(__METHOD__ ": Unable to map file.", errno);
_fd = fd.release();
_map = map.release();
}
MappedFile::~MappedFile()
{
if (_map != MAP_FAILED) ::munmap(_map, _size);
if (_fd >= 0) ::close(_fd);
}
// does NOT close f on exception.
void MappedFile::init(int f, bool readOnly)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::init"
_fd = -1;
_map = MAP_FAILED;
_offset = 0;
_blocks = 0;
_size = 0;
_readOnly = readOnly;
_encoding = ProDOSOrder;
_size = ::lseek(f, 0, SEEK_END);
if (_size < 0)
throw POSIXException(__METHOD__ ": Unable to determine file size.", errno);
/*
_map = ::mmap(NULL, _size, readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, fd, 0);
*/
::lseek(f, 0, SEEK_SET);
ProFUSE::auto_map map(
NULL,
_size,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, //readOnly ? MAP_FILE : MAP_FILE | MAP_SHARED,
f,
0
);
/*
_map = ::mmap(NULL, _size, readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, f, 0);
*/
if (map == MAP_FAILED) throw POSIXException(__METHOD__ ": Unable to map file.", errno);
_fd = f;
_map = map.release();
}
const unsigned MappedFile::DOSMap[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
void MappedFile::readBlock(unsigned block, void *bp)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::readBlock"
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
switch(_encoding)
{
case ProDOSOrder:
{
size_t address = block * 512;
std::memcpy(bp, (uint8_t *)_map + _offset + address, 512);
}
break;
case DOSOrder:
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t address = track | (DOSMap[sector+i] << 8);
std::memcpy(bp, (uint8_t *)_map + _offset + address, 256);
bp = (uint8_t *)bp + 256;
}
}
break;
default:
throw Exception(__METHOD__ ": Unsupported Encoding.");
}
}
void MappedFile::writeBlock(unsigned block, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::writeBlock"
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (_readOnly) throw Exception(__METHOD__ ": File is readonly.");
switch(_encoding)
{
case ProDOSOrder:
{
size_t address = block * 512;
std::memcpy((uint8_t *)_map + _offset + address , bp, 512);
}
break;
case DOSOrder:
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t address = track | (DOSMap[sector+i] << 8);
std::memcpy((uint8_t *)_map + _offset + address, bp, 256);
bp = (uint8_t *)bp + 256;
}
}
break;
default:
throw Exception(__METHOD__ ": Unsupported Encoding.");
}
}
void MappedFile::sync()
{
#undef __METHOD__
#define __METHOD__ "MappedFile::sync"
if (_readOnly) return;
if (::msync(_map, _size, MS_SYNC) < 0)
throw POSIXException(__METHOD__ ": msync error.", errno);
}
void MappedFile::reset()
{
_offset = 0;
_blocks = 0;
_encoding = ProDOSOrder;
}

70
Device/MappedFile.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef __MAPPED_FILE__
#define __MAPPED_FILE__
#include <stdint.h>
#include <cstdlib>
namespace Device {
class MappedFile {
public:
enum Encoding {
ProDOSOrder = 0,
DOSOrder,
Nibblized62,
Nibblized53
};
MappedFile(const char *name, bool ReadOnly);
MappedFile(int fd, bool readOnly);
MappedFile(const char *name, size_t size);
~MappedFile();
void readBlock(unsigned block, void *bp);
void writeBlock(unsigned block, const void *bp);
void sync();
void reset();
Encoding encoding() const { return _encoding; }
void setEncoding(Encoding e) { _encoding = e; }
unsigned offset() const { return _offset; }
void setOffset(unsigned o) { _offset = o; }
unsigned blocks() const { return _blocks; }
void setBlocks(unsigned b) { _blocks = b; }
bool readOnly() const { return _readOnly; }
size_t fileSize() const { return _size; }
void *fileData() const { return _map; }
void *imageData() const { return _offset + (uint8_t *)_map; }
private:
MappedFile& operator=(const MappedFile& other);
void init(int fd, bool readOnly);
static const unsigned DOSMap[];
int _fd;
void *_map;
size_t _size;
bool _readOnly;
Encoding _encoding;
unsigned _offset;
unsigned _blocks;
};
}
#endif

View File

@@ -12,7 +12,7 @@
#include <sys/disk.h>
#endif
#ifdef __linux__
#ifdef __LINUX__
#include <sys/mount.h>
#endif
@@ -20,22 +20,15 @@
#include <sys/dkio.h>
#endif
#ifdef __FREEBSD__
#include <sys/disk.h>
#endif
#ifdef __minix
#include <minix/partition.h>
#endif
#include <Device/RawDevice.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <ProFUSE/auto.h>
#include <ProFUSE/Exception.h>
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#ifdef __SUN__
void RawDevice::devSize(int fd)
@@ -46,7 +39,7 @@ void RawDevice::devSize(int fd)
struct dk_minfo minfo;
if (::ioctl(fd, DKIOCGMEDIAINFO, &minfo) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
_size = minfo.dki_lbsize * minfo.dki_capacity;
_blockSize = 512; // not really, but whatever.
@@ -65,11 +58,11 @@ void RawDevice::devSize(int fd)
uint64_t blockCount; // 64 bit
if (::ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
throw POSIXException(__METHOD__ ": Unable to determine block size.", errno);
if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &blockCount) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine block count.", errno);
throw POSIXException(__METHOD__ ": Unable to determine block count.", errno);
_blockSize = blockSize;
_size = _blockSize * blockCount;
@@ -79,7 +72,7 @@ void RawDevice::devSize(int fd)
#endif
#ifdef __linux__
#ifdef __LINUX__
void RawDevice::devSize(int fd)
{
@@ -89,7 +82,7 @@ void RawDevice::devSize(int fd)
int blocks;
if (::ioctl(fd, BLKGETSIZE, &blocks) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
_size = 512 * blocks;
_blockSize = 512; //
@@ -97,108 +90,48 @@ void RawDevice::devSize(int fd)
}
#endif
// TODO -- FreeBSD/NetBSD/OpenBSD
#ifdef __FREEBSD__
void RawDevice::devSize(int fd)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::devSize"
unsigned blockSize;
off_t mediaSize;
if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize)
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize)
throw POSIX::Exception(__METHOD__ ": Unable to determine media size.", errno);
_blockSize = blockSize;
_size = mediaSize;
_blocks = mediaSize / 512;
}
#endif
#ifdef __minix
void RawDevice::devSize(int fd)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::devSize"
struct partition entry;
if (::ioctl(fd, DIOCGETP, &entry) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
_size = entry.size
_blockSize = 512; // not really but whatever.
_blocks = _size / 512;
}
#endif
RawDevice::RawDevice(const char *name, File::FileFlags flags) :
_file(name, flags)
RawDevice::RawDevice(const char *name, bool readOnly)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::RawDevice"
if (!_file.isValid())
{
throw ::Exception(__METHOD__ ": Invalid file handle.");
}
_readOnly = flags == File::ReadOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
devSize(_file.fd());
}
// open read-only, verify if device is readable, and then try to upgrade to read/write?
RawDevice::RawDevice(File& file, File::FileFlags flags) :
_file(file)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::RawDevice"
ProFUSE::auto_fd fd;
if (!_file.isValid())
if (!readOnly) fd.reset(::open(name, O_RDWR));
if (fd < 0)
{
throw ::Exception(__METHOD__ ": Invalid file handle.");
readOnly = false;
fd.reset(::open(name, O_RDONLY));
}
_readOnly = flags == File::ReadOnly;
if (fd < 0)
throw POSIXException(__METHOD__ ": Unable to open device.", errno);
_fd = -1;
_readOnly = readOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
devSize(_file.fd());
devSize(fd);
_fd = fd.release();
}
RawDevice::~RawDevice()
{
}
BlockDevicePointer RawDevice::Open(const char *name, File::FileFlags flags)
{
//return BlockDevicePointer(new RawDevice(name, flags));
return MAKE_SHARED(RawDevice, name, flags);
if (_fd >= 0) ::close(_fd);
}
@@ -207,20 +140,20 @@ void RawDevice::read(unsigned block, void *bp)
#undef __METHOD__
#define __METHOD__ "RawDevice::read"
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
// sun -- use pread
// apple - read full native block(s) ?
off_t offset = block * 512;
ssize_t ok = ::pread(_file.fd(), bp, 512, offset);
size_t ok = ::pread(_fd, bp, 512, offset);
// TODO -- EINTR?
if (ok != 512)
throw ok < 0
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
: ::Exception(__METHOD__ ": Error reading block.");
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}
@@ -230,20 +163,20 @@ void RawDevice::read(TrackSector ts, void *bp)
#define __METHOD__ "RawDevice::read"
unsigned block = ts.track * 8 + ts.sector / 2;
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
// sun -- use pread
// apple - read full native block(s) ?
off_t offset = (ts.track * 16 + ts.sector) * 256;
ssize_t ok = ::pread(_file.fd(), bp, 256, offset);
size_t ok = ::pread(_fd, bp, 256, offset);
// TODO -- EINTR?
if (ok != 256)
throw ok < 0
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
: ::Exception(__METHOD__ ": Error reading block.");
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}
@@ -252,19 +185,19 @@ void RawDevice::write(unsigned block, const void *bp)
#undef __METHOD__
#define __METHOD__ "RawDevice::write"
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (_readOnly)
throw ::Exception(__METHOD__ ": File is readonly.");
throw Exception(__METHOD__ ": File is readonly.");
off_t offset = block * 512;
ssize_t ok = ::pwrite(_file.fd(), bp, 512, offset);
size_t ok = ::pwrite(_fd, bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
: ::Exception(__METHOD__ ": Error writing block.");
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
@@ -274,19 +207,19 @@ void RawDevice::write(TrackSector ts, const void *bp)
#define __METHOD__ "RawDevice::write"
unsigned block = ts.track * 8 + ts.sector / 2;
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (_readOnly)
throw ::Exception(__METHOD__ ": File is readonly.");
throw Exception(__METHOD__ ": File is readonly.");
off_t offset = (ts.track * 16 + ts.sector) * 256;
ssize_t ok = ::pwrite(_file.fd(), bp, 256, offset);
size_t ok = ::pwrite(_fd, bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
: ::Exception(__METHOD__ ": Error writing block.");
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
@@ -300,13 +233,6 @@ bool RawDevice::mapped()
return false;
}
unsigned RawDevice::blocks()
{
return _blocks;
}
void RawDevice::sync()
{
#undef __METHOD__
@@ -314,7 +240,6 @@ void RawDevice::sync()
if (_readOnly) return;
if (::fsync(_file.fd()) < 0)
throw POSIX::Exception(__METHOD__ ": fsync error.", errno);
}
if (::fsync(_fd) < 0)
throw POSIXException(__METHOD__ ": fsync error.", errno);
}

View File

@@ -5,20 +5,15 @@
#include <Device/BlockDevice.h>
#include <File/File.h>
namespace Device {
// /dev/xxx
class RawDevice : public BlockDevice {
class RawDevice : BlockDevice {
public:
static BlockDevicePointer Open(const char *name, File::FileFlags flags);
RawDevice(const char *name, bool readOnly);
virtual ~RawDevice();
@@ -32,22 +27,17 @@ public:
virtual bool mapped();
virtual void sync();
virtual unsigned blocks();
RawDevice(const char *name, File::FileFlags flags);
RawDevice(File& file, File::FileFlags flags);
private:
void devSize(int fd);
File _file;
int _fd;
bool _readOnly;
uint64_t _size; // size of device in bytes.
unsigned _blocks; // # of 512k blocks i.e. _size / 512
uint64_t _size;
unsigned _blocks;
unsigned _blockSize; // native block size.
unsigned _blockSize;
};
}

View File

@@ -1,220 +0,0 @@
//
// 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 <cstring>
#include <NufxLib.h>
#include <File/File.h>
#include <File/MappedFile.h>
#include <Common/Exception.h>
#include <NuFX/Exception.h>
#include <POSIX/Exception.h>
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 NuFX::Exception(__METHOD__ ": NuGetAttr", e);
}
for (unsigned position = 0; position < recordCount; ++position)
{
NuRecordIdx rIndex;
const NuRecord *record;
e = NuGetRecordIdxByPosition(archive, position, &rIndex);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuGetRecordIdxByPosition", e);
}
e = NuGetRecord(archive, rIndex, &record);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuGetRecord", e);
}
for (unsigned i = 0; i < NuRecordGetNumThreads(record); ++i)
{
const NuThread *thread = NuGetThread(record, i);
if (thread && 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 NuFX::Exception(__METHOD__ ": NuOpenRO", e);
}
rt = FindDiskImageThread(archive);
fd = mkstemp(tmp);
if (fd < 0)
{
throw POSIX::Exception(__METHOD__ ": mkstemp", errno);
}
fp = fdopen(fd, "w");
if (!fp)
{
::close(fd);
throw POSIX::Exception(__METHOD__ ": fdopen", errno);
}
e = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &sink);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuCreateDataSinkForFP", e);
}
e = NuExtractThread(archive, rt.thread_index, sink);
if (e)
{
throw NuFX::Exception(__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;
}

View File

@@ -1,30 +0,0 @@
//
// 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

@@ -1,46 +1,37 @@
#include <Device/UniversalDiskImage.h>
#include <Device/MappedFile.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Common/Exception.h>
#include <Cache/MappedBlockCache.h>
#include <Cache/ConcreteBlockCache.h>
#include <ProFUSE/Exception.h>
using namespace Device;
using namespace LittleEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
UniversalDiskImage::UniversalDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
const void *data = file()->fileData();
// flags. bit 31 = locked.
_flags = Read32(data, 0x10);
}
UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
uint8_t * data = (uint8_t *)address();
_format = Read32(data, 0x0c);
_flags = Read32(data, 0x10);
_blocks = Read32(data, 0x14);
_dataOffset = Read32(data, 0x18);
_dataLength = Read32(data, 0x1c);
setBlocks(_blocks);
// TODO -- DO, Nibble support.
setAdaptor(new POAdaptor(_dataOffset + data));
_flags = 0;
}
BlockDevicePointer UniversalDiskImage::Create(const char *name, size_t blocks)
UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
{
// 64-byte header.
MappedFile *file = MappedFile::Create(name, blocks * 512 + 64);
MappedFile *file = new MappedFile(name, blocks * 512 + 64);
uint8_t tmp[64];
@@ -74,20 +65,18 @@ BlockDevicePointer UniversalDiskImage::Create(const char *name, size_t blocks)
// comment offset, creator, reserved -- 0.
header.setOffset(64, true);
std::memcpy(file->address(), header.buffer(), 64);
std::memcpy(file->fileData(), header.buffer(), 64);
//return BlockDevicePointer(new UniversalDiskImage(file));
return MAKE_SHARED(UniversalDiskImage, file);
file->setOffset(64);
file->setBlocks(blocks);
return new UniversalDiskImage(file);
}
BlockDevicePointer UniversalDiskImage::Open(MappedFile *file)
UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file)
{
Validate(file);
//return BlockDevicePointer(new UniversalDiskImage(file));
return MAKE_SHARED(UniversalDiskImage, file);
return new UniversalDiskImage(file);
}
@@ -96,68 +85,47 @@ BlockDevicePointer UniversalDiskImage::Open(MappedFile *file)
* TODO -- honor read-only flag.
*
*/
bool UniversalDiskImage::Validate(MappedFile *file, const std::nothrow_t &)
void UniversalDiskImage::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "UniversalDiskImage::Validate"
const void *data = file->address();
size_t size = file->length();
const void *data = file->fileData();
size_t size = file->fileSize();
bool ok = false;
unsigned blocks = 0;
unsigned offset = 0;
unsigned fileSize = 0;
if (size < 64) return false;
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
// file size blank in some cases.
//if (Read32(data, 0x1c) != blocks * 512) return false;
fileSize = Read32(data, 0x1c);
if (fileSize != 0 && fileSize != blocks * 512) return false;
do {
if (offset + blocks * 512 > size) return false;
if (size < 64) break;
if (std::memcmp(data, "2IMG", 4)) break;
// only prodos supported, for now...
if (Read32(data, 0x0c) != 1) break;
offset = Read32(data, 0x20);
blocks = Read32(data, 0x14);
// file size == blocks * 512
if (Read32(data, 0x1c) != blocks * 512) break;
if (offset + blocks * 512 > size) break;
ok = true;
} while (false);
return true;
}
bool UniversalDiskImage::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "UniversalDiskImage::Validate"
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
if (!Validate(file, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
file->reset();
file->setOffset(offset);
file->setBlocks(blocks);
}
bool UniversalDiskImage::readOnly()
{
return (_flags & 0x8000000) || DiskImage::readOnly();
}
BlockCachePointer UniversalDiskImage::createBlockCache()
{
if (_format == 1)
{
return MappedBlockCache::Create(shared_from_this(), _dataOffset + (uint8_t *)address());
}
return DiskImage::createBlockCache();
}
}

View File

@@ -3,39 +3,24 @@
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
#include <stdint.h>
namespace Device {
class UniversalDiskImage : public DiskImage {
public:
UniversalDiskImage(const char *name, bool readOnly);
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
static UniversalDiskImage *Create(const char *name, size_t blocks);
static UniversalDiskImage *Open(MappedFile *);
virtual bool readOnly();
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
UniversalDiskImage(MappedFile *);
private:
UniversalDiskImage();
UniversalDiskImage(MappedFile *);
static void Validate(MappedFile *);
uint32_t _format;
uint32_t _flags;
uint32_t _blocks;
uint32_t _dataOffset;
uint32_t _dataLength;
};
}

View File

@@ -5,11 +5,11 @@
#include <cstring>
namespace LittleEndian {
#include "IOBuffer.cpp.h"
#include "IOBuffer.t.cpp"
}
namespace BigEndian {
#include "IOBuffer.cpp.h"
#include "IOBuffer.t.cpp"
}
#endif

View File

@@ -30,9 +30,9 @@
_offset += 4;
}
void writeBytes(const void *src, unsigned count)
void writeBytes(const void *value, unsigned count)
{
std::memcpy(_offset + (uint8_t *)_buffer, src, count);
std::memcpy(_offset + (uint8_t *)_buffer, value, count);
_offset += count;
}
@@ -43,42 +43,6 @@
_offset += count;
}
uint8_t read8()
{
uint8_t x = Read8(_buffer, _offset);
_offset += 1;
return x;
}
uint16_t read16()
{
uint16_t x = Read16(_buffer, _offset);
_offset += 2;
return x;
}
uint32_t read24()
{
uint32_t x = Read24(_buffer, _offset);
_offset += 3;
return x;
}
uint32_t read32()
{
uint32_t x = Read32(_buffer, _offset);
_offset += 4;
return x;
}
void readBytes(void *dest, unsigned count)
{
std::memcpy(dest, _offset + (uint8_t *)_buffer, count);
_offset += count;
}
unsigned offset() const { return _offset; }
void setOffset(unsigned offset) { _offset = offset; }

View File

@@ -2,8 +2,12 @@
#include <cerrno>
#include <File/File.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <ProFUSE/Exception.h>
using ProFUSE::Exception;
using ProFUSE::POSIXException;
File::File()
@@ -22,22 +26,6 @@ File::File(File& f)
f._fd = -1;
}
File::File(const char *name, int flags, const std::nothrow_t&)
{
_fd = ::open(name, flags);
}
File::File(const char *name, int flags, mode_t mode, const std::nothrow_t&)
{
_fd = ::open(name, flags, mode);
}
File::File(const char *name, FileFlags flags, const std::nothrow_t&)
{
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
}
File::File(const char *name, int flags)
{
#undef __METHOD__
@@ -45,43 +33,14 @@ File::File(const char *name, int flags)
_fd = ::open(name, flags);
if (_fd < 0)
throw POSIX::Exception( __METHOD__ ": open", errno);
throw POSIXException( __METHOD__ ": open", errno);
}
File::File(const char *name, int flags, mode_t mode)
{
#undef __METHOD__
#define __METHOD__ "File::File"
_fd = ::open(name, flags, mode);
if (_fd < 0)
throw POSIX::Exception( __METHOD__ ": open", errno);
}
File::File(const char *name, FileFlags flags)
{
#undef __METHOD__
#define __METHOD__ "File::File"
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
if (_fd < 0)
throw POSIX::Exception( __METHOD__ ": open", errno);
}
File::~File()
{
close();
}
int File::release()
{
int tmp = _fd;
_fd = -1;
return tmp;
}
void File::close()
{
#undef __METHOD__
@@ -89,35 +48,22 @@ void File::close()
if (_fd >= 0)
{
::close(_fd);
int fd = _fd;
_fd = -1;
// destructor shouldn't throw.
/*
if (::close(fd) != 0)
throw POSIX::Exception(__METHOD__ ": close", errno);
*/
if (::close(fd) != 0)
throw POSIXException(__METHOD__ ": close", errno);
}
}
void File::adopt(File &f)
{
if (&f == this) return;
close();
_fd = f._fd;
f._fd = -1;
}
void File::adopt(int fd)
{
if (fd == _fd) return;
close();
_fd = fd;
}
void File::swap(File &f)
{
std::swap(_fd, f._fd);

View File

@@ -1,51 +1,25 @@
#ifndef __FILE_H__
#define __FILE_H__
#include <new>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
class File {
public:
enum FileFlags {
ReadOnly = 1,
ReadWrite = 2
};
File();
File(File &);
File(int fd);
File(const char *name, int flags);
File(const char *name, int flags, mode_t mode);
File(const char *name, FileFlags flags);
File(const char *name, int flags, const std::nothrow_t &);
File(const char *name, int flags, mode_t mode, const std::nothrow_t &);
File(const char *name, FileFlags flags, const std::nothrow_t &);
~File();
bool isValid() const
{
return _fd >= 0;
}
int fd() const { return _fd; }
int release();
void close();
void adopt(File &f);
void adopt(int fd);
void swap(File &f);
private:

View File

@@ -4,63 +4,53 @@
#include <sys/stat.h>
#include <File/MappedFile.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <ProFUSE/Exception.h>
using ProFUSE::POSIXException;
MappedFile::MappedFile()
{
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
}
MappedFile::MappedFile(MappedFile &mf)
{
_address = mf._address;
_length = mf._length;
_readOnly = mf._readOnly;
mf._address = MAP_FAILED;
mf._length = -1;
mf._readOnly = true;
}
MappedFile::MappedFile(const File &f, File::FileFlags flags, size_t size)
MappedFile::MappedFile(File f, bool readOnly)
{
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
struct stat st;
init(f, flags == File::ReadOnly, size);
// close enough
if (f.fd() < 0)
throw POSIXException( __METHOD__, EBADF);
if (::fstat(f.fd(), &st) != 0)
throw POSIXException(__METHOD__ ": fstat", errno);
if (!S_ISREG(st.st_mode))
throw POSIXException(__METHOD__, ENODEV);
_length = st.st_size;
_address = ::mmap(0, _length,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, f.fd(), 0);
if (_address == MAP_FAILED)
throw POSIXException(__METHOD__ ": mmap", errno);
}
MappedFile::MappedFile(const char *name, File::FileFlags flags)
{
File f(name, flags);
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
init(f, flags == File::ReadOnly, 0);
}
MappedFile::MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t &nothrow)
{
File f(name, flags, nothrow);
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
if (f.isValid())
init(f, flags == File::ReadOnly, 0);
}
MappedFile::~MappedFile()
{
close();
@@ -68,42 +58,6 @@ MappedFile::~MappedFile()
void MappedFile::init(const File &f, bool readOnly, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::init"
struct stat st;
int prot = readOnly ? PROT_READ : PROT_READ | PROT_WRITE;
int flags = MAP_FILE | MAP_SHARED;
// close enough
if (f.fd() < 0)
throw POSIX::Exception( __METHOD__, EBADF);
if (!size)
{
if (::fstat(f.fd(), &st) != 0)
throw POSIX::Exception(__METHOD__ ": fstat", errno);
if (!S_ISREG(st.st_mode))
throw POSIX::Exception(__METHOD__, ENODEV);
size = st.st_size;
}
_length = size;
_address = ::mmap(0, _length, prot, flags, f.fd(), 0);
if (_address == MAP_FAILED)
throw POSIX::Exception(__METHOD__ ": mmap", errno);
_readOnly = readOnly;
}
void MappedFile::close()
{
#undef __METHOD__
@@ -111,22 +65,14 @@ void MappedFile::close()
if (_address != MAP_FAILED)
{
/*
void *address = _address;
size_t length = _length;
*/
::munmap(_address, _length);
_address = MAP_FAILED;
_length = -1;
_readOnly = true;
// destructor shouldn't throw.
/*
if (::munmap(address, length) != 0)
throw POSIX::Exception(__METHOD__ ": munmap", errno);
*/
throw POSIXException(__METHOD__ ": munmap", errno);
}
}
@@ -138,7 +84,7 @@ void MappedFile::sync()
if (_address != MAP_FAILED)
{
if (::msync(_address, _length, MS_SYNC) != 0)
throw POSIX::Exception(__METHOD__ ": msync", errno);
throw POSIXException(__METHOD__ ": msync", errno);
}
}
@@ -147,39 +93,13 @@ void MappedFile::adopt(MappedFile &mf)
close();
_address = mf._address;
_length = mf._length;
_readOnly = mf._readOnly;
mf._address = MAP_FAILED;
mf._length = -1;
mf._readOnly = true;
}
void MappedFile::swap(MappedFile &mf)
{
std::swap(_address, mf._address);
std::swap(_length, mf._length);
std::swap(_readOnly, mf._readOnly);
}
MappedFile *MappedFile::Create(const char *name, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::Create"
File fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644));
if (!fd.isValid())
{
throw POSIX::Exception(__METHOD__ ": Unable to create file.", errno);
}
// TODO -- is ftruncate portable?
if (::ftruncate(fd.fd(), size) < 0)
{
// TODO -- unlink?
throw POSIX::Exception(__METHOD__ ": Unable to truncate file.", errno);
}
return new MappedFile(fd, File::ReadWrite, size);
}

View File

@@ -1,39 +1,25 @@
#ifndef __MAPPED_FILE_H__
#define __MAPPED_FILE_H__
#include <new>
#include <sys/mman.h>
#include <File/File.h>
class File;
class MappedFile {
public:
MappedFile();
MappedFile(File f, bool readOnly);
MappedFile(MappedFile&);
MappedFile(const File &f, File::FileFlags flags, size_t size = -1);
MappedFile(const char *name, File::FileFlags flags);
MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t &nothrow);
~MappedFile();
static MappedFile *Create(const char *name, size_t size);
bool isValid() const
{
return _address != MAP_FAILED;
}
void sync();
void close();
void *address() const { return _address; }
size_t length() const { return _length; }
bool readOnly() const { return _readOnly; }
void swap(MappedFile &);
void adopt(MappedFile &);
@@ -42,11 +28,8 @@ class MappedFile {
MappedFile& operator=(MappedFile &);
void init(const File &f, bool readOnly, size_t size);
void *_address;
size_t _length;
bool _readOnly;
};

298
Makefile
View File

@@ -1,280 +1,22 @@
CC = c++
CPPFLAGS += -Wall -W -Wno-multichar -Wno-unused-parameter -Wno-unknown-pragmas -Wno-narrowing -I. -O2 -g -std=c++11
LDFLAGS += -pthread
UNAME = $(shell uname -s)
CC = g++
CPPFLAGS += -Wall -O2 -g
ifeq ($(UNAME),Darwin)
# should use pkg-config but it may not be installed.
FUSE_LIBS += -losxfuse -pthread -liconv
CPPFLAGS += -I/usr/local/include/osxfuse/fuse -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE
else
CPPFLAGS += $(shell pkg-config --cflags fuse)
FUSE_LIBS += $(shell pkg-config --libs fuse)
endif
ifdef HAVE_NUFX
DEVICE_OBJECTS += Device/SDKImage.o
EXCEPTION_OBJECTS += NuFX/Exception.o
LDFLAGS += -L/usr/local/lib/
LIBS += -lnufx -lz
CPPFLAGS += -DHAVE_NUFX=1
endif
OBJECTS += ${wildcard *.o}
OBJECTS += ${wildcard bin/*.o}
OBJECTS += ${wildcard Cache/*.o}
OBJECTS += ${wildcard Device/*.o}
OBJECTS += ${wildcard Endian/*.o}
OBJECTS += ${wildcard File/*.o}
OBJECTS += ${wildcard Pascal/*.o}
OBJECTS += ${wildcard Common/*.o}
OBJECTS += ${wildcard ProDOS/*.o}
OBJECTS += ${wildcard POSIX/*.o}
OBJECTS += ${wildcard NuFX/*.o}
TARGETS = o/apfm o/newfs_pascal o/fuse_pascal o/profuse o/xattr
BIN_OBJECTS += bin/apfm.o
BIN_OBJECTS += bin/fuse_pascal_ops.o
BIN_OBJECTS += bin/newfs_prodos.o
BIN_OBJECTS += bin/fuse_pascal.o
BIN_OBJECTS += bin/newfs_pascal.o
BIN_OBJECTS += bin/xattr.o
BIN_OBJECTS += bin/profuse.o
BIN_OBJECTS += bin/profuse_dirent.o
BIN_OBJECTS += bin/profuse_file.o
BIN_OBJECTS += bin/profuse_stat.o
BIN_OBJECTS += bin/profuse_xattr.o
CACHE_OBJECTS += Cache/BlockCache.o
CACHE_OBJECTS += Cache/ConcreteBlockCache.o
CACHE_OBJECTS += Cache/MappedBlockCache.o
DEVICE_OBJECTS += Device/Adaptor.o
DEVICE_OBJECTS += Device/BlockDevice.o
DEVICE_OBJECTS += Device/DavexDiskImage.o
DEVICE_OBJECTS += Device/DiskCopy42Image.o
DEVICE_OBJECTS += Device/DiskImage.o
DEVICE_OBJECTS += Device/RawDevice.o
DEVICE_OBJECTS += Device/UniversalDiskImage.o
ENDIAN_OBJECTS += Endian/Endian.o
FILE_OBJECTS += File/File.o
FILE_OBJECTS += File/MappedFile.o
PASCAL_OBJECTS += Pascal/Date.o
PASCAL_OBJECTS += Pascal/FileEntry.o
PASCAL_OBJECTS += Pascal/TextWriter.o
PASCAL_OBJECTS += Pascal/Entry.o
PASCAL_OBJECTS += Pascal/VolumeEntry.o
COMMON_OBJECTS += Common/Lock.o
PRODOS_OBJECTS += ProDOS/DateTime.o
PRODOS_OBJECTS += ProDOS/Disk.o
PRODOS_OBJECTS += ProDOS/File.o
EXCEPTION_OBJECTS += Common/Exception.o
EXCEPTION_OBJECTS += ProDOS/Exception.o
EXCEPTION_OBJECTS += POSIX/Exception.o
all: $(TARGETS)
apfm: o/apfm
@true
fuse_pascal: o/fuse_pascal
@true
newfs_pascal: o/newfs_pascal
@true
profuse: o/profuse
@true
xattr: o/xattr
@true
o:
mkdir $@
o/xattr: bin/xattr.o | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/newfs_pascal: bin/newfs_pascal.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/apfm: bin/apfm.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/fuse_pascal: bin/fuse_pascal.o bin/fuse_pascal_ops.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
o/profuse: bin/profuse.o bin/profuse_dirent.o bin/profuse_file.o \
bin/profuse_stat.o bin/profuse_xattr.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PRODOS_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
clean:
rm -f ${OBJECTS} ${TARGETS}
xattr.o: bin/xattr.cpp
newfs_prodos: \
newfs_prodos.o \
Exception.o \
BlockDevice.o \
BlockCache.o \
UniversalDiskImage.o \
DiskCopy42Image.o \
DavexDiskImage.o \
RawDevice.o \
MappedFile.o \
Buffer.o \
Entry.o \
Directory.o \
VolumeDirectory.o \
Bitmap.o \
DateTime.o
newfs_pascal.o: bin/newfs_pascal.cpp Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h Cache/BlockCache.h Device/RawDevice.h File/File.h \
Pascal/Pascal.h Pascal/Date.h
fuse_pascal.o: bin/fuse_pascal.cpp Pascal/Pascal.h Pascal/Date.h \
Common/Exception.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
fuse_pascal_ops.o: bin/fuse_pascal_ops.cpp Pascal/Pascal.h Pascal/Date.h \
Common/auto.h Common/Exception.h
apfm.o: bin/apfm.cpp Pascal/Pascal.h Pascal/Date.h Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h
File/File.o: File/File.cpp File/File.h Common/Exception.h
File/MappedFile.o: File/MappedFile.cpp File/MappedFile.h File/File.h \
Common/Exception.h
Device/Adaptor.o: Device/Adaptor.cpp Device/Adaptor.h Device/TrackSector.h \
Common/Exception.h
Device/BlockDevice.o: Device/BlockDevice.cpp Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
Cache/ConcreteBlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Device/UniversalDiskImage.h \
Device/DiskCopy42Image.h Device/DavexDiskImage.h Device/RawDevice.h
Device/DavexDiskImage.o: Device/DavexDiskImage.cpp \
Device/DavexDiskImage.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
Device/DiskCopy42Image.o: Device/DiskCopy42Image.cpp \
Device/DiskCopy42Image.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
Device/DiskImage.o: Device/DiskImage.cpp Device/DiskImage.h \
Common/Exception.h \
Device/BlockDevice.h Device/TrackSector.h Cache/BlockCache.h \
Device/Adaptor.h File/MappedFile.h File/File.h Cache/MappedBlockCache.h
Device/RawDevice.o: Device/RawDevice.cpp Device/RawDevice.h \
Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h File/File.h
Device/UniversalDiskImage.o: Device/UniversalDiskImage.cpp \
Device/UniversalDiskImage.h Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h Cache/BlockCache.h Device/DiskImage.h \
Device/Adaptor.h File/MappedFile.h File/File.h Endian/Endian.h \
Endian/IOBuffer.h Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h \
Cache/ConcreteBlockCache.h
Endian/Endian.o: Endian/Endian.cpp Endian/Endian.h
Cache/BlockCache.o: Cache/BlockCache.cpp Cache/BlockCache.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Common/auto.h
Cache/ConcreteBlockCache.o: Cache/ConcreteBlockCache.cpp \
Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
Cache/ConcreteBlockCache.h Common/auto.h
Cache/MappedBlockCache.o: Cache/MappedBlockCache.cpp \
Cache/MappedBlockCache.h \
Cache/BlockCache.h Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h
Common/Exception.o: Common/Exception.cpp Common/Exception.h
Common/Lock.o: Common/Lock.cpp Common/Lock.h
Pascal/Date.o: Pascal/Date.cpp Pascal/Date.h
Pascal/Entry.o: Pascal/Entry.cpp Pascal/Entry.h Pascal/Date.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
Pascal/FileEntry.o: Pascal/FileEntry.cpp Pascal/Pascal.h Pascal/Date.h \
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h Pascal/TextWriter.h
Pascal/VolumeEntry.o: Pascal/VolumeEntry.cpp Pascal/Pascal.h Pascal/Date.h \
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
Pascal/TextWriter.o: Pascal/TextWriter.cpp Pascal/TextWriter.h \
Pascal/FileEntry.h Pascal/Entry.h Pascal/Date.h Common/Exception.h
ProDOS/DateTime.o: ProDOS/DateTime.cpp ProDOS/DateTime.h
ProDOS/Disk.o: ProDOS/Disk.cpp ProDOS/Disk.h
ProDOS/File.o: ProDOS/File.cpp ProDOS/File.h
ProDOS/Exception.o: ProDOS/Exception.cpp ProDOS/Exception.h Common/Exception.h
NuFX/Exception.o: NuFX/Exception.cpp NuFX/Exception.h Common/Exception.h
POSIX/Exception.o: POSIX/Exception.cpp POSIX/Exception.h Common/Exception.h

61
MappedFile.cpp Normal file
View File

@@ -0,0 +1,61 @@
#include <unistd.h>
#include <sys/mman.h>
#include <cerrno>
class MappedFile
{
public:
MappedFile(int fd, bool readOnly)
{
_fd = fd;
_length = ::lseek(fd, 0, SEEK_END);
_address = MAP_FAILED;
if (_length > 0)
{
_address = ::mmap(0, _length,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE, fd, 0);
}
}
MappedFile(void *address, size_t size)
{
_fd = -1;
_address = address;
_size = size;
}
~MappedFile()
{
if (_address != MAP_FAILED) ::munmap(_address, _length);
if (_fd != -1) ::close(_fd);
}
void *address() const
{
return _address;
}
size_t length()
{
return _length;
}
int sync()
{
if (::msync(_address, _length, MS_SYNC) == 0)
return 0;
return errno;
}
private:
int _fd;
void *_address;
size_t _length;
};

View File

@@ -1,63 +0,0 @@
#include "DirectoryEntry.h"
#include <cstring>
using namespace NuFX;
EntryPointer DirectoryEntry::lookup(const std::string& name) const
{
EntryIterator iter;
for (iter = _children.begin(); iter != _children.end(); ++iter)
{
EntryPointer e = *iter;
if (e->name() == name) return e;
}
return EntryPointer(); // empty.
}
DirectoryEntryPointer DirectoryEntry::dir_lookup(const std::string &name)
{
EntryIterator iter;
for (iter = _children.begin(); iter != _children.end(); ++iter)
{
EntryPointer e = *iter;
if (e->name() == name)
{
// dynamic cast, will return as empty pointer if
// not a directory.
return DYNAMIC_POINTER_CAST(DirectoryEntryPointer, e);
}
}
// not found, insert it..
DirectoryEntryPointer e(new DirectoryEntryPointer(name));
VolumeEntryPointer v = volume().lock();
_children.add(e);
if (v)
{
v->addEntry(e);
}
return e;
}
#pragma mark -
#pragma mark fuse-support
EntryPointer DirectoryEntry::childAtIndex(unsigned index) const
{
if (index >= _children.size()) return EntryPointer();
return _children[index];
}

View File

@@ -1,38 +0,0 @@
#ifndef __NUFX_DIRECTORYENTRY_H__
#define __NUFX_DIRECTORYENTRY_H__
#include "Entry.h"
#include <vector>
#include <dirent.t>
namespace NuFX {
class DirectoryEntry : public Entry
{
public:
EntryPointer lookup(const std::string & name) const;
EntryPointer childAtIndex(unsigned index) const;
protected:
// creates directory if it does not exist.
DirectoryEntryPointer dir_lookup(const std::string &name);
private:
std::vector<EntryPointer> _children;
typedef std::vector<EntryPointer>::iterator EntryIterator;
};
}
#endif

View File

@@ -1,66 +0,0 @@
#include "Entry.h"
#include "Exception.h"
using namespace NuFX;
Entry::Entry() :
_inode(0)
{
}
Entry::Entry(const std::string& name) :
_name(name), _inode(0)
{
}
Entry::~Entry()
{
}
unsigned Entry::inode() const
{
return _inode;
}
const std::string& Entry::name() const
{
return _name;
}
void Entry::setName(const std::string& name)
{
_name = name;
}
VolumeEntryWeakPointer Entry::volume() const
{
return _volume;
}
void Entry::setVolume(VolumeEntryWeakPointer volume)
{
_volume = volume;
}
int Entry::stat(struct stat *st) const
{
return -1;
}
ssize_t Entry::read(size_t size, off_t offset) const
{
return -1;
}
ssize_t Entry::listxattr(char *namebuf, size_t size, int options) const
{
return -1;
}
ssize_t Entry::getxattr(const std::string &name, void *value, size_t size, u_int32_t position, int options) const
{
return -1;
}

View File

@@ -1,69 +0,0 @@
#ifndef __NUFX_ENTRY_H__
#define __NUFX_ENTRY_H__
#include <Common/smart_pointers.h>
#include <string>
#include <stdint.h>
#include <NufxLib.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <dirent.h>
namespace NuFX {
class DirectoryEntry;
class Entry;
class FileEntry;
class VolumeEntry;
typedef SHARED_PTR(DirectoryEntry) DirectoryEntryPointer;
typedef SHARED_PTR(Entry) EntryPointer;
typedef SHARED_PTR(FileEntry) FileEntryPointer;
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
typedef WEAK_PTR(Entry) EntryWeakPointer;
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
public:
virtual ~Entry();
virtual unsigned inode() const;
virtual const std::string& name() const;
// operations...
virtual int stat(VolumeEntryPointer, struct stat *) const;
virtual ssize_t read(VolumeEntryPointer, size_t size, off_t offset) const;
virtual ssize_t listxattr(VolumeEntryPointer, char *namebuf, size_t size, int options) const;
virtual ssize_t getxattr(VolumeEntryPointer, const std::string &name, void *value, size_t size, u_int32_t position, int options) const;
virtual int open(VolumeEntryPointer, int flags);
virtual int close(VolumeEntryPointer);
protected:
Entry();
Entry(const std::string& name);
void setName(const std::string&);
private:
Entry(const Entry&);
Entry& operator=(const Entry&);
friend VolumeEntry;
std::string _name;
unsigned _inode;
};
}
#endif

View File

@@ -1,10 +0,0 @@
#include "Exception.h"
namespace NuFX {
const char *NuFX::Exception::errorString()
{
return ::NuStrError((NuError)error());
}
}

View File

@@ -1,35 +0,0 @@
#ifndef __NUFX_EXCEPTION_H__
#define __NUFX_EXCEPTION_H__
#include <Common/Exception.h>
#include <NufxLib.h>
namespace NuFX {
class Exception : public ::Exception
{
public:
Exception(const char *cp, NuError error);
Exception(const std::string& string, NuError error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, NuError error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, NuError error) :
super(string, error)
{
}
}
#endif

View File

@@ -1,23 +0,0 @@
#ifndef __NUFX_FILEENTRY_H__
#define __NUFX_FILEENTRY_H__
#include "Entry.h"
#include <NufxLib.h>
namespace NuFX {
class FileEntry : public Entry
{
public:
private:
NuRecordIdx _recordID;
unsigned _flags; // threads
size_t _size; // data size
};
}
#endif

View File

@@ -1,14 +0,0 @@
#include "VolumeEntry.h"
using namespace NuFX;
void VolumeEntry::addEntry(EntryPointer e)
{
if (!e) return;
e->setVolume(pointer());
_inodeIndex->push_back(e);
e->_inode = _inodeIndex->length() + 100 - 1;
}

View File

@@ -1,35 +0,0 @@
#ifndef __NUFX_VOLUMEENTRY_H__
#define __NUFX_VOLUMEENTRY_H__
#include "DirectoryEntry.h"
#include <Common/unordered_map.h>
namespace NuFX {
class VolumeEntry : public DirectoryEntry
{
public:
void addEntry(EntryPointer);
private:
VolumeEntryPointer pointer() const
{
return STATIC_POINTER_CAST(VolumeEntryPointer, shared_from_this());
}
void parse();
NuArchive *_archive;
//unsigned _inodeGenerator;
std::vector<WeakPointer> _inodeIndex;
};
}
#endif

View File

@@ -1,13 +0,0 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
namespace POSIX {
const char *Exception::errorString()
{
return strerror(error());
}
}

View File

@@ -1,34 +0,0 @@
#ifndef __POSIX_EXCEPTION_H__
#define __POSIX_EXCEPTION_H__
#include <Common/Exception.h>
namespace POSIX {
class Exception : public ::Exception {
public:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, int error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, int error) :
super(string, error)
{
}
}
#endif

View File

@@ -1,11 +1,13 @@
#include <Pascal/Entry.h>
#include <Pascal/File.h>
#include <Common/Exception.h>
#include <ProfUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
#include <algorithm>
@@ -64,7 +66,7 @@ Entry::Entry()
_lastBlock = 0;
_fileKind = 0;
_inode = 0;
_address = NULL;
_parent = NULL;
}
Entry::Entry(void *vp)

View File

@@ -1,89 +0,0 @@
#ifndef __PASCAL_ENTRY_H__
#define __PASCAL_ENTRY_H__
#include <Common/smart_pointers.h>
namespace Device {
class BlockDevice;
class BlockCache;
}
namespace LittleEndian {
class IOBuffer;
}
namespace Pascal {
enum {
kUntypedFile,
kBadBlockFile,
kCodeFile,
kTextFile,
kInfoFile,
kDataFile,
kGrafFile,
kFotoFile,
kSecureDir
};
class FileEntry;
class VolumeEntry;
typedef SHARED_PTR(FileEntry) FileEntryPointer;
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
typedef WEAK_PTR(FileEntry) FileEntryWeakPointer;
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
public:
virtual ~Entry();
unsigned blocks() const { return _lastBlock - _firstBlock; }
unsigned firstBlock() const { return _firstBlock; }
unsigned lastBlock() const { return _lastBlock; }
unsigned fileKind() const { return _fileKind; }
unsigned inode() const { return _inode; }
void setInode(unsigned inode) { _inode = inode; }
VolumeEntryWeakPointer parent() { return _parent; }
protected:
static unsigned ValidName(const char *name, unsigned maxSize);
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
Entry();
Entry(void *);
void init(void *);
unsigned _firstBlock;
unsigned _lastBlock;
unsigned _fileKind;
unsigned _inode;
private:
friend class VolumeEntry;
VolumeEntryWeakPointer _parent;
unsigned _address;
};
}
#endif

187
Pascal/File.h Normal file
View File

@@ -0,0 +1,187 @@
#ifndef __FILE_H__
#define __FILE_H__
#include <Pascal/Date.h>
#include <vector>
namespace Device {
class BlockDevice;
class AbstractBlockCache;
}
namespace LittleEndian {
class IOBuffer;
}
namespace Pascal {
enum {
kUntypedFile,
kBadBlockFile,
kCodeFile,
kTextFile,
kInfoFile,
kDataFile,
kGrafFile,
kFotoFile,
kSecureDir
};
class FileEntry;
class VolumeEntry;
class Entry {
public:
virtual ~Entry();
unsigned blocks() const { return _lastBlock - _firstBlock; }
unsigned firstBlock() const { return _firstBlock; }
unsigned lastBlock() const { return _lastBlock; }
unsigned fileKind() const { return _fileKind; }
unsigned inode() const { return _inode; }
void setInode(unsigned inode) { _inode = inode; }
VolumeEntry *parent() { return _parent; }
protected:
static unsigned ValidName(const char *name, unsigned maxSize);
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
Entry();
Entry(void *);
void init(void *);
unsigned _firstBlock;
unsigned _lastBlock;
unsigned _fileKind;
unsigned _inode;
private:
friend class VolumeEntry;
VolumeEntry *_parent;
};
class VolumeEntry : public Entry {
public:
// create new
VolumeEntry(const char *name, Device::BlockDevice *);
// open existing
VolumeEntry(Device::BlockDevice *);
virtual ~VolumeEntry();
const char *name() const { return _fileName; }
unsigned fileCount() const { return _fileCount; }
unsigned volumeBlocks() const { return _lastVolumeBlock; }
Pascal::Date lastBoot() const { return _lastBoot; }
FileEntry *fileAtIndex(unsigned i) const;
void addChild(FileEntry *child, unsigned blocks);
void *loadBlock(unsigned block);
void unloadBlock(unsigned block, bool dirty = false);
void readBlock(unsigned block, void *);
void writeBlock(unsigned block, void *);
unsigned static ValidName(const char *);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
VolumeEntry();
void init(void *);
unsigned _fileNameLength;
char _fileName[8];
unsigned _lastVolumeBlock;
unsigned _fileCount;
unsigned _accessTime;
Pascal::Date _lastBoot;
std::vector<FileEntry *> _files;
unsigned _inodeGenerator;
Device::BlockDevice *_device;
Device::AbstractBlockCache *_cache;
};
class FileEntry : public Entry {
public:
FileEntry(const char *name, unsigned fileKind);
FileEntry(void *vp);
virtual ~FileEntry();
unsigned fileSize();
unsigned lastByte() const { return _lastByte; }
int read(uint8_t *buffer, unsigned size, unsigned offset);
int write(uint8_t *buffer, unsigned size, unsigned offset);
const char *name() const { return _fileName; }
Date modification() const { return _modification; }
unsigned static ValidName(const char *);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
unsigned _status;
unsigned _fileNameLength;
char _fileName[16];
unsigned _lastByte;
Date _modification;
// non-text files
unsigned dataFileSize();
int dataRead(uint8_t *buffer, unsigned size, unsigned offset);
// for text files.
void textInit();
unsigned textFileSize();
int textRead(uint8_t *buffer, unsigned size, unsigned offset);
unsigned textReadPage(unsigned block, uint8_t *in);
unsigned textDecodePage(unsigned block, uint8_t *out);
std::vector<unsigned> *_pageSize;
unsigned _fileSize;
};
}
#endif

View File

@@ -3,19 +3,17 @@
#include <cstring>
#include <cctype>
#include <memory>
#include <cerrno>
#include <Pascal/Pascal.h>
#include <Pascal/TextWriter.h>
#include <Pascal/File.h>
#include <Common/auto.h>
#include <Common/Exception.h>
#include <ProDOS/Exception.h>
#include <ProfUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
@@ -23,38 +21,14 @@ using namespace LittleEndian;
using namespace Pascal;
enum {
kDLE = 16
};
/*
* _lastByte is in the range 1..512 and indicates how many bytes in the last
* block are in use.
* _lastBlock is the block *after* the actual last block.
* _maxFileSize is the maximum file size the file can grow to.
*
*/
#pragma mark -
#pragma mark FileEntry
unsigned FileEntry::ValidName(const char *cp)
{
return Entry::ValidName(cp, 15);
}
FileEntryPointer FileEntry::Open(void *vp)
{
//return FileEntryPointer(new FileEntry(vp));
return MAKE_SHARED(FileEntry, vp);
}
FileEntryPointer FileEntry::Create(const char *name, unsigned fileKind)
{
//return FileEntryPointer(new FileEntry(name, fileKind));
return MAKE_SHARED(FileEntry, name, fileKind);
}
FileEntry::FileEntry(void *vp) :
Entry(vp)
{
@@ -67,7 +41,6 @@ FileEntry::FileEntry(void *vp) :
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::FileEntry(const char *name, unsigned fileKind)
@@ -78,7 +51,7 @@ FileEntry::FileEntry(const char *name, unsigned fileKind)
unsigned length = ValidName(name);
if (!length)
throw ::Exception(__METHOD__ ": Invalid file name.");
throw ProFUSE::Exception(__METHOD__ ": Invalid file name.");
_fileKind = fileKind;
_status = 0;
@@ -93,7 +66,6 @@ FileEntry::FileEntry(const char *name, unsigned fileKind)
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::~FileEntry()
@@ -102,42 +74,6 @@ FileEntry::~FileEntry()
}
void FileEntry::setFileKind(unsigned kind)
{
_fileKind = kind;
if (_pageSize)
{
delete _pageSize;
_fileSize = 0;
_pageSize = NULL;
}
VolumeEntryPointer v = parent().lock();
// throw if expired?
if (v) v->writeEntry(this);
}
void FileEntry::setName(const char *name)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::setName"
unsigned length = ValidName(name);
if (!length)
throw ProDOS::Exception(__METHOD__ ": Invalid file name.", ProDOS::badPathSyntax);
_fileNameLength = length;
for (unsigned i = 0; i < length; ++i)
_fileName[i] = std::toupper(name[i]);
// parent's responsibility.
//parent()->writeEntry(this);
}
unsigned FileEntry::fileSize()
{
switch(fileKind())
@@ -181,258 +117,6 @@ int FileEntry::read(uint8_t *buffer, unsigned size, unsigned offset)
}
int FileEntry::truncate(unsigned newSize)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::truncate"
unsigned currentSize = fileSize();
if (currentSize == newSize) return 0;
if (fileKind() == kTextFile)
{
if (newSize)
{
errno = EINVAL;
return -1;
}
newSize = 2; // text files have a 2-page scratch buffer for the editor.
if (_pageSize)
{
_pageSize->clear();
_fileSize = 0;
}
}
if (truncateCommon(newSize) != 0)
return -1;
_modification = Date::Today();
VolumeEntryPointer v = parent().lock();
if (v) v->writeEntry(this);
return 0;
}
/*
* truncateCommon -- common truncation code.
* updates _lastByte and _lastBlock but does
* not update _modification or commit to disk.
*/
int FileEntry::truncateCommon(unsigned newSize)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::truncateCommon"
unsigned currentSize = fileSize();
VolumeEntryPointer v = parent().lock();
if (newSize == currentSize) return 0;
if (newSize > currentSize)
{
if (newSize > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
unsigned remainder = newSize - currentSize;
unsigned block = _lastBlock - 1;
if (_lastByte != 512)
{
// last page not full
unsigned count = std::min(512 - _lastByte, remainder);
if (v)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memset(address + _lastByte, 0, count);
v->unloadBlock(block, true);
}
remainder -= count;
}
block++;
while (remainder)
{
unsigned count = std::min(512u, remainder);
if (v)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memset(address, 0, count);
v->unloadBlock(block, true);
}
remainder -= count;
block++;
}
}
setFileSize(newSize);
return 0;
}
int FileEntry::write(TextWriter &text)
{
unsigned blocks = text.blocks();
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return -1;
}
if (v->readOnly())
{
errno = EROFS;
return -1;
}
if (fileKind() != kTextFile)
{
errno = EINVAL;
return -1;
}
if (blocks * 512 > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
for (unsigned i = 0; i < blocks; ++i)
{
void *buffer = text.data(i);
v->writeBlock(_firstBlock + i, buffer);
}
_modification = Date::Today();
setFileSize(blocks * 512);
v->writeEntry(this);
v->sync();
return blocks * 512;
}
int FileEntry::write(const uint8_t *buffer, unsigned size, unsigned offset)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::write"
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return -1;
}
if (v->readOnly())
{
errno = EROFS;
return -1;
}
if (fileKind() == kTextFile)
{
errno = EINVAL;
return -1;
}
unsigned currentSize = fileSize();
unsigned newSize = std::max(offset + size, currentSize);
if (newSize > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
if (offset > currentSize)
{
if (truncateCommon(offset) != 0) return -1;
}
// now write the data...
unsigned block = firstBlock() + offset / 512;
unsigned start = offset % 512;
unsigned remainder = size;
if (start)
{
unsigned count = std::min(512 - start, remainder);
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memcpy(address + start, buffer, count);
v->unloadBlock(block, true);
remainder -= count;
buffer += count;
block++;
}
while (remainder)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
unsigned count = std::min(512u, size);
std::memcpy(address, buffer, count);
v->unloadBlock(block, true);
remainder -= count;
buffer += count;
block++;
}
if (newSize > currentSize) setFileSize(newSize);
_modification = Date::Today();
v->writeEntry(this);
return size;
}
/*
* private
* set the file size. Does not check if > _maxFileSize.
*
*/
void FileEntry::setFileSize(unsigned size)
{
if (size == 0)
{
// TODO -- verify how 0 byte files are handled.
_lastBlock = _firstBlock + 1;
_lastByte = 0;
return;
}
_lastBlock = 1 + _firstBlock + size / 512;
_lastByte = size % 512;
if (_lastByte == 0) _lastByte = 512;
}
unsigned FileEntry::dataFileSize()
{
return blocks() * 512 - 512 + _lastByte;
@@ -455,13 +139,6 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
unsigned count = 0;
unsigned block = 0;
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return 0;
}
block = _firstBlock + (offset / 512);
@@ -476,7 +153,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
{
unsigned bytes = std::min(offset % 512, size);
v->readBlock(block++, tmp);
parent()->readBlock(block++, tmp);
std::memcpy(buffer, tmp + 512 - bytes, bytes);
@@ -491,7 +168,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
while (size >= 512)
{
v->readBlock(block++, buffer);
parent()->readBlock(block++, buffer);
buffer += 512;
count += 512;
@@ -503,7 +180,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
*/
if (size)
{
v->readBlock(block, tmp);
parent()->readBlock(block, tmp);
std::memcpy(buffer, tmp, size);
count += size;
@@ -524,7 +201,7 @@ int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
unsigned l;
unsigned count = 0;
::auto_array<uint8_t> tmp;
ProFUSE::auto_array<uint8_t> tmp;
unsigned tmpSize = 0;
if (!_pageSize) textInit();
@@ -544,7 +221,7 @@ int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
to += pageSize;
}
// first 2 pages are spare, for editor use, not actually text.
block = _firstBlock + 2 + (page * 2);
@@ -613,7 +290,7 @@ unsigned FileEntry::textDecodePage(unsigned block, uint8_t *out)
dle = false;
continue;
}
if (c == kDLE) { dle = true; continue; }
if (c == 16) { dle = true; continue; }
//if (c & 0x80) continue; // ascii only.
@@ -632,20 +309,13 @@ unsigned FileEntry::textReadPage(unsigned block, uint8_t *in)
// reads up to 2 blocks.
// assumes block within _startBlock ... _lastBlock - 1
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return 0;
}
v->readBlock(block, in);
parent()->readBlock(block, in);
if (block + 1 == _lastBlock)
{
return _lastByte;
}
v->readBlock(block + 1, in + 512);
parent()->readBlock(block + 1, in + 512);
if (block +2 == _lastBlock)
{
return 512 + _lastByte;
@@ -666,69 +336,8 @@ void FileEntry::textInit()
for (unsigned block = _firstBlock + 2; block < _lastBlock; block += 2)
{
unsigned size = textDecodePage(block, NULL);
//printf("%u: %u\n", block, size);
printf("%u: %u\n", block, size);
_fileSize += size;
_pageSize->push_back(size);
}
}
/*
* compress white space into a dle.
* returns true if altered.
* nb -- only leading white space is compressed.
*/
bool FileEntry::Compress(std::string& text)
{
std::string out;
size_t pos;
size_t count;
if (text.length() < 3) return false;
if (text[0] != ' ') return false;
pos = text.find_first_not_of(' ');
if (pos == std::string::npos)
count = text.length();
else count = pos;
if (count < 3) return false;
count = std::min((int)count, 255 - 32);
out.push_back(kDLE);
out.push_back(32 + count);
out.append(text.begin() + count, text.end());
text.swap(out);
return true;
}
/*
* dle will only occur at start.
*
*/
bool FileEntry::Uncompress(std::string& text)
{
std::string out;
unsigned c;
if (text.length() < 2) return false;
if (text[0] != kDLE) return false;
c = text[1];
if (c < 32) c = 32;
out.append(c - 32, ' ');
out.append(text.begin() + 2, text.end());
text.swap(out);
return true;
}

View File

@@ -1,111 +0,0 @@
#ifndef __PASCAL_FILEENTRY_H__
#define __PASCAL_FILEENTRY_H__
#include <Pascal/Entry.h>
#include <Pascal/Date.h>
#include <vector>
#include <string>
namespace Pascal {
class TextWriter;
class FileEntry : public Entry {
public:
static unsigned ValidName(const char *);
static bool Compress(std::string& text);
static bool Uncompress(std::string& text);
static FileEntryPointer Create(const char *name, unsigned fileKind);
static FileEntryPointer Open(void *vp);
virtual ~FileEntry();
unsigned fileSize();
unsigned lastByte() const { return _lastByte; }
const char *name() const { return _fileName; }
Date modification() const { return _modification; }
void setFileKind(unsigned kind);
int read(uint8_t *buffer, unsigned size, unsigned offset);
int write(const uint8_t *buffer, unsigned size, unsigned offset);
int write(TextWriter& text);
int truncate(unsigned newSize);
FileEntry(const char *name, unsigned fileKind);
FileEntry(void *vp);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
friend class VolumeEntry;
FileEntryPointer thisPointer()
{
return STATIC_POINTER_CAST(FileEntry, shared_from_this());
}
void setName(const char *name);
int truncateCommon(unsigned newSize);
void setFileSize(unsigned size);
unsigned _status;
unsigned _fileNameLength;
char _fileName[16];
unsigned _lastByte;
Date _modification;
unsigned _maxFileSize; // maximum file size.
// non-text files
unsigned dataFileSize();
int dataRead(uint8_t *buffer, unsigned size, unsigned offset);
// for text files.
void textInit();
unsigned textFileSize();
int textRead(uint8_t *buffer, unsigned size, unsigned offset);
unsigned textReadPage(unsigned block, uint8_t *in);
unsigned textDecodePage(unsigned block, uint8_t *out);
std::vector<unsigned> *_pageSize;
unsigned _fileSize;
};
}
#endif

View File

@@ -1,9 +0,0 @@
#ifndef __PASCAL_FILE_H__
#define __PASCAL_FILE_H__
#include <Pascal/Date.h>
#include <Pascal/Entry.h>
#include <Pascal/FileEntry.h>
#include <Pascal/VolumeEntry.h>
#endif

View File

@@ -1,104 +0,0 @@
#include <Pascal/TextWriter.h>
#include <Pascal/FileEntry.h>
#include <Common/Exception.h>
#include <string>
#include <cstring>
using namespace Pascal;
TextWriter::TextWriter()
{
_offset = 0;
_current = new uint8_t[1024];
_blocks.push_back(new uint8_t[1024]); // 1024k for editor scratch data.
std::memset(_blocks.back(), 0, 1024);
std::memset(_current, 0, 1024);
}
TextWriter::~TextWriter()
{
std::vector<uint8_t *>::iterator iter;
if (_current) delete[] _current;
for (iter = _blocks.begin(); iter != _blocks.end(); ++iter)
{
delete[] *iter;
}
}
unsigned TextWriter::blocks() const
{
if (_offset == 0) return _blocks.size() * 2;
if (_offset <= 512) return _blocks.size() * 2 + 1;
return _blocks.size() * 2 + 2;
}
void *TextWriter::data(unsigned block) const
{
unsigned offset = (block & 0x01) * 512;
unsigned halfBlock = block >> 1;
if (halfBlock < _blocks.size())
{
return _blocks[halfBlock] + offset;
}
if (halfBlock == _blocks.size())
{
if (offset > _offset) return NULL;
return _current + offset;
}
return NULL;
}
void TextWriter::writeLine(const char *line)
{
writeLine(line, std::strlen(line));
}
void TextWriter::writeLine(const char *line, unsigned length)
{
#undef __METHOD__
#define __METHOD__ "TextWriter::writeLine"
if (line == NULL) line = "";
std::string text(line, length);
if (length)
{
char c = text[length - 1];
if (c == 0x0a) text[length - 1] = 0x0d;
else if (c != 0x0d) text.push_back(0x0d);
FileEntry::Compress(text);
}
else
{
text.push_back(0x0d);
}
length = text.length();
if (length > 1024)
{
throw ::Exception(__METHOD__ ": String is too long.");
}
if (_offset + length > 1024)
{
_blocks.push_back(_current);
_offset = 0;
_current = new uint8_t[1024];
std::memset(_current, 0, 1024);
}
std::memcpy(_current + _offset, text.data(), length);
_offset += length;
}

View File

@@ -1,37 +0,0 @@
#ifndef __PASCAL_TEXTWRITER_H__
#define __PASCAL_TEXTWRITER_H__
#include <vector>
#include <stdint.h>
namespace Pascal {
class TextWriter {
public:
TextWriter();
~TextWriter();
unsigned blocks() const;
void *data(unsigned block) const;
void writeLine(const char *);
void writeLine(const char *, unsigned length);
private:
std::vector<uint8_t *> _blocks;
unsigned _offset;
uint8_t *_current;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +0,0 @@
#ifndef __PASCAL_VOLUMEENTRY_H__
#define __PASCAL_VOLUMEENTRY_H__
#include <Pascal/Entry.h>
#include <vector>
#include <Device/BlockDevice.h>
namespace Pascal {
class VolumeEntry : public Entry {
public:
static unsigned ValidName(const char *);
static VolumeEntryPointer Open(Device::BlockDevicePointer);
static VolumeEntryPointer Create(Device::BlockDevicePointer, const char *name);
//
virtual ~VolumeEntry();
const char *name() const { return _fileName; }
unsigned fileCount() const { return _fileCount; }
unsigned volumeBlocks() const { return _lastVolumeBlock; }
Pascal::Date lastBoot() const { return _lastBoot; }
unsigned freeBlocks(bool krunched = false) const;
unsigned maxContiguousBlocks() const;
bool canKrunch() const;
FileEntryPointer fileAtIndex(unsigned i) const;
FileEntryPointer fileByName(const char *name) const;
void *loadBlock(unsigned block);
void unloadBlock(unsigned block, bool dirty = false);
void readBlock(unsigned block, void *);
void writeBlock(unsigned block, void *);
void sync();
bool readOnly() { return _device->readOnly(); }
int unlink(const char *name);
int rename(const char *oldName, const char *newName);
int copy(const char *oldName, const char *newName);
FileEntryPointer create(const char *name, unsigned blocks);
int krunch();
VolumeEntry(Device::BlockDevicePointer, const char *name);
VolumeEntry(Device::BlockDevicePointer);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
friend class FileEntry;
VolumeEntry();
VolumeEntryPointer thisPointer()
{
return STATIC_POINTER_CAST(VolumeEntry, shared_from_this());
}
void init(void *);
void setParents();
uint8_t *readDirectoryHeader();
void writeDirectoryHeader(void *);
uint8_t *readBlocks(unsigned startingBlock, unsigned count);
void writeBlocks(void *buffer, unsigned startingBlock, unsigned count);
void writeEntry(FileEntry *e);
void writeEntry();
void calcMaxFileSize();
unsigned _fileNameLength;
char _fileName[8];
unsigned _lastVolumeBlock;
unsigned _fileCount;
unsigned _accessTime;
Pascal::Date _lastBoot;
std::vector<FileEntryPointer> _files;
unsigned _inodeGenerator;
Device::BlockDevicePointer _device;
Device::BlockCachePointer _cache;
};
}
#endif

View File

@@ -1,486 +0,0 @@
/*
* Disk.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#include "Disk.h"
#include "common.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <vector>
#include <Endian/Endian.h>
struct ucmp
{
bool operator()(unsigned a, unsigned b) const
{
return a < b;
}
};
using std::set;
using std::vector;
using namespace LittleEndian;
typedef set<unsigned, ucmp> uset;
Disk::Disk()
{
_blocks = 0;
}
Disk::~Disk()
{
}
Disk::Disk(Device::BlockDevicePointer device) :
_device(device)
{
_blocks = _device->blocks();
}
DiskPointer Disk::OpenFile(Device::BlockDevicePointer device)
{
DiskPointer disk(new Disk(device));
return disk;
}
// load the mini entry into the regular entry.
int Disk::Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee)
{
uint8_t buffer[BLOCK_SIZE];
int ok;
if (fork > 1) return -P8_INVALID_FORK;
if (f.storage_type != EXTENDED_FILE)
{
return fork == 0 ? 0 : -P8_INVALID_FORK;
}
ok = Read(f.key_pointer, buffer);
if (ok < 0) return ok;
ExtendedEntry e;
e.Load(buffer);
if (fork == 0)
{
f.storage_type = e.dataFork.storage_type;
f.key_pointer = e.dataFork.key_block;
f.eof = e.dataFork.eof;
f.blocks_used = e.dataFork.blocks_used;
}
else
{
f.storage_type = e.resourceFork.storage_type;
f.key_pointer = e.resourceFork.key_block;
f.eof = e.resourceFork.eof;
f.blocks_used = e.resourceFork.blocks_used;
}
if (ee) *ee = e;
return 0;
}
int Disk::Read(unsigned block, void *buffer)
{
if (block > _blocks) return -P8_INVALID_BLOCK;
_device->read(block, buffer);
return 1;
}
void *Disk::ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int *error)
{
#define SET_ERROR(x) if (error) *error = (x)
#define SET_SIZE(x) if (size) *size = (x)
SET_ERROR(0);
SET_SIZE(0);
if (fork != P8_DATA_FORK && fork != P8_RESOURCE_FORK)
{
SET_ERROR(-P8_INVALID_FORK);
return NULL;
}
uint8_t buffer[BLOCK_SIZE];
int ok;
uint32_t eof;
uint32_t alloc;
unsigned blocks;
unsigned storage_type;
unsigned key_block;
switch(f.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
if (fork != P8_DATA_FORK)
{
SET_ERROR(1);
return NULL;
}
storage_type = f.storage_type;
eof = f.eof;
key_block = f.key_pointer;
break;
case EXTENDED_FILE:
{
ok = Read(f.key_pointer, buffer);
if (ok < 0)
{
SET_ERROR(ok);
return NULL;
}
ExtendedEntry entry;
entry.Load(buffer);
if (fork == P8_DATA_FORK)
{
storage_type = entry.dataFork.storage_type;
eof = entry.dataFork.eof;
key_block = entry.dataFork.key_block;
}
else
{
storage_type = entry.resourceFork.storage_type;
eof = entry.resourceFork.eof;
key_block = entry.resourceFork.key_block;
}
}
break;
default:
SET_ERROR(-P8_INVALID_STORAGE_TYPE);
return NULL;
}
if (eof == 0)
{
SET_ERROR(1);
return NULL;
}
blocks = (eof + BLOCK_SIZE - 1) >> 9;
alloc = (eof + BLOCK_SIZE - 1) & (~BLOCK_SIZE);
uint8_t* data = new uint8_t[alloc];
switch (storage_type)
{
case SEEDLING_FILE:
ok = Read(key_block, data);
break;
case SAPLING_FILE:
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
break;
case TREE_FILE:
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
break;
default:
ok = false;
}
if (ok < 0)
{
SET_ERROR(ok);
delete[] data;
return NULL;
}
bzero(data + eof, alloc - eof);
SET_SIZE(eof);
return data;
}
int Disk::ReadFile(const FileEntry &f, void *buffer)
{
int blocks = (f.eof + BLOCK_SIZE - 1) >> 9;
int ok;
switch(f.storage_type)
{
case TREE_FILE:
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
break;
case SAPLING_FILE:
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
break;
case SEEDLING_FILE:
ok = Read(f.key_pointer, buffer);
break;
default:
return -P8_INVALID_STORAGE_TYPE;
}
if (ok >= 0)
{
bzero((uint8_t *)buffer + f.eof, (blocks << 9) - f.eof);
}
return ok;
}
int Disk::ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks)
{
if (level == 0)
{
// data level
if (block == 0) // sparse file
{
bzero(buffer, BLOCK_SIZE);
return 1;
}
return Read(block, buffer);
}
unsigned blockCount;
unsigned readSize;
unsigned first;
//unsigned last;
switch(level)
{
case 1:
first = (offset >> 9) & 0xff;
blockCount = 1;
readSize = BLOCK_SIZE;
offset = 0;
break;
case 2:
first = (offset >> 17) & 0xff;
blockCount = 256;
readSize = BLOCK_SIZE << 8;
offset &= 0x1ffff;
break;
default:
return -P8_INTERNAL_ERROR;
}
int ok;
uint8_t key[BLOCK_SIZE];
if (block) // not sparse.
{
ok = Read(block, key);
if (ok < 0 ) return ok;
}
else
{
// sparse -- zero it out so code below works w/o special cases.
bzero(key, BLOCK_SIZE);
}
for (unsigned i = first; blocks; i++)
{
// block pointers are split up since 8-bit indexing is limited to 256.
unsigned newBlock = (key[i]) | (key[256 + i] << 8);
unsigned b = std::min(blocks, blockCount);
ok = ReadIndex(newBlock, buffer, level - 1, offset, b);
if (ok < 0) return ok;
offset = 0;
buffer = ((char *)buffer) + readSize;
blocks -= b;
}
return blocks;
}
int Disk::ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files)
{
if (files) files->resize(0);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned prev;
unsigned next;
uset blocks;
unsigned block = 2;
blocks.insert(block);
ok = Read(block, buffer);
if (ok < 0) return ok;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
VolumeEntry v;
v.Load(buffer + 0x04);
if (v.storage_type != VOLUME_HEADER) return -P8_INVALID_STORAGE_TYPE;
if (volume) *volume = v;
if (!files) return 1;
if (v.file_count)
{
files->reserve(v.file_count);
//files->resize(v.file_count);
//unsigned count = 0;
unsigned index = 1; // skip the header.
for(;;)
{
//
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
{
unsigned offset = v.entry_length * index + 0x4;
FileEntry f;
f.Load(buffer + offset);
f.address = (block << 9) + offset;
files->push_back(f);
//if (++count == v.file_count) break;
}
index++;
if (index >= v.entries_per_block)
{
if (!next) break; // all done!
if (blocks.insert(next).second == false)
{
return -P8_CYCLICAL_BLOCK;
}
ok = Read(next, buffer);
if (ok < 0) return ok;
block = next;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
index = 0;
}
}
}
return 1;
}
int Disk::ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files)
{
if (files) files->resize(0);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned prev;
unsigned next;
// keep a list of blocks to prevent cyclical problems.
uset blocks;
blocks.insert(block);
ok = Read(block, buffer);
if (ok < 0) return ok;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
SubdirEntry v;
v.Load(buffer + 0x04);
if (v.storage_type != SUBDIR_HEADER) return -P8_INVALID_STORAGE_TYPE;
if (dir) *dir = v;
if (!files) return 1;
if (v.file_count)
{
files->reserve(v.file_count);
//files->resize(v.file_count);
//unsigned count = 0;
unsigned index = 1; // skip the header.
for(;;)
{
//
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
{
unsigned offset = v.entry_length * index + 0x4;
FileEntry f;
f.Load(buffer + offset);
f.address = (block << 9) + offset;
files->push_back(f);
//if (++count == v.file_count) break;
}
index++;
if (index >= v.entries_per_block)
{
if (!next) break; // all done!
if (blocks.insert(next).second == false)
{
return -P8_CYCLICAL_BLOCK;
}
ok = Read(next, buffer);
if (ok < 0) return ok;
block = next;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
index = 0;
}
}
}
return 1;
}

View File

@@ -1,84 +0,0 @@
/*
* Disk.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#ifndef __DISK_H__
#define __DISK_H__
#include <fcntl.h>
#include <stdint.h>
#include <vector>
#include <ProDOS/File.h>
#include <Device/BlockDevice.h>
#include <memory>
#include <Common/smart_pointers.h>
enum {
P8_OK = 0,
P8_INTERNAL_ERROR,
P8_INVALID_FORK,
P8_INVALID_BLOCK,
P8_INVALID_STORAGE_TYPE,
P8_CYCLICAL_BLOCK
};
enum {
P8_DATA_FORK = 0,
P8_RESOURCE_FORK = 1
};
/* flags */
enum {
P8_DOS_ORDER = 1,
P8_2MG = 2,
P8_DC42 = 4
};
class Disk;
typedef SHARED_PTR(Disk) DiskPointer;
class Disk {
public:
~Disk();
//static Disk *Open2MG(const char *file);
static DiskPointer OpenFile(Device::BlockDevicePointer device);
int Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee = NULL);
int Read(unsigned block, void *buffer);
int ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks);
int ReadFile(const FileEntry &f, void *buffer);
void *ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int * error);
int ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files);
int ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files);
private:
Disk();
Disk(Device::BlockDevicePointer device);
unsigned _blocks;
Device::BlockDevicePointer _device;
};
#endif

View File

@@ -1,132 +0,0 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
namespace ProDOS {
const char *Exception::errorString()
{
switch (error())
{
case badSystemCall:
return "Bad System Call";
case invalidPcount:
return "Invalid Parameter Count";
case gsosActive:
return "GS/OS Active";
case devNotFound:
return "Device Not Found";
case invalidDevNum:
return "Invalid Device Number";
case drvrBadReq:
return "Driver Bad Request";
case drvrBadCode:
return "Driver Bad Code";
case drvrBadParm:
return "Driver Bad Parameter";
case drvrNotOpen:
return "Driver Not Open";
case drvrPriorOpen:
return "Driver Prior Open";
case irqTableFull:
return "IRQ Table Full";
case drvrNoResrc:
return "Driver No Resource";
case drvrIOError:
return "Driver IO Error";
case drvrNoDevice:
return "Driver No Device";
case drvrBusy:
return "Driver Busy";
case drvrWrtProt:
return "Driver Write Protected";
case drvrBadCount:
return "Driver Bad Count";
case drvrBadBlock:
return "Driver Bad Block";
case drvrDiskSwitch:
return "Driver Disk Switch";
case drvrOffLine:
return "Driver Off Line";
case badPathSyntax:
return "Bad Path Syntax";
case invalidRefNum:
return "Invalid Ref Num";
case pathNotFound:
return "Path Not Found";
case volNotFound:
return "Volume Not Found";
case fileNotFound:
return "File Not Found";
case dupPathName:
return "Duplicate Path Name";
case volumeFull:
return "Volume Full";
case volDirFull:
return "Volume Directory Full";
case badFileFormat:
return "Bad File Format";
case badStoreType:
return "Bad Storage Type";
case eofEncountered:
return "End of File";
case outOfRange:
return "Out of Range";
case invalidAccess:
return "Invalid Access";
case buffTooSmall:
return "Buffer Too Small";
case fileBusy:
return "File Busy";
case dirError:
return "Directory Error";
case unknownVol:
return "Unknown Volume";
case paramRangeError:
return "Parameter Range Error";
case outOfMem:
return "Out of Memory";
case dupVolume:
return "Duplicate Volume";
case notBlockDev:
return "Not a Block Device";
case invalidLevel:
return "Invalid Level";
case damagedBitMap:
return "Damaged Bit Map";
case badPathNames:
return "Bad Path Names";
case notSystemFile:
return "Not a System File";
case osUnsupported:
return "OS Unsupported";
case stackOverflow:
return "Stack Overflow";
case dataUnavail:
return "Data Unavailable";
case endOfDir:
return "End Of Directory";
case invalidClass:
return "Invalid Class";
case resForkNotFound:
return "Resource Fork Not Found";
case invalidFSTID:
return "Invalid FST ID";
case devNameErr:
return "Device Name Error";
case resExistsErr:
return "Resource Exists Error";
case resAddErr:
return "Resource Add Error";
default:
return "";
}
return "";
}
}

View File

@@ -1,97 +0,0 @@
#ifndef __PRODOS_EXCEPTION_H__
#define __PRODOS_EXCEPTION_H__
#include <Common/Exception.h>
namespace ProDOS {
// ProDOS Errors
enum
{
badSystemCall = 0x01,
invalidPcount = 0x04,
gsosActive = 0x07,
devNotFound = 0x10,
invalidDevNum = 0x11,
drvrBadReq = 0x20,
drvrBadCode = 0x21,
drvrBadParm = 0x22,
drvrNotOpen = 0x23,
drvrPriorOpen = 0x24,
irqTableFull = 0x25,
drvrNoResrc = 0x26,
drvrIOError = 0x27,
drvrNoDevice = 0x28,
drvrBusy = 0x29,
drvrWrtProt = 0x2b,
drvrBadCount = 0x2c,
drvrBadBlock = 0x2d,
drvrDiskSwitch = 0x2e,
drvrOffLine = 0x2f,
badPathSyntax = 0x40,
invalidRefNum = 0x43,
pathNotFound = 0x44,
volNotFound = 0x45,
fileNotFound = 0x46,
dupPathName = 0x47,
volumeFull = 0x48,
volDirFull = 0x49,
badFileFormat = 0x4a,
badStoreType = 0x4b,
eofEncountered = 0x4c,
outOfRange = 0x4d,
invalidAccess = 0x4e,
buffTooSmall = 0x4f,
fileBusy = 0x50,
dirError = 0x51,
unknownVol = 0x52,
paramRangeError = 0x53,
outOfMem = 0x54,
dupVolume = 0x57,
notBlockDev = 0x58,
invalidLevel = 0x59,
damagedBitMap = 0x5a,
badPathNames = 0x5b,
notSystemFile = 0x5c,
osUnsupported = 0x5d,
stackOverflow = 0x5f,
dataUnavail = 0x60,
endOfDir = 0x61,
invalidClass = 0x62,
resForkNotFound = 0x63,
invalidFSTID = 0x64,
devNameErr = 0x67,
resExistsErr = 0x70,
resAddErr = 0x71
};
class Exception : public ::Exception {
public:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, int error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, int error) :
super(string, error)
{
}
}
#endif

View File

@@ -1,231 +0,0 @@
/*
* File.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#include <ProDOS/File.h>
#include <ProDOS/DateTime.h>
#include <Endian/Endian.h>
#include "common.h"
#include <cstring>
#include <cctype>
#include <cstdio>
#include <stdint.h>
using namespace LittleEndian;
bool FileEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
address = 0;
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(file_name, &cp[0x01], name_length);
file_name[name_length] = 0;
file_type = cp[0x10];
key_pointer = Read16(&cp[0x11]);
blocks_used = Read16(&cp[0x13]);
eof = Read24(&cp[0x15]);
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
//version = cp[0x1c];
//min_version = cp[0x1d];
unsigned xcase = Read16(&cp[0x1c]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) file_name[i] = tolower(file_name[i]);
mask = mask >> 1;
}
}
access = cp[0x1e];
aux_type = Read16(&cp[0x1f]);
last_mod = ProDOS::DateTime(Read16(&cp[0x21]), Read16(&cp[0x23]));
header_pointer = Read16(&cp[0x25]);
return true;
}
bool ExtendedEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prodos technote #25.
// offset 0 - mini entry for data fork
dataFork.storage_type = cp[0x00] & 0x0f;
dataFork.key_block = Read16(&cp[0x01]);
dataFork.blocks_used = Read16(&cp[0x03]);
dataFork.eof = Read24(&cp[0x05]);
// offset 256 - mini entry for resource fork.
resourceFork.storage_type = cp[256 + 0x00] & 0x0f;
resourceFork.key_block = Read16(&cp[256 + 0x01]);
resourceFork.blocks_used = Read16(&cp[256 + 0x03]);
resourceFork.eof = Read24(&cp[256 + 0x05]);
// xFInfo may be missing.
bzero(FInfo, sizeof(FInfo));
bzero(xFInfo, sizeof(xFInfo));
// size must be 18.
unsigned size;
unsigned entry;
for (unsigned i = 0; i < 2; i++)
{
unsigned ptr = i == 0 ? 8 : 26;
size = cp[ptr];
if (size != 18) continue;
entry = cp[ptr + 1];
switch(entry)
{
case 1:
memcpy(FInfo, &cp[ptr + 2], 16);
break;
case 2:
memcpy(xFInfo, &cp[ptr + 2], 16);
break;
}
}
//
return true;
}
bool VolumeEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prev_block = load16(&cp[0x00]);
//next_block = load16(&cp[0x02]);
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(volume_name, &cp[0x01], name_length);
volume_name[name_length] = 0;
// 0x14--0x1b reserved
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
last_mod = ProDOS::DateTime(Read16(&cp[0x12]), Read16(&cp[0x14]));
if (last_mod == 0) last_mod = creation;
//version = cp[0x1c];
//min_version = cp[0x1d];
unsigned xcase = Read16(&cp[0x16]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) volume_name[i] = tolower(volume_name[i]);
mask = mask >> 1;
}
}
access = cp[0x1e];
entry_length = cp[0x1f];
entries_per_block = cp[0x20];
file_count = Read16(&cp[0x21]);
bit_map_pointer = Read16(&cp[0x23]);
total_blocks = Read16(&cp[0x25]);
return true;
}
bool SubdirEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prev_block = load16(&cp[0x00]);
//next_block = load16(&cp[0x02]);
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(subdir_name, &cp[0x01], name_length);
subdir_name[name_length] = 0;
// 0x14 should be $14.
// 0x145-0x1b reserved
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
//version = cp[0x1c];
//min_version = cp[0x1d];
/*
unsigned xcase = load16(&cp[0x1c]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) subdir_name[i] = tolower(subdir_name[i]);
mask = mask >> 1;
}
}
*/
access = cp[0x1e];
entry_length = cp[0x1f];
entries_per_block = cp[0x20];
file_count = Read16(&cp[0x21]);
parent_pointer = Read16(&cp[0x23]);
parent_entry = cp[0x25];
parent_entry_length = cp[0x26];
return true;
}

View File

@@ -1,133 +0,0 @@
/*
* File.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#ifndef __PRODOS_FILE_H__
#define __PRODOS_FILE_H__
#include <time.h>
#include <stdint.h>
enum {
DELETED_FILE = 0,
SEEDLING_FILE = 1,
SAPLING_FILE = 2,
TREE_FILE = 3,
PASCAL_FILE = 4,
EXTENDED_FILE = 5,
DIRECTORY_FILE = 0x0d,
SUBDIR_HEADER = 0x0e,
VOLUME_HEADER = 0x0f
};
enum {
FILE_ENTRY_SIZE = 0x27,
};
enum {
ACCESS_DESTROY = 0x80,
ACCESS_RENAME = 0x40,
ACCESS_MODIFIED = 0x20,
ACCESS_WRITE = 0x02,
ACCRESS_READ = 0x01
};
class FileEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char file_name[15 + 1];
unsigned file_type;
unsigned key_pointer;
unsigned blocks_used;
uint32_t eof;
time_t creation;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned aux_type;
time_t last_mod;
unsigned header_pointer;
uint32_t address;
};
struct MiniEntry {
unsigned storage_type;
unsigned key_block;
unsigned blocks_used;
uint32_t eof;
};
class ExtendedEntry {
public:
bool Load(const void *data);
MiniEntry dataFork;
MiniEntry resourceFork;
uint8_t FInfo[16];
uint8_t xFInfo[16];
};
class VolumeEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char volume_name[15+1];
time_t creation;
time_t last_mod;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned entry_length;
unsigned entries_per_block;
unsigned file_count;
unsigned bit_map_pointer;
unsigned total_blocks;
friend class DirIter;
};
class SubdirEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char subdir_name[15+1];
time_t creation;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned entry_length;
unsigned entries_per_block;
unsigned file_count;
unsigned parent_pointer;
unsigned parent_entry;
unsigned parent_entry_length;
};
#endif

7
ProDOS/Makefile Normal file
View File

@@ -0,0 +1,7 @@
CC = g++
CPPFLAGS += -g -Wall -I../
all : DateTime.o
DateTime.o : DateTime.cpp DateTime.h

View File

@@ -1,18 +0,0 @@
/*
* common.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/20/08.
*
*/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdint.h>
#define BLOCK_SIZE 512
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

13
ProFUSE/Exception.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <ProFUSE/Exception.h>
using namespace ProFUSE;
Exception::~Exception() throw()
{
}
const char *Exception::what()
{
return _string.c_str();
}

92
ProFUSE/Exception.h Normal file
View File

@@ -0,0 +1,92 @@
#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__
#include <string>
#include <exception>
namespace ProFUSE {
class Exception : public std::exception
{
public:
Exception(const char *cp);
Exception(const std::string &str);
virtual ~Exception() throw ();
virtual const char *what();
int error() const { return _error; }
protected:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
private:
int _error;
std::string _string;
};
class POSIXException : public Exception {
public:
POSIXException(const char *cp, int error);
POSIXException(const std::string& string, int error);
};
class ProDOSException : public Exception {
public:
ProDOSException(const char *cp, int error);
ProDOSException(const std::string& string, int error);
};
inline Exception::Exception(const char *cp):
_error(0),
_string(cp)
{
}
inline Exception::Exception(const std::string& string):
_error(0),
_string(string)
{
}
inline Exception::Exception(const char *cp, int error):
_error(error),
_string(cp)
{
}
inline Exception::Exception(const std::string& string, int error):
_error(error),
_string(string)
{
}
inline POSIXException::POSIXException(const char *cp, int error) :
Exception(cp, error)
{
}
inline POSIXException::POSIXException(const std::string& string, int error) :
Exception(string, error)
{
}
inline ProDOSException::ProDOSException(const char *cp, int error) :
Exception(cp, error)
{
}
inline ProDOSException::ProDOSException(const std::string& string, int error) :
Exception(string, error)
{
}
}
#endif

View File

@@ -1,5 +1,6 @@
#include "Lock.h"
#include <ProfUSE/Lock.h>
using namespace ProFUSE;
Lock::Lock()
{
@@ -24,4 +25,4 @@ void Lock::unlock()
bool Lock::tryLock()
{
return pthread_mutex_trylock(&_mutex) == 0;
}
}

View File

@@ -3,6 +3,7 @@
#include <pthread.h>
namespace ProFUSE {
class Lock {
public:
@@ -27,4 +28,6 @@ private:
};
}
#endif

9
ProFUSE/Makefile Normal file
View File

@@ -0,0 +1,9 @@
CC = g++
CPPFLAGS += -g -Wall -I../
all : Exception.o Lock.o
Exception.o : Exception.cpp Exception.h
Lock.o : Lock.cpp Lock.h

View File

@@ -3,6 +3,7 @@
#include <cstddef>
namespace ProFUSE {
template <class T>
class auto_array
@@ -97,6 +98,7 @@ private:
};
#endif
}
#endif

View File

@@ -1,11 +0,0 @@
# profuse
ProDOS file system for FUSE
## End of Life Notice
This software, while usable, is EOL. No further updates are expected.
ProFUSE 2.0, featuring read/write support for ProDOS and HFS, is available as part of
[Golden Gate](http://golden-gate.ksherlock.com)
See the Wiki for details about the ProDOS file system.

366
apfm.cpp Normal file
View File

@@ -0,0 +1,366 @@
/*
* FileMan utilities.
*
* L - list dir
* E -
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <unistd.h>
#include <strings.h>
#include <Pascal/File.h>
#include <Pascal/Date.h>
#include <Device/BlockDevice.h>
#include <Device/DiskCopy42Image.h>
const char *MonthName(unsigned m)
{
static const char *months[] = {
"",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
if (m > 12) return "";
return months[m];
}
const char *FileType(unsigned ft)
{
static const char *types[] = {
"Unknown",
"Badblocks",
"Codefile",
"Textfile",
"Infofile",
"Datafile",
"Graffile",
"Fotofile",
"SecureDir"
};
if (ft < 8) return types[ft];
return "";
}
void printUnusedEntry(unsigned block, unsigned size)
{
std::printf("< UNUSED > %4u %4u\n", size, block);
}
void printFileEntry(Pascal::FileEntry *e, bool extended)
{
Pascal::Date dt = e->modification();
if (extended)
{
std::printf("%-15s %4u %2u-%s-%2u %5u %5u %s\n",
e->name(),
e->blocks(),
dt.day(),
MonthName(dt.month()),
dt.year() % 100,
e->firstBlock(),
e->lastByte(),
FileType(e->fileKind())
);
}
else
{
std::printf("%-15s %4u %2u-%s-%2u\n",
e->name(),
e->blocks(),
dt.day(),
MonthName(dt.month()),
dt.year() % 100
);
}
}
int action_ls(int argc, char **argv, Pascal::VolumeEntry *volume)
{
//TODO -- check for -l flag.
bool extended = false;
unsigned fileCount = volume->fileCount();
unsigned used = volume->blocks();
unsigned max = 0;
unsigned volumeSize = volume->volumeBlocks();
unsigned lastBlock = volume->lastBlock();
int ch;
std::fprintf(stdout, "%s:\n", volume->name());
argv[0] = "afpm ls";
while ((ch = ::getopt(argc, argv, "l")) != -1)
{
switch(ch)
{
case 'l':
extended = true;
break;
}
}
argc -= optind;
argv += optind;
for (unsigned i = 0; i < fileCount; ++i)
{
Pascal::FileEntry *e = volume->fileAtIndex(i);
if (!e) continue;
if (lastBlock != e->firstBlock())
{
unsigned size = e->firstBlock() - lastBlock;
max = std::max(max, size);
if (extended)
{
printUnusedEntry(lastBlock, size);
}
}
printFileEntry(e, extended);
lastBlock = e->lastBlock();
used += e->blocks();
}
if (extended && (lastBlock != volumeSize))
{
unsigned size = volumeSize - lastBlock;
max = std::max(max, size);
printUnusedEntry(lastBlock, size);
}
std::fprintf(stdout,
"%u/%u files<listed/in-dir>, "
"%u blocks used, "
"%u unused, "
"%u in largest\n",
fileCount, fileCount,
used,
volumeSize - used,
max
);
return 0;
}
int action_cat(unsigned argc, char **argv, Pascal::VolumeEntry *volume)
{
// cat file1, file2...
argv[0] = "afpm cat";
if (argc < 2)
{
std::fprintf(stderr, "apfm cat: Please specify one or more files.");
return 1;
}
for (unsigned i = 0; i < argc; ++i)
{
const char *fname = argv[i];
unsigned fileSize;
unsigned offset;
uint8_t buffer[512];
Pascal::FileEntry *e = NULL;
// find it...
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
e = volume->fileAtIndex(i);
if (::strcasecmp(e->name(), fname) == 0) break;
e = NULL;
}
if (!e)
{
std::fprintf(stderr, "apfm cat: %s: no such file.\n", fname);
continue;
}
fileSize = e->fileSize();
offset = 0;
while (offset < fileSize)
{
unsigned count = std::min(512u, fileSize - offset);
e->read(buffer, count, offset);
std::fwrite(buffer, count, 1, stdout);
offset += count;
}
}
return 0;
}
int action_cp(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_mv(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_rm(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_krunch(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
void usage()
{
std::printf(
"Pascal File Manager v 0.0\n\n"
"Usage: fileman [-h] [-f format] diskimage action ...\n"
"Options:\n"
" -h Show usage information.\n"
" -f format Specify disk format. Valid values are:\n"
" po: ProDOS order disk image\n"
" do: DOS Order disk image\n"
"\n"
"Actions:\n"
" cat\n"
" cp\n"
" krunch\n"
" ls\n"
" mv\n"
" rm\n"
);
}
int main(int argc, char **argv)
{
std::auto_ptr<Pascal::VolumeEntry> volume;
std::auto_ptr<Device::BlockDevice> device;
unsigned fmt = 0;
int c;
// getop stops at first non '-' arg so it will not affect action flags.
while ((c = ::getopt(argc, argv, "f:h")) != -1)
{
std::printf("%c\n", c);
switch(c)
{
case 'f':
fmt = Device::DiskImage::ImageType(optarg);
if (!fmt)
{
std::fprintf(stderr, "Error: Invalid file format: ``%s''.\n",
optarg);
}
break;
case 'h':
case '?':
case ':':
usage();
return c == 'h' ? 0 : 1;
}
}
argc -= optind;
argv += optind;
optreset = 1;
optind = 1;
if (argc != 2)
{
usage();
return 0;
}
const char *file = argv[0];
const char *action = argv[1];
if (!fmt) fmt = Device::DiskImage::ImageType(optarg, 'PO__');
try {
switch(fmt)
{
case 'DO__':
device.reset( new Device::DOSOrderDiskImage(file, true) );
break;
case 'PO__':
device.reset( new Device::ProDOSOrderDiskImage(file, true) );
break;
case 'DC42':
device.reset( new Device::DiskCopy42Image(file, true) );
break;
default:
std::fprintf(stderr, "Unable to determine format. Please use -f flag.\n");
return 2;
}
volume.reset( new Pascal::VolumeEntry(device.get()));
device.release();
if (!::strcasecmp("cat", action)) return action_cat(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("cp", action)) return action_cp(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("krunch", action)) return action_krunch(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("ls", action)) return action_ls(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("mv", action)) return action_mv(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("rm", action)) return action_rm(argc - 1, argv + 1, volume.get());
usage();
return 3;
}
catch (ProFUSE::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", strerror(e.error()));
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,314 +0,0 @@
#include <memory>
#include <new>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include <Device/BlockDevice.h>
#include <Device/RawDevice.h>
#include <Common/Exception.h>
#include <Pascal/Pascal.h>
#include <File/File.h>
#include <File/MappedFile.h>
using namespace Pascal;
using namespace Device;
#define NEWFS_VERSION "0.1"
bool yes_or_no()
{
int ch, first;
(void)fflush(stderr);
first = ch = getchar();
while (ch != '\n' && ch != EOF)
ch = getchar();
return (first == 'y' || first == 'Y');
}
/*
* \d+ by block
* \d+[Kk] by kilobyte
* \d+[Mm] by megabyte
*/
unsigned parseBlocks(const char *cp)
{
unsigned long blocks = 0;
char *mod;
errno = 0;
blocks = std::strtoul(cp, &mod, 10);
if (errno) return -1;
if (mod == cp) return -1;
if (blocks > 0xffff) return -1;
if (mod)
{
switch(*mod)
{
case 0:
break;
case 'm': // 1m = 1024*1024b = 2048 blocks
case 'M':
blocks *= 2048;
break;
case 'k': // 1k = 1024b = 2 blocks
case 'K':
blocks *= 2;
break;
}
if (blocks > 0xffff) return -1;
}
return (unsigned)blocks;
}
// return the basename, without an extension.
std::string filename(const std::string& src)
{
unsigned start;
unsigned end;
if (src.empty()) return std::string("");
start = end = 0;
for(unsigned i = 0, l = src.length(); i < l; ++i)
{
char c = src[i];
if (c == '/') start = end = i + 1;
if (c == '.') end = i;
}
if (start == src.length()) return std::string("");
if (start == end) return src.substr(start);
return src.substr(start, end - start);
}
void usage()
{
std::printf("newfs_pascal %s\n", NEWFS_VERSION);
std::printf("\n");
std::printf("newfs_pascal [-v volume_name] [-s size] [-f format] file\n");
std::printf("\n");
std::printf(" -v volume_name Specify the volume name.\n"
" Default is Untitled.\n"
" -s size Specify size in blocks.\n"
" Default is 1600 blocks (800K)\n"
" -b bootfile Specify a file that contains the boot block\n"
" -f format Specify the disk image format. Valid values are:\n"
" 2img Universal Disk Image\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
);
}
int main(int argc, char **argv)
{
unsigned blocks = 1600;
std::string volumeName;
std::string fileName;
std::string bootFile;
int format = 0;
const char *fname;
int c;
while ( (c = ::getopt(argc, argv, "hf:s:v:")) != -1)
{
switch(c)
{
case 'h':
default:
usage();
return c == 'h' ? 0 : 1;
break;
case 'v':
volumeName = optarg;
// make sure it's legal.
if (!VolumeEntry::ValidName(optarg))
{
std::fprintf(stderr, "Error: `%s' is not a valid Pascal volume name.\n", optarg);
return 0x40;
}
break;
case 's':
blocks = parseBlocks(optarg);
if (blocks > 0xffff)
{
std::fprintf(stderr, "Error: `%s' is not a valid disk size.\n", optarg);
return 0x5a;
}
break;
case 'f':
format = Device::BlockDevice::ImageType(optarg);
if (format == 0)
{
std::fprintf(stderr, "Error: `%s' is not a supported disk image format.\n", optarg);
return -1;
}
break;
case 'b':
bootFile = optarg;
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1)
{
usage();
return -1;
}
fname = argv[0];
fileName = argv[0];
try
{
struct stat st;
bool rawDevice = false;
BlockDevicePointer device;
VolumeEntryPointer volume;
// Check for block device. if so, verify.
// if file exists, verify before overwrite.
std::memset(&st, 0, sizeof(st));
if (::stat(fname, &st) == 0)
{
if (S_ISBLK(st.st_mode))
{
fprintf(stderr, "`%s' is a raw device. Are you sure you want to initialize it? ", fname);
if (!yes_or_no()) return -1;
device = RawDevice::Open(fname, File::ReadWrite);
blocks = device->blocks();
rawDevice = true;
if (blocks > 0xffff)
{
std::fprintf(stderr, "Error: device is too large.\n");
return 0x5a;
}
}
else
{
// file exists, verify we want to destroy it.
fprintf(stderr, "`%s' already exists. Are you sure you want to overwrite it? ", fname);
if (!yes_or_no()) return -1;
}
}
// generate a filename.
if (volumeName.empty())
{
if (!rawDevice)
volumeName = filename(fileName);
if (volumeName.empty() || !VolumeEntry::ValidName(volumeName.c_str()))
volumeName = "PASCAL";
}
if (!rawDevice)
device = BlockDevice::Create(fname, volumeName.c_str(), blocks, format);
if (!device.get())
{
std::fprintf(stderr, "Error: Unsupported diskimage format.\n");
return -1;
}
if (!bootFile.empty())
{
MappedFile bf(bootFile.c_str(), File::ReadOnly, std::nothrow);
if (!bf.isValid())
{
std::fprintf(stderr, "Warning: unable to open boot file `%s'.\n", bootFile.c_str());
}
else
{
size_t length = bf.length();
// either 1 or 2 blocks.
if (length == 512)
{
device->write(0, bf.address());
}
else if (length == 1024)
{
device->write(0, bf.address());
device->write(1, (uint8_t*)bf.address() + 512);
}
else
{
std::fprintf(stderr, "Warning: boot file must be 512 or 1024 bytes.\n");
}
}
}
volume = VolumeEntry::Create(device, volumeName.c_str());
}
catch (::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", ::strerror(e.error()));
return -2;
}
return 0;
}

View File

@@ -1,374 +0,0 @@
/*
* main.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/24/08.
*
*/
/*
#define __FreeBSD__ 10
#define _FILE_OFFSET_BITS 64
#define __DARWIN_64_BIT_INO_T 1
#define _REENTRANT
#define _POSIX_C_SOURCE 200112L
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <vector>
#include <string>
#include <Device/BlockDevice.h>
#include "profuse.h"
using std::vector;
using std::string;
/*
* globals variables.
*
*/
std::string fDiskImage;
DiskPointer disk;
VolumeEntry volume;
bool validProdosName(const char *name)
{
// OS X looks for hidden files that don't exist (and aren't legal prodos names)
// most are not legal prodos names, so this filters them out easily.
// [A-Za-z][0-9A-Za-z.]{0,14}
if (!isalpha(*name)) return false;
unsigned i;
for(i = 1; name[i]; i++)
{
char c = name[i];
if (c == '.' || isalnum(c)) continue;
return false;
}
return i < 16;
}
static struct fuse_lowlevel_ops prodos_oper;
enum {
PRODOS_OPT_HELP,
PRODOS_OPT_VERSION,
PRODOS_OPT_WRITE,
PRODOS_OPT_FORMAT,
PRODOS_OPT_VERBOSE
};
struct options {
char *format;
int readOnly;
int readWrite;
int verbose;
int debug;
} options;
#define PRODOS_OPT_KEY(T, P, V) {T, offsetof(struct options, P), V}
static struct fuse_opt prodos_opts[] = {
FUSE_OPT_KEY("-h", PRODOS_OPT_HELP),
FUSE_OPT_KEY("--help", PRODOS_OPT_HELP),
FUSE_OPT_KEY("-V", PRODOS_OPT_VERSION),
FUSE_OPT_KEY("--version", PRODOS_OPT_VERSION),
PRODOS_OPT_KEY("-v", verbose, 1),
PRODOS_OPT_KEY("-w", readWrite, 1),
PRODOS_OPT_KEY("rw", readWrite, 1),
PRODOS_OPT_KEY("-d", debug, 1),
PRODOS_OPT_KEY("--format=%s", format, 0),
PRODOS_OPT_KEY("format=%s", format, 0),
{0, 0, 0}
};
static void usage()
{
fprintf(stderr, "profuse [options] disk_image [mountpoint]\n"
"Options:\n"
" -d debug\n"
" -r readonly\n"
" -w mount writable [not yet]\n"
" -v verbose\n"
" --format=format specify the disk image format. Valid values are:\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
#ifdef HAVE_NUFX
" sdk ShrinkIt Disk Image\n"
#endif
" 2img Universal Disk Image\n"
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
" -o opt1,opt2... other mount parameters.\n"
);
}
static int prodos_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
switch(key)
{
case PRODOS_OPT_HELP:
usage();
exit(0);
break;
case PRODOS_OPT_VERSION:
// TODO
exit(1);
break;
case FUSE_OPT_KEY_NONOPT:
// first arg is the disk image.
if (fDiskImage.empty())
{
fDiskImage = arg;
return 0;
}
return 1;
}
return 1;
}
#ifdef __APPLE__
// create a dir in /Volumes/diskname.
bool make_mount_dir(string name, string &path)
{
path = "";
if (name.find('/') != string::npos) return false;
if (name.find('\\') != string::npos) return false;
if (name.find(':') != string::npos) return false;
path = "";
path = "/Volumes/" + name;
rmdir(path.c_str());
if (mkdir(path.c_str(), 0777) == 0) return true;
for (unsigned i = 0; i < 26; i++)
{
path = "/Volumes/" + name + " " + (char)('a' + i);
rmdir(path.c_str());
if (mkdir(path.c_str(), 0777) == 0) return true;
}
path = "";
return false;
}
#endif
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
struct options options;
unsigned format = 0;
int foreground = false;
int multithread = false;
#if __APPLE__
string mountpath;
#endif
std::memset(&prodos_oper, 0, sizeof(prodos_oper));
std::memset(&options, 0, sizeof(options));
prodos_oper.listxattr = prodos_listxattr;
prodos_oper.getxattr = prodos_getxattr;
prodos_oper.opendir = prodos_opendir;
prodos_oper.releasedir = prodos_releasedir;
prodos_oper.readdir = prodos_readdir;
prodos_oper.lookup = prodos_lookup;
prodos_oper.getattr = prodos_getattr;
prodos_oper.open = prodos_open;
prodos_oper.release = prodos_release;
prodos_oper.read = prodos_read;
prodos_oper.statfs = prodos_statfs;
// scan the argument list, looking for the name of the disk image.
if (fuse_opt_parse(&args, &options , prodos_opts, prodos_opt_proc) == -1)
exit(1);
if (fDiskImage.empty())
{
usage();
exit(1);
}
// default prodos-order disk image.
if (options.format)
{
format = Device::BlockDevice::ImageType(options.format);
if (!format)
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
}
try {
Device::BlockDevicePointer device;
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
if (!device)
{
std::fprintf(stderr, "Error: Unknown or unsupported device type.\n");
exit(1);
}
disk = Disk::OpenFile(device);
if (!disk)
{
fprintf(stderr, "Unable to mount disk %s\n", fDiskImage.c_str());
exit(1);
}
}
catch (::Exception &e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", e.errorString());
return -1;
}
disk->ReadVolume(&volume, NULL);
#ifdef __APPLE__
{
// Macfuse supports custom volume names (displayed in Finder)
string str="-ovolname=";
str += volume.volume_name;
fuse_opt_add_arg(&args, str.c_str());
}
#endif
// use 512byte blocks.
#if __APPLE__
fuse_opt_add_arg(&args, "-oiosize=512");
#endif
do {
if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) break;
#ifdef __APPLE__
if (mountpoint == NULL || *mountpoint == 0)
{
if (make_mount_dir(volume.volume_name, mountpath))
mountpoint = (char *)mountpath.c_str();
}
#endif
foreground = options.debug;
if (mountpoint == NULL || *mountpoint == 0)
{
fprintf(stderr, "no mount point\n");
break;
}
if ( (ch = fuse_mount(mountpoint, &args)) != NULL)
{
struct fuse_session *se;
se = fuse_lowlevel_new(&args, &prodos_oper, sizeof(prodos_oper), NULL);
if (se != NULL) do {
err = fuse_daemonize(foreground);
if (err < 0 ) break;
err = fuse_set_signal_handlers(se);
if (err < 0) break;
fuse_session_add_chan(se, ch);
if (multithread) err = fuse_session_loop_mt(se);
else err = fuse_session_loop(se);
fuse_remove_signal_handlers(se);
fuse_session_remove_chan(ch);
} while (false);
if (se) fuse_session_destroy(se);
fuse_unmount(mountpoint, ch);
}
} while (false);
fuse_opt_free_args(&args);
disk.reset();
#ifdef __APPLE__
if (mountpath.size()) rmdir(mountpath.c_str());
#endif
return err ? 1 : 0;
}

View File

@@ -1,56 +0,0 @@
/*
* profuse.h
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#ifndef __PROFUSE_H__
#define __PROFUSE_H__
#include <ProDOS/File.h>
#include <ProDOS/Disk.h>
#include <ProDOS/common.h>
#define FUSE_USE_VERSION 27
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#undef ERROR
#define ERROR(cond,errno) if ( (cond) ){ fuse_reply_err(req, errno); return; }
extern DiskPointer disk;
bool validProdosName(const char *name);
// xattr
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off);
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size);
void prodos_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size);
//dirent
void prodos_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
// stat
void prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_lookup(fuse_req_t req, fuse_ino_t parent, const char *name);
void prodos_statfs(fuse_req_t req, fuse_ino_t ino);
// file io.
void prodos_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
#endif

View File

@@ -1,132 +0,0 @@
/*
* profuse_dirent.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <strings.h>
#include <cstdio>
#include <cerrno>
#include <algorithm>
#include <string>
#include <vector>
using std::string;
using std::vector;
#pragma mark Directory Functions
/*
* when the directory is opened, we load the volume/directory and store the FileEntry vector into
* fi->fh.
*
*/
void prodos_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "opendir: %u\n", (unsigned)ino);
// verify it's a directory/volume here?
uint8_t buffer[BLOCK_SIZE];
vector<FileEntry> files;
bool ok;
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
ERROR(ok < 0, EIO)
if (ino == 1)
{
VolumeEntry v;
v.Load(buffer + 0x04);
ok = disk->ReadVolume(&v, &files);
ERROR(ok < 0, EIO)
}
else
{
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
ERROR(e.storage_type != DIRECTORY_FILE, ENOTDIR)
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
ERROR(ok < 0, EIO);
}
// copy the vector contents to a vector *.
vector<FileEntry> *fp = new vector<FileEntry>();
files.swap(*fp);
fi->fh = (uint64_t)fp;
fuse_reply_open(req, fi);
}
void prodos_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr,"releasedir: %u\n", (unsigned)ino);
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
if (files) delete files;
fuse_reply_err(req, 0);
}
void prodos_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
struct stat st;
fprintf(stderr, "readdir %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
// TODO -- add "." and ".." entries...
// if the offset >= number of entries, get out.
if (!files || files->size() <= off)
{
fprintf(stderr, "fuse_reply_buf(req, NULL, 0)\n");
fuse_reply_buf(req, NULL, 0);
return;
}
// now some dirent info...
bzero(&st, sizeof(st));
// only mode and ino are used.
char *buffer = new char[size];
unsigned count = files->size();
unsigned current_size = 0;
for (unsigned i = off; i < count; ++i)
{
FileEntry &f = (*files)[i];
st.st_mode = f.storage_type == DIRECTORY_FILE ? S_IFDIR | 0555 : S_IFREG | 0444;
st.st_ino = f.address;
unsigned entry_size = fuse_add_direntry(req, NULL, 0, f.file_name, NULL, 0);
if (entry_size + current_size >= size) break;
fuse_add_direntry(req, (char *)buffer + current_size, size, f.file_name, &st, i + 1);
current_size += entry_size;
}
fuse_reply_buf(req, buffer, current_size);
delete []buffer;
}

View File

@@ -1,136 +0,0 @@
/*
* profuse_file.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <cerrno>
#include <cstdio>
#pragma mark Read Functions
void prodos_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "open: %u\n", (unsigned)ino);
uint8_t buffer[BLOCK_SIZE];
int ok;
FileEntry *e = NULL;
ERROR(ino == 1, EISDIR)
ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
e = new FileEntry();
e->Load(buffer + (ino & 0x1ff));
if (e->storage_type == EXTENDED_FILE)
{
ok = disk->Normalize(*e, 0);
if (ok < 0)
{
delete e;
ERROR(true, EIO)
}
}
// EXTENDED_FILE already handled (it would be an error here.)
switch(e->storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
break;
//case PASCAL_FILE: //?
case DIRECTORY_FILE:
delete e;
ERROR(true, EISDIR)
break;
default:
ERROR(true, EIO)
}
if ( (fi->flags & O_ACCMODE) != O_RDONLY)
{
delete e;
ERROR(true, EACCES);
}
fi->fh = (uint64_t)e;
fuse_reply_open(req, fi);
}
void prodos_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "release: %u\n", (unsigned)ino);
FileEntry *e = (FileEntry *)fi->fh;
if (e) delete e;
fuse_reply_err(req, 0);
}
void prodos_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
fprintf(stderr, "read: %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
FileEntry *e = (FileEntry *)fi->fh;
ERROR(e == NULL, EIO)
if (off >= e->eof)
{
fuse_reply_buf(req, NULL, 0);
return;
}
unsigned level = 0;
switch(e->storage_type)
{
case TREE_FILE:
level = 2;
break;
case SAPLING_FILE:
level = 1;
break;
case SEEDLING_FILE:
level = 0;
break;
}
// currently, reading is done on a block basis.
// experimentally, fuse reads the entire file
// this may not hold for larger files.
// TODO -- error if size + off > eof.
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
int ok;
uint8_t *buffer = new uint8_t[blocks << 9];
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e->key_pointer, level, (int)off, (int)blocks);
ok = disk->ReadIndex(e->key_pointer, buffer, level, off, blocks);
if (ok < 0)
{
fuse_reply_err(req, EIO);
}
else
{
fuse_reply_buf(req, (const char *)buffer + (off & 0x1ff), size);
}
delete []buffer;
}

View File

@@ -1,253 +0,0 @@
/*
* prodos_stat.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#pragma mark Stat Functions
#include "profuse.h"
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <vector>
#include <sys/stat.h>
#include <sys/statvfs.h>
using std::vector;
int prodos_stat(FileEntry& e, struct stat *st)
{
uint8_t buffer[BLOCK_SIZE];
int ok;
if (e.storage_type == EXTENDED_FILE)
{
ok = disk->Normalize(e, 0);
if (ok < 0) return ok;
}
st->st_blksize = BLOCK_SIZE;
st->st_ctime = e.creation;
#ifdef HAVE_STAT_BIRTHTIME
st->st_birthtime = e.creation;
#endif
st->st_mtime = e.last_mod;
st->st_atime = e.last_mod;
st->st_nlink = 1;
st->st_mode = 0444 | S_IFREG;
st->st_size = e.eof;
if (e.storage_type == DIRECTORY_FILE)
{
ok = disk->Read(e.key_pointer, buffer);
if (ok < 0) return -1;
SubdirEntry se;
se.Load(buffer + 0x04);
if (se.storage_type != SUBDIR_HEADER) return -1;
st->st_mode = S_IFDIR | 0555;
st->st_size = BLOCK_SIZE;
st->st_nlink = se.file_count + 1;
return 0;
}
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
//case PASCAL_FILE:
break;
default:
return -1;
}
return 0;
}
int prodos_stat(const VolumeEntry &v, struct stat *st)
{
if (v.storage_type != VOLUME_HEADER) return -1;
st->st_mode = S_IFDIR | 0555;
st->st_ctime = v.creation;
#ifdef HAVE_STAT_BIRTHTIME
st->st_birthtime = v.creation;
#endif
st->st_mtime = v.last_mod;
st->st_atime = v.last_mod;
st->st_nlink = v.file_count + 1;
st->st_size = BLOCK_SIZE;
st->st_blksize = BLOCK_SIZE;
return 1;
}
void prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
uint8_t buffer[BLOCK_SIZE];
struct stat st;
int ok;
fprintf(stderr, "get_attr %u\n", (unsigned)ino);
bzero(&st, sizeof(st));
/*
* ino 1 is the volume header. Others are pointers.
*
*/
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
ERROR(ok < 0, EIO)
// ino 1 is the volume header.
if (ino == 1)
{
VolumeEntry v;
v.Load(buffer + 0x04);
ok = prodos_stat(v, &st);
ERROR(ok < 0, EIO);
st.st_ino = ino;
fuse_reply_attr(req, &st, 0.0);
return;
}
else
{
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
ok = prodos_stat(e, &st);
ERROR(ok < 0, EIO);
st.st_ino = ino;
fuse_reply_attr(req, &st, 0.0); //
}
}
// TODO -- add Disk::Lookup support so we don't have to parse the entire dir header.
// TODO -- add caching.
void prodos_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
uint8_t buffer[BLOCK_SIZE];
struct fuse_entry_param entry;
int ok;
vector<FileEntry> files;
fprintf(stderr, "lookup: %u %s\n", (unsigned)parent, name);
ERROR(!validProdosName(name), ENOENT)
ok = disk->Read(parent == 1 ? 2 : parent >> 9, buffer);
ERROR(ok < 0, EIO)
bzero(&entry, sizeof(entry));
entry.attr_timeout = 0.0;
entry.entry_timeout = 0.0;
// get the file list
// TODO -- Disk::look-up-one-file
if (parent == 1)
{
VolumeEntry v;
ok = disk->ReadVolume(&v, &files);
ERROR(ok < 0, EIO)
}
else
{
FileEntry e;
e.Load(buffer + (parent & 0x1ff));
ERROR(e.storage_type != DIRECTORY_FILE, ENOENT);
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
ERROR(ok < 0, EIO)
}
// ok, now go through the file list and look for a (case insensitive) match.
ok = -1;
unsigned name_length = strlen(name);
for(vector<FileEntry>::iterator iter = files.begin(); iter != files.end(); ++iter)
{
FileEntry& f = *iter;
if ( (f.name_length == name_length) && (strcasecmp(name, f.file_name) == 0))
{
ok = prodos_stat(f, &entry.attr);
fprintf(stderr, "stat %s %x (%x %x) %d\n", f.file_name, f.address, f.address >> 9, f.address & 0x1ff, ok);
entry.ino = f.address;
entry.attr.st_ino = f.address;
break;
}
}
ERROR(ok < 0, ENOENT);
fprintf(stderr, "file found!\n");
fuse_reply_entry(req, &entry);
}
void prodos_statfs(fuse_req_t req, fuse_ino_t ino)
{
struct statvfs vst;
VolumeEntry volume;
disk->ReadVolume(&volume, NULL);
// returns statvfs for the mount path or any file in the fs
// therefore, ignore ino.
std::memset(&vst, 0, sizeof(vst));
vst.f_bsize = 512; // fs block size
vst.f_frsize = 512; // fundamental fs block size
vst.f_blocks = volume.total_blocks;
vst.f_bfree = 0; // free blocks
vst.f_bavail = 0; // free blocks (non-root)
vst.f_files = 0; // ?
vst.f_ffree = -1; // free inodes.
vst.f_favail = -1; // free inodes (non-root)
vst.f_fsid = 0; // file system id?
vst.f_flag = ST_RDONLY | ST_NOSUID;
vst.f_namemax = 15;
fuse_reply_statfs(req, &vst);
}

View File

@@ -1,575 +0,0 @@
/*
* profuse_xattr.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <algorithm>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <strings.h>
using std::string;
#ifdef __APPLE__
#define NO_ATTRIBUTE ENOATTR
#else
#define NO_ATTRIBUTE EOPNOTSUPP
#endif
static bool isTextFile(unsigned ftype, unsigned auxtype)
{
if (ftype == 0x04) return true; // ascii text
if (ftype == 0xb0) return true; // source code.
if (ftype == 0x50 && auxtype == 0x5445) return true; // teach text
return false;
}
static const char *mimeType(unsigned ftype, unsigned auxtype)
{
switch(ftype)
{
case 0x04:
if (auxtype == 0) return "text/plain";
break;
case 0xb0:
return "text/plain";
break;
case 0x50:
if (auxtype == 0x5445) return "text/plain";
break;
case 0xc0:
if (auxtype == 0x8006) return "image/gif";
break;
case 0xe0:
if (auxtype == 0x8000) return "application/x-BinaryII";
if (auxtype == 0x8002) return "application/x-Shrinkit";
break;
}
return NULL;
}
static void setCreator(uint8_t *finfo, unsigned ftype, unsigned auxtype)
{
/*
tech note PT515
ProDOS -> Macintosh conversion
ProDOS Macintosh
Filetype Auxtype Creator Filetype
$00 $0000 'pdos' 'BINA'
$B0 (SRC) (any) 'pdos' 'TEXT'
$04 (TXT) $0000 'pdos' 'TEXT'
$FF (SYS) (any) 'pdos' 'PSYS'
$B3 (S16) (any) 'pdos' 'PS16'
$uv $wxyz 'pdos' 'p' $uv $wx $yz
Programmer's Reference for System 6.0:
ProDOS Macintosh
File Type Auxiliary Type Creator Type File Type
$00 $0000 “pdos” “BINA”
$04 (TXT) $0000 “pdos” “TEXT”
$FF (SYS) (any) “pdos” “PSYS”
$B3 (S16) $DByz “pdos” “p” $B3 $DB $yz
$B3 (S16) (any) “pdos” “PS16”
$D7 $0000 “pdos” “MIDI”
$D8 $0000 “pdos” “AIFF”
$D8 $0001 “pdos” “AIFC”
$E0 $0005 “dCpy” “dImg”
$FF (SYS) (any) “pdos” “PSYS”
$uv $wxyz “pdos” “p” $uv $wx $yz
*/
finfo[0] = 'p';
finfo[1] = ftype;
finfo[2] = auxtype >> 8;
finfo[3] = auxtype;
memcpy(finfo + 4, "pdos", 4);
switch (ftype)
{
case 0x00:
if (auxtype == 0) memcpy(finfo, "BINA", 4);
break;
case 0x04:
if (auxtype == 0) memcpy(finfo, "TEXT", 4);
break;
case 0x50:
if (auxtype == 0x5445) memcpy(finfo, "TEXT", 4);
break;
case 0xb0:
memcpy(finfo, "TEXT", 4);
break;
case 0xb3:
if ((auxtype >> 8) != 0xdb) memcpy(finfo, "PS16", 4);
break;
case 0xd7:
if (auxtype == 0) memcpy(finfo, "MIDI", 4);
break;
case 0xd8:
if (auxtype == 0) memcpy(finfo, "AIFF", 4);
if (auxtype == 1) memcpy(finfo, "AIFC", 4);
break;
case 0xe0:
if (auxtype == 5) memcpy(finfo, "dImgdCpy", 8);
break;
case 0xff:
memcpy(finfo, "PSYS", 4);
break;
}
}
#pragma mark XAttribute Functions
static void xattr_filetype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
uint8_t attr = e.file_type;
unsigned attr_size = 1;
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
// consider position here?
fuse_reply_buf(req, (char *)&attr, attr_size);
}
static void xattr_auxtype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
uint8_t attr[2];
unsigned attr_size = 2;
attr[0] = e.aux_type & 0xff;
attr[1] = (e.aux_type >> 8) & 0xff;
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
// consider position here?
fuse_reply_buf(req, (char *)&attr, attr_size);
}
// user.charset
static void xattr_charset(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
const char attr[] = "macintosh";
unsigned attr_size = sizeof(attr) - 1;
ERROR(!isTextFile(e.file_type, e.aux_type), NO_ATTRIBUTE)
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, (char *)&attr, attr_size);
}
//apple.TextEncoding
static void xattr_textencoding(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
const char attr[] = "MACINTOSH;0";
unsigned attr_size = sizeof(attr) - 1;
ERROR(!isTextFile(e.file_type, e.aux_type), NO_ATTRIBUTE)
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, (char *)&attr, attr_size);
}
static void xattr_rfork(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
int ok;
unsigned level;
ERROR (e.storage_type != EXTENDED_FILE, NO_ATTRIBUTE)
ok = disk->Normalize(e, 1);
ERROR(ok < 0, EIO)
switch(e.storage_type)
{
case SEEDLING_FILE:
level = 0;
break;
case SAPLING_FILE:
level = 1;
break;
case TREE_FILE:
level = 2;
break;
default:
ERROR(true, EIO)
}
if (size == 0)
{
fuse_reply_xattr(req, e.eof);
return;
}
size = std::min((uint32_t)(size + off), e.eof);
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
uint8_t *buffer = new uint8_t[blocks << 9];
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e.key_pointer, level, (int)off, (int)blocks);
ok = disk->ReadIndex(e.key_pointer, buffer, level, off, blocks);
if (ok < 0)
{
fuse_reply_err(req, EIO);
}
else
{
fuse_reply_buf(req, (char *)buffer + (off & 0x1ff), size);
}
delete []buffer;
return;
}
// Finder info.
static void xattr_finfo(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
int ok;
ExtendedEntry ee;
uint8_t attr[32];
unsigned attr_size = 32;
//ERROR (e.storage_type != EXTENDED_FILE, ENOENT)
switch (e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
bzero(attr, attr_size);
setCreator(attr, e.file_type, e.aux_type);
fuse_reply_buf(req, (char *)attr, attr_size);
return;
case EXTENDED_FILE:
// handled below.
break;
default:
ERROR(true, NO_ATTRIBUTE);
}
ok = disk->Normalize(e, 1, &ee);
ERROR(ok < 0, EIO)
// sanity check
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
break;
default:
ERROR(true, EIO)
}
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
memcpy(attr, ee.FInfo, 16);
memcpy(attr + 16, ee.xFInfo, 16);
// if no creator, create one.
if (memcmp(attr, "\0\0\0\0\0\0\0\0", 8) == 0)
setCreator(attr, e.file_type, e.aux_type);
fuse_reply_buf(req, (char *)attr, attr_size);
}
static void xattr_mimetype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
unsigned attr_size;
const char *mime = mimeType(e.file_type, e.aux_type);
ERROR(!mime, NO_ATTRIBUTE);
attr_size = strlen(mime);
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, mime, attr_size);
}
void prodos_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
// list of supported attributes.
//
#define NO_ATTR() \
{ \
if (size) fuse_reply_buf(req, NULL, 0); \
else fuse_reply_xattr(req, 0); \
return; \
}
fprintf(stderr, "listxattr %u\n", (unsigned)ino);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned attr_size;
string attr;
if(ino == 1)
NO_ATTR()
ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
attr += "prodos.FileType";
attr.append(1, 0);
attr += "prodos.AuxType";
attr.append(1, 0);
switch(e.storage_type)
{
case EXTENDED_FILE:
{
// TODO -- pretend there's no resource fork if resource fork eof == 0 ?
//
//ok = disk->Normalize(e, 1);
//ERROR(ok < 0, EIO)
attr += "prodos.ResourceFork";
attr.append(1, 0);
attr += "com.apple.FinderInfo";
attr.append(1, 0);
break;
}
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
// generate HFS creator codes.
attr += "com.apple.FinderInfo";
attr.append(1, 0);
break;
case DIRECTORY_FILE:
NO_ATTR()
break;
default:
NO_ATTR()
break;
}
if (isTextFile(e.file_type, e.aux_type))
{
attr += "com.apple.TextEncoding";
attr.append(1, 0);
attr += "user.charset";
attr.append(1, 0);
}
if (mimeType(e.file_type, e.aux_type))
{
attr += "user.mime_type";
attr.append(1, 0);
}
attr_size = attr.length();
fprintf(stderr, "%d %s\n", attr_size, attr.c_str());
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
if (size < attr_size)
{
fuse_reply_err(req, ERANGE);
return;
}
fuse_reply_buf(req, attr.data(), attr_size);
return;
}
/*
* offset is only valid in OS X for the resource fork.
*
*/
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
{
fprintf(stderr, "getxattr: %u %s %u %u \n", (unsigned)ino, name, (unsigned)size, (unsigned)off);
uint8_t buffer[BLOCK_SIZE];
ERROR(ino == 1, NO_ATTRIBUTE) // finder can't handle EISDIR.
int ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
case EXTENDED_FILE:
break;
case DIRECTORY_FILE:
ERROR(true, NO_ATTRIBUTE) // Finder can't handle EISDIR.
default:
ERROR(true, NO_ATTRIBUTE);
}
if (strcmp("prodos.FileType", name) == 0)
{
xattr_filetype(e, req, size, off);
return;
}
if (strcmp("prodos.AuxType", name) == 0)
{
xattr_auxtype(e, req, size, off);
return;
}
if (strcmp("com.apple.TextEncoding", name) == 0)
{
xattr_textencoding(e, req, size, off);
return;
}
if ( (e.storage_type == EXTENDED_FILE) && (strcmp("prodos.ResourceFork", name) == 0))
{
xattr_rfork(e, req, size, off);
return;
}
if ( strcmp("com.apple.FinderInfo", name) == 0)
{
xattr_finfo(e, req, size, off);
return;
}
// linux standard
if (strcmp("user.mime_type", name) == 0)
{
xattr_mimetype(e, req, size, off);
return;
}
// linux standard
if (strcmp("user.charset", name) == 0)
{
xattr_charset(e, req, size, off);
return;
}
fuse_reply_err(req, NO_ATTRIBUTE);
}
/*
* Linux, et alia do not have an offset parameter.
*/
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
prodos_getxattr(req, ino, name, size, 0);
}

View File

@@ -1,552 +0,0 @@
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cerrno>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <err.h>
typedef std::vector<std::string>::iterator vsiter;
// prototypes to shut up clang.
void hexdump(const uint8_t *data, ssize_t size);
ssize_t get_attr_list(const char * fname, std::vector<std::string> &out);
void dumpxattr(const char *file, const char *attr);
int op_list(int argc, char **argv);
int op_dump(int argc, char **argv);
int op_read(int argc, char **argv);
int op_rm(int argc, char **argv);
int op_mv(int argc, char **argv);
int op_cp(int argc, char **argv);
ssize_t read_all(int fd, std::vector<uint8_t> &out);
int op_write(int argc, char **argv);
void usage(const char *name);
#ifdef __APPLE__
// apple has additional parameter for position and options.
inline ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
{
return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
}
// apple has additional parameter for options.
inline ssize_t listxattr(const char *path, char *namebuf, size_t size)
{
return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
}
// apple has additional parameter for offset .
inline int setxattr(const char *path, const char *name, void *value, size_t size, int flags)
{
return setxattr(path, name, value, size, 0, flags);
}
inline int removexattr(const char *path, const char *name) {
return removexattr(path, name, 0);
}
#endif
void hexdump(const uint8_t *data, ssize_t size)
{
const char *HexMap = "0123456789abcdef";
char buffer1[16 * 3 + 1 + 1];
char buffer2[16 + 1];
ssize_t offset = 0;
unsigned i, j;
while(size > 0)
{
std::memset(buffer1, ' ', sizeof(buffer1));
std::memset(buffer2, ' ', sizeof(buffer2));
unsigned linelen = (unsigned)std::min(size, (ssize_t)16);
for (i = 0, j = 0; i < linelen; i++)
{
unsigned x = data[i];
buffer1[j++] = HexMap[x >> 4];
buffer1[j++] = HexMap[x & 0x0f];
j++;
if (i == 7) j++;
// isascii not part of std:: and may be a macro.
buffer2[i] = isascii(x) && std::isprint(x) ? x : '.';
}
buffer1[sizeof(buffer1)-1] = 0;
buffer2[sizeof(buffer2)-1] = 0;
std::printf("%06x:\t%s\t%s\n", (unsigned)offset, buffer1, buffer2);
offset += 16;
data += 16;
size -= 16;
}
std::printf("\n");
}
/*
* build a list of attribute names for a file.
* returns -1 on failure
* otherwise, returns # of attributes.
*
*/
ssize_t get_attr_list(const char * fname, std::vector<std::string> &out)
{
ssize_t asize = listxattr(fname, NULL, 0);
if (asize < 0) return -1;
if (asize == 0) return 0;
char *buffer = new char[asize];
if ((asize = listxattr(fname, buffer, asize)) < 0)
{
delete []buffer;
return -1;
}
//hexdump((uint8_t *)buffer, asize);
char *cp = buffer;
// buffer is null-terminated utf-8 strings
// eg:
// c a t 0 d o g 0
// 0 1 2 3 4 5 6 7
while (asize > 0)
{
ssize_t len = std::strlen(cp);
out.push_back(std::string(cp, len));
++len;
asize -= len;
cp += len;
}
delete []buffer;
return out.size();
}
/*
* safely read the entire xattr into buffer.
*
*/
int read_xattr(const char *fname, const char *attr_name, std::vector<uint8_t> &buffer) {
for(;;) {
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
if (asize < 0) {
if (errno == EINTR) continue;
return -1;
}
buffer.resize(asize);
asize = getxattr(fname, attr_name, buffer.data(), asize);
if (asize < 0) {
// ERANGE -- buffer is not large enough.
if (errno == EINTR || errno == ERANGE) continue;
return -1;
}
buffer.resize(asize);
return 0;
}
}
/*
* hexdump an individual attribute.
*/
void dumpxattr(const char *fname, const char *attr_name)
{
std::vector<uint8_t> buffer;
if (read_xattr(fname, attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
return;
}
std::printf("%s: %u\n", attr_name, (unsigned)buffer.size());
hexdump(buffer.data(), buffer.size());
}
/*
* list a file's attributes (and size)
*
*/
int op_list(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
get_attr_list(fname, attr);
std::sort(attr.begin(), attr.end());
}
else
{
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
}
// find the max name length (not utf-8 happy)
unsigned maxname = 0;
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
maxname = std::max(maxname, (unsigned)i->length());
}
maxname += 2;
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
if (asize >= 0)
{
std::printf("%-*s %8u\n", maxname, attr_name, (unsigned)asize);
}
else
{
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
}
}
return 0;
}
/*
* hexdump a file's attributes.
*
*/
int op_dump(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
get_attr_list(fname, attr);
std::sort(attr.begin(), attr.end());
}
else
{
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
}
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
dumpxattr(fname, attr_name);
}
return 0;
}
// must specify the xattr name.
int op_read(int argc, char **argv)
{
if (argc != 2)
{
std::fprintf(stderr, "Must specify attribute to be read.\n");
return -1;
}
const char *fname = argv[0];
const char *attr_name = argv[1];
std::vector<uint8_t> buffer;
if (read_xattr(fname, attr_name, buffer) < 0 ) {
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
return -1;
}
if (buffer.size() == 0) return 0;
if (::write(STDOUT_FILENO, buffer.data(), buffer.size()) < 0)
{
warn("write");
return -1;
}
return 0;
}
ssize_t read_all(int fd, std::vector<uint8_t> &out)
{
struct stat st;
ssize_t bsize;
// stdin could be a file, a pipe, or a tty.
if (fstat(fd, &st) < 0)
{
warn("fstat stdin");
return -1;
}
bsize = std::max(st.st_blksize, (blksize_t)512);
out.reserve(std::max(st.st_size, (off_t)512));
std::vector<uint8_t> buffer;
buffer.resize(bsize);
for(;;)
{
ssize_t size = ::read(fd, buffer.data(), bsize);
if (size == 0) break;
if (size == -1)
{
if (errno == EINTR) continue;
warn("read");
return -1;
}
// force reserve?
out.insert(out.end(), buffer.data(), buffer.data() + size);
}
return out.size();
}
// xattr write filename attrname
// stdin -> filename:attrname
int op_write(int argc, char **argv)
{
std::vector<uint8_t> buffer;
if (argc != 2)
{
std::fprintf(stderr, "Must specify attribute to be written.\n");
return -1;
}
const char *fname = argv[0];
const char *attr_name = argv[1];
if (read_all(STDIN_FILENO, buffer) < 0)
{
return -1;
}
// Flag options:
// XATTR_CREATE : fails if attr already exists.
// XATTR_REPLACE : fails if attr does not exist.
// if neither of the above is specified, will create and replace.
// XATTR_NOFOLLOW : do not follow symbolic links (Apple only)
ssize_t asize = setxattr(fname, attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, attr_name);
return -1;
}
return 0;
}
/*
* remove an attribute
*
*/
int op_rm(int argc, char **argv)
{
int rv = 0;
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
std::fprintf(stderr, "Must specify attribute to be removed.\n");
return -1;
}
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
int ok = removexattr(fname, attr_name);
if (ok < 0) {
warn("removexattr(\"%s\", \"%s\")", fname, attr_name);
rv = 1;
}
}
return rv;
}
/*
* copy an attribute
*
*/
int op_cp(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc != 3)
{
std::fprintf(stderr, "Must specify source and destination.\n");
return -1;
}
const char *src_attr_name = argv[1];
const char *dest_attr_name = argv[2];
std::vector<uint8_t> buffer;
if (read_xattr(fname, src_attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, src_attr_name);
return -1;
}
ssize_t asize = setxattr(fname, dest_attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
return 0;
}
/*
* move an attribute
*
*/
int op_mv(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc != 3)
{
std::fprintf(stderr, "Must specify source and destination.\n");
return -1;
}
const char *src_attr_name = argv[1];
const char *dest_attr_name = argv[2];
std::vector<uint8_t> buffer;
if (read_xattr(fname, src_attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, src_attr_name);
return -1;
}
ssize_t asize = setxattr(fname, dest_attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
int ok = removexattr(fname, src_attr_name);
if (ok < 0) {
warn("removexattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
return 0;
}
void usage(const char *name)
{
std::printf("usage:\n");
std::printf("%s list file [attr ...]\n", name);
std::printf("%s dump file [attr ...]\n", name);
std::printf("%s read file attr\n", name);
std::printf("%s write file attr\n", name);
std::exit(0);
}
/*
* xattr list file [xattr ...]
* xattr dump file [xattr ...]
*
*/
int main(int argc, char **argv)
{
if (argc < 3) usage(*argv);
if (std::strcmp(argv[1], "list") == 0 || std::strcmp(argv[1], "ls") == 0)
return op_list(argc - 2, argv + 2);
if (std::strcmp(argv[1], "dump") == 0 || std::strcmp(argv[1], "hd") == 0)
return op_dump(argc - 2, argv + 2);
if (std::strcmp(argv[1], "read") == 0) return op_read(argc - 2, argv + 2);
if (std::strcmp(argv[1], "write") == 0) return op_write(argc - 2, argv + 2);
if (std::strcmp(argv[1], "rm") == 0) return op_rm(argc - 2, argv + 2);
if (std::strcmp(argv[1], "mv") == 0) return op_mv(argc - 2, argv + 2);
if (std::strcmp(argv[1], "cp") == 0) return op_cp(argc - 2, argv + 2);
usage(*argv);
return 1;
}

View File

@@ -2,7 +2,6 @@
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <vector>
#include <string>
@@ -10,24 +9,26 @@
#include <unistd.h>
#define __FreeBSD__ 10
#define __DARWIN_64_BIT_INO_T 1
#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION 27
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#include <fuse/fuse_opt.h>
#include <fuse/fuse_lowlevel.h>
#include <Pascal/Pascal.h>
#include <Common/Exception.h>
#include <File/File.h>
#include <Pascal/File.h>
#include <ProFUSE/Exception.h>
#include <Device/Device.h>
#include <Device/BlockDevice.h>
#include <Device/MappedFile.h>
#include <Device/DiskCopy42Image.h>
Pascal::VolumeEntry *fVolume = NULL;
std::string fDiskImage;
void usage()
{
std::printf("profuse_pascal 0.1\n\n");
@@ -41,11 +42,6 @@ void usage()
" -v verbose\n"
" --format=format specify the disk image format. Valid values are:\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
" 2img Universal Disk Image\n"
#ifdef HAVE_NUFX
" sdk ShrinkIt Disk Image\n"
#endif
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
" -o opt1,opt2... other mount parameters.\n"
@@ -156,23 +152,17 @@ bool make_mount_dir(std::string name, std::string &path)
int main(int argc, char **argv)
{
extern void init_ops(fuse_lowlevel_ops *ops);
struct options options;
struct options options = { 0 };
std::memset(&options, 0, sizeof(options));
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
std::string mountPath;
unsigned format = 0;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
std::string mountPath;
unsigned format;
int foreground = false;
int multithread = false;
Pascal::VolumeEntryPointer volume;
init_ops(&pascal_ops);
@@ -190,34 +180,56 @@ int main(int argc, char **argv)
// default prodos-order disk image.
if (options.format)
{
format = Device::BlockDevice::ImageType(options.format);
format = Device::DiskImage::ImageType(options.format);
if (!format)
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
}
try
{
Device::BlockDevicePointer device;
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
if (!format)
format = Device::DiskImage::ImageType(fDiskImage.c_str(), 'PO__');
bool readOnly = true;
try {
std::auto_ptr<Device::BlockDevice> device;
if (!device.get())
switch(format)
{
case 'DC42':
device.reset(new Device::DiskCopy42Image(fDiskImage.c_str(), readOnly));
break;
case 'PO__':
device.reset(new Device::ProDOSOrderDiskImage(fDiskImage.c_str(), readOnly));
break;
case 'DO__':
device.reset(new Device::DOSOrderDiskImage(fDiskImage.c_str(), readOnly));
break;
default:
std::fprintf(stderr, "Error: Unknown or unsupported device type.\n");
exit(1);
}
volume = Pascal::VolumeEntry::Open(device);
fVolume = new Pascal::VolumeEntry(device.get());
device.release();
}
catch (::Exception &e)
catch (ProFUSE::POSIXException &e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", std::strerror(e.error()));
return -1;
}
catch (ProFUSE::Exception &e)
{
std::fprintf(stderr, "%s\n", e.what());
return -1;
}
@@ -225,7 +237,7 @@ int main(int argc, char **argv)
{
// Macfuse supports custom volume names (displayed in Finder)
std::string str("-ovolname=");
str += volume->name();
str += fVolume->name();
fuse_opt_add_arg(&args, str.c_str());
// 512 byte blocksize.
@@ -254,7 +266,7 @@ int main(int argc, char **argv)
if (mountpoint == NULL || *mountpoint == 0)
{
if (make_mount_dir(volume->name(), mountPath))
if (make_mount_dir(fVolume->name(), mountPath))
mountpoint = (char *)mountPath.c_str();
}
@@ -265,14 +277,15 @@ int main(int argc, char **argv)
{
struct fuse_session* se;
std::printf("Mounting ``%s'' on ``%s''\n", volume->name(), mountpoint);
std::printf("Mounting ``%s'' on ``%s''\n", fVolume->name(), mountpoint);
se = fuse_lowlevel_new(&args, &pascal_ops, sizeof(pascal_ops), volume.get());
se = fuse_lowlevel_new(&args, &pascal_ops, sizeof(pascal_ops), fVolume);
if (se) do {
//foreground = 1;
//multithread = 0;
err = fuse_daemonize(foreground);
err = fuse_daemonize(foreground); // todo
if (err < 0 ) break;
err = fuse_set_signal_handlers(se);
@@ -294,6 +307,7 @@ int main(int argc, char **argv)
fuse_opt_free_args(&args);
delete fVolume;
#ifdef __APPLE__
@@ -302,4 +316,4 @@ int main(int argc, char **argv)
return err ? 1 : 0;
}
}

View File

@@ -1,28 +1,27 @@
#define __FreeBSD__ 10
#define __DARWIN_64_BIT_INO_T 1
#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION 27
#include <string>
#include <vector>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <fuse/fuse_opt.h>
#include <fuse/fuse_lowlevel.h>
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#include <Pascal/Pascal.h>
#include <Common/auto.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <Pascal/File.h>
#include <ProFUSE/auto.h>
#include <ProFUSE/Exception.h>
#define NO_ATTR() \
{ \
@@ -43,105 +42,55 @@
}
#define DEBUGNAME() \
if (0) { std::fprintf(stderr, "%s\n", __func__); }
// linux doesn't have ENOATTR.
#ifndef ENOATTR
#define ENOATTR ENOENT
#endif
using namespace Pascal;
// fd_table is files which have been open.
// fd_table_available is a list of indexes in fd_table which are not currently used.
static std::vector<FileEntryPointer> fd_table;
static std::vector<unsigned> fd_table_available;
static FileEntryPointer findChild(VolumeEntry *volume, unsigned inode)
static FileEntry *findChild(VolumeEntry *volume, unsigned inode)
{
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntryPointer child = volume->fileAtIndex(i);
if (!child) continue;
FileEntry *child = volume->fileAtIndex(i);
if (inode == child->inode()) return child;
}
return FileEntryPointer();
return NULL;
}
#pragma mark -
#pragma mark fs
static void pascal_init(void *userdata, struct fuse_conn_info *conn)
{
DEBUGNAME()
//std::printf("pascal_init\n");
// nop
// text files have a non-thread safe index.
// which is initialized via read() or fileSize()
VolumeEntry *volume = (VolumeEntry *)userdata;
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntryPointer child = volume->fileAtIndex(i);
child->fileSize();
volume->fileAtIndex(i)->fileSize();
}
}
static void pascal_destroy(void *userdata)
{
DEBUGNAME()
//std::printf("pascal_destroy\n");
// nop
}
static void pascal_statfs(fuse_req_t req, fuse_ino_t ino)
{
DEBUGNAME()
struct statvfs vst;
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
ERROR(!volume, EIO)
// returns statvfs for the mount path or any file in the fs
// therefore, ignore ino.
std::memset(&vst, 0, sizeof(vst));
vst.f_bsize = 512; // fs block size
vst.f_frsize = 512; // fundamental fs block size
vst.f_blocks = volume->volumeBlocks();
vst.f_bfree = volume->freeBlocks(true); // free blocks
vst.f_bavail = volume->freeBlocks(false); // free blocks (non-root)
vst.f_files = volume->fileCount();
vst.f_ffree = -1; // free inodes.
vst.f_favail = -1; // free inodes (non-root)
vst.f_fsid = 0; // file system id?
vst.f_flag = volume->readOnly() ? ST_RDONLY | ST_NOSUID : ST_NOSUID;
vst.f_namemax = 15;
fuse_reply_statfs(req, &vst);
}
#pragma mark -
#pragma mark xattr
static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
DEBUGNAME()
//std::printf("pascal_listxattr\n");
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntryPointer file;
FileEntry *file;
std::string attr;
unsigned attrSize;
@@ -181,11 +130,11 @@ static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
DEBUGNAME()
//std::printf("pascal_getxattr\n");
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntryPointer file;
FileEntry *file;
std::string attr(name);
ERROR(ino == 1, ENOATTR)
@@ -228,8 +177,6 @@ static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, si
// OS X version.
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
{
DEBUGNAME()
pascal_getxattr(req, ino, name, size);
}
@@ -238,7 +185,8 @@ static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, si
static void pascal_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
DEBUGNAME()
//std::printf("pascal_opendir\n");
ERROR(ino != 1, ENOTDIR)
@@ -249,17 +197,17 @@ static void pascal_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
DEBUGNAME()
//std::printf("pascal_releasedir\n");
fuse_reply_err(req, 0);
}
static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
DEBUGNAME()
//std::printf("pascal_readdir %u, %u, %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
::auto_array<uint8_t> buffer(new uint8_t[size]);
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[size]);
unsigned count = volume->fileCount();
@@ -305,7 +253,7 @@ static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t of
{
unsigned tmp;
FileEntryPointer file = volume->fileAtIndex(i);
FileEntry *file = volume->fileAtIndex(i);
if (file == NULL) break; //?
// only these fields are used.
@@ -335,8 +283,6 @@ static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t of
static void stat(FileEntry *file, struct stat *st)
{
DEBUGNAME()
std::memset(st, 0, sizeof(struct stat));
time_t t = file->modification();
@@ -356,8 +302,6 @@ static void stat(FileEntry *file, struct stat *st)
static void stat(VolumeEntry *volume, struct stat *st)
{
DEBUGNAME()
std::memset(st, 0, sizeof(struct stat));
time_t t = volume->lastBoot();
@@ -377,7 +321,7 @@ static void stat(VolumeEntry *volume, struct stat *st)
static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
DEBUGNAME()
//std::printf("pascal_lookup %u %s\n", (unsigned)parent, name);
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
struct fuse_entry_param entry;
@@ -388,7 +332,7 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntryPointer file = volume->fileAtIndex(i);
FileEntry *file = volume->fileAtIndex(i);
if (file == NULL) break;
if (::strcasecmp(file->name(), name)) continue;
@@ -399,7 +343,7 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
entry.entry_timeout = 0.0;
entry.ino = file->inode();
stat(file.get(), &entry.attr);
stat(file, &entry.attr);
fuse_reply_entry(req, &entry);
return;
}
@@ -410,11 +354,11 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
DEBUGNAME()
//std::printf("pascal_getattr %u\n", (unsigned)ino);
struct stat st;
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntryPointer file;
FileEntry *file;
if (ino == 1)
{
@@ -426,9 +370,9 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
file = findChild(volume, ino);
ERROR(file == NULL, ENOENT)
//printf("\t%s\n", file->name());
printf("\t%s\n", file->name());
stat(file.get(), &st);
stat(file, &st);
fuse_reply_attr(req, &st, 0.0);
}
@@ -438,11 +382,10 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
unsigned index;
DEBUGNAME()
//std::printf("pascal_open\n");
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntryPointer file;
FileEntry *file;
ERROR(ino == 1, EISDIR)
@@ -452,20 +395,7 @@ static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f
ERROR((fi->flags & O_ACCMODE) != O_RDONLY, EACCES)
// insert the FileEntryPointer into fd_table.
if (fd_table_available.size())
{
index = fd_table_available.back();
fd_table_available.pop_back();
fd_table[index] = file;
}
else
{
index = fd_table.size();
fd_table.push_back(file);
}
fi->fh = index;
fi->fh = (uint64_t)file;
fuse_reply_open(req, fi);
}
@@ -473,11 +403,7 @@ static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f
static void pascal_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
unsigned index = fi->fh;
DEBUGNAME()
fd_table[index].reset();
fd_table_available.push_back(index);
//std::printf("pascal_release\n");
fuse_reply_err(req, 0);
}
@@ -485,26 +411,22 @@ static void pascal_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
unsigned index = fi->fh;
DEBUGNAME()
//std::printf("pascal_read %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
//VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntryPointer file = fd_table[index];
FileEntry *file = (FileEntry *)fi->fh;
try
{
::auto_array<uint8_t> buffer(new uint8_t[size]);
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[size]);
unsigned rsize = file->read(buffer.get(), size, off);
fuse_reply_buf(req, (char *)(buffer.get()), rsize);
return;
}
catch (POSIX::Exception &e)
catch (ProFUSE::POSIXException &e)
{
printf("posix error...\n");
ERROR(true, e.error());
@@ -517,15 +439,14 @@ static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
}
void init_ops(fuse_lowlevel_ops *ops)
{
DEBUGNAME()
std::memset(ops, 0, sizeof(fuse_lowlevel_ops));
ops->init = pascal_init;
ops->destroy = pascal_destroy;
ops->statfs = pascal_statfs;
// returns pascal.filekind, text encoding.
ops->listxattr = pascal_listxattr;

BIN
libNuFX.a

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More