Merge pull request #570 from davidgiven/vfs

Add VFS write support.
This commit is contained in:
David Given
2022-08-31 00:32:38 +02:00
committed by GitHub
26 changed files with 760 additions and 389 deletions

View File

@@ -128,6 +128,19 @@ uint8_t& Bytes::operator [] (unsigned pos)
return (*_data)[pos];
}
Bytes Bytes::readFromFile(const std::string& filename)
{
Bytes bytes;
ByteWriter bw(bytes);
std::ifstream f(filename);
if (!f)
Error() << fmt::format("cannot open '{}': {}", filename, strerror(errno));
bw += f;
return bytes;
}
Bytes Bytes::slice(unsigned start, unsigned len) const
{
start += _low;

View File

@@ -19,6 +19,9 @@ public:
Bytes* operator = (const Bytes& other);
public:
static Bytes readFromFile(const std::string& filename);
public:
/* General purpose methods */

View File

@@ -15,6 +15,17 @@ Image::Image(std::set<std::shared_ptr<const Sector>>& sectors)
calculateSize();
}
bool Image::empty() const
{
return _sectors.empty();
}
bool Image::contains(unsigned track, unsigned side, unsigned sectorid) const
{
key_t key = std::make_tuple(track, side, sectorid);
return _sectors.find(key) != _sectors.end();
}
std::shared_ptr<const Sector> Image::get(unsigned track, unsigned side, unsigned sectorid) const
{
static std::shared_ptr<const Sector> NONE;
@@ -37,6 +48,12 @@ std::shared_ptr<Sector> Image::put(unsigned track, unsigned side, unsigned secto
return sector;
}
void Image::erase(unsigned track, unsigned side, unsigned sectorid)
{
key_t key = std::make_tuple(track, side, sectorid);
_sectors.erase(key);
}
void Image::calculateSize()
{
_geometry = {};

View File

@@ -39,8 +39,11 @@ public:
public:
void calculateSize();
bool empty() const;
bool contains(unsigned track, unsigned side, unsigned sectorId) const;
std::shared_ptr<const Sector> get(unsigned track, unsigned side, unsigned sectorId) const;
std::shared_ptr<Sector> put(unsigned track, unsigned side, unsigned sectorId);
void erase(unsigned track, unsigned side, unsigned sectorId);
const_iterator begin() const { return const_iterator(_sectors.cbegin()); }
const_iterator end() const { return const_iterator(_sectors.cend()); }

View File

@@ -187,8 +187,6 @@ void writeTracks(FluxSink& fluxSink,
producer,
std::function<bool(const Location& location)> verifier)
{
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
for (const auto& location : Mapper::computeLocations())
{
testForEmergencyStop();
@@ -287,44 +285,53 @@ void writeTracksAndVerify(FluxSink& fluxSink,
return false;
}
auto wantedSectors = encoder.collectSectors(location, image);
std::sort(wantedSectors.begin(),
wantedSectors.end(),
sectorPointerSortPredicate);
Image wanted;
for (const auto& sector : encoder.collectSectors(location, image))
wanted.put(sector->logicalTrack, sector->logicalSide, sector->logicalSector)->data = sector->data;
std::vector<std::shared_ptr<const Sector>> gotSectors(
trackFlux->sectors.begin(), trackFlux->sectors.end());
std::sort(gotSectors.begin(),
gotSectors.end(),
sectorPointerSortPredicate);
if (!std::equal(gotSectors.begin(),
gotSectors.end(),
wantedSectors.begin(),
wantedSectors.end(),
sectorPointerEqualsPredicate))
{
Logger() << "good read but the data doesn't match";
return false;
}
for (const auto& sector : trackFlux->sectors)
{
const auto s = wanted.get(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
if (!s)
{
Logger() << "spurious sector on verify";
return false;
}
if (s->data != sector->data.slice(0, s->data.size()))
{
Logger() << "data mismatch on verify";
return false;
}
wanted.erase(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
}
if (!wanted.empty())
{
Logger() << "missing sector on verify";
return false;
}
return true;
});
}
void writeDiskCommand(std::shared_ptr<const Image> image,
void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource)
{
const Image* imagep = &image;
std::unique_ptr<const Image> remapped;
if (config.has_sector_mapping())
image = std::move(Mapper::remapSectorsLogicalToPhysical(
*image, config.sector_mapping()));
{
remapped = Mapper::remapSectorsLogicalToPhysical(
image, config.sector_mapping());
imagep = &*remapped;
}
if (fluxSource && decoder)
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *imagep);
else
writeTracks(fluxSink, encoder, *image);
writeTracks(fluxSink, encoder, *imagep);
}
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)

View File

@@ -23,7 +23,7 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(std::shared_ptr<const Image> image,
extern void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder = nullptr,

View File

@@ -6,30 +6,32 @@
class AcornDfsDirent : public Dirent
{
public:
AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1)
{
filename += (char)(bytes0[7] & 0x7f);
filename += '.';
for (int j = 0; j < 7; j++)
filename += bytes0[j] & 0x7f;
filename = filename.substr(0, filename.find(' '));
AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1)
{
filename += (char)(bytes0[7] & 0x7f);
filename += '.';
for (int j = 0; j < 7; j++)
filename += bytes0[j] & 0x7f;
filename = filename.substr(0, filename.find(' '));
this->inode = inode;
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
load_address = ((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
exec_address = ((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2];
locked = bytes0[7] & 0x80;
length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4];
file_type = TYPE_FILE;
mode = locked ? "L" : "";
}
this->inode = inode;
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
load_address =
((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
exec_address =
((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2];
locked = bytes0[7] & 0x80;
length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4];
file_type = TYPE_FILE;
mode = locked ? "L" : "";
}
public:
int inode;
uint32_t start_sector;
uint32_t load_address;
uint32_t exec_address;
bool locked;
int inode;
uint32_t start_sector;
uint32_t load_address;
uint32_t exec_address;
bool locked;
};
class AcornDfsFilesystem : public Filesystem
@@ -37,7 +39,7 @@ class AcornDfsFilesystem : public Filesystem
public:
AcornDfsFilesystem(
const AcornDfsProto& config, std::shared_ptr<SectorInterface> sectors):
Filesystem(sectors),
Filesystem(sectors),
_config(config)
{
}
@@ -53,46 +55,49 @@ public:
throw FileNotFoundException();
std::vector<std::unique_ptr<Dirent>> result;
for (auto& dirent : findAllFiles())
result.push_back(std::move(dirent));
for (auto& dirent : findAllFiles())
result.push_back(std::move(dirent));
return result;
}
Bytes getFile(const Path& path)
{
auto dirent = findFile(path);
int sectors = (dirent->length + 255) / 256;
Bytes getFile(const Path& path)
{
auto dirent = findFile(path);
int sectors = (dirent->length + 255) / 256;
Bytes data;
ByteWriter bw(data);
for (int i = 0; i < sectors; i++)
{
auto sector = getLogicalSector(dirent->start_sector + i);
bw.append(sector);
}
Bytes data;
ByteWriter bw(data);
for (int i = 0; i < sectors; i++)
{
auto sector = getLogicalSector(dirent->start_sector + i);
bw.append(sector);
}
data.resize(dirent->length);
return data;
}
data.resize(dirent->length);
return data;
}
std::map<std::string, std::string> getMetadata(const Path& path)
{
std::map<std::string, std::string> attributes;
std::map<std::string, std::string> getMetadata(const Path& path)
{
std::map<std::string, std::string> attributes;
auto dirent = findFile(path);
attributes["filename"] = dirent->filename;
attributes["length"] = fmt::format("{}", dirent->length);
attributes["type"] = "file";
attributes["mode"] = dirent->mode;
attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode);
attributes["acorndfs.start_sector"] = fmt::format("{}", dirent->start_sector);
attributes["acorndfs.load_address"] = fmt::format("0x{:x}", dirent->load_address);
attributes["acorndfs.exec_address"] = fmt::format("0x{:x}", dirent->exec_address);
attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked);
auto dirent = findFile(path);
attributes["filename"] = dirent->filename;
attributes["length"] = fmt::format("{}", dirent->length);
attributes["type"] = "file";
attributes["mode"] = dirent->mode;
attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode);
attributes["acorndfs.start_sector"] =
fmt::format("{}", dirent->start_sector);
attributes["acorndfs.load_address"] =
fmt::format("0x{:x}", dirent->load_address);
attributes["acorndfs.exec_address"] =
fmt::format("0x{:x}", dirent->exec_address);
attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked);
return attributes;
}
return attributes;
}
private:
std::vector<std::unique_ptr<AcornDfsDirent>> findAllFiles()
@@ -110,25 +115,26 @@ private:
auto bytes0 = sector0.slice(i * 8 + 8, 8);
auto bytes1 = sector1.slice(i * 8 + 8, 8);
result.push_back(std::make_unique<AcornDfsDirent>(i, bytes0, bytes1));
result.push_back(
std::make_unique<AcornDfsDirent>(i, bytes0, bytes1));
}
return result;
}
std::unique_ptr<AcornDfsDirent> findFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
std::unique_ptr<AcornDfsDirent> findFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
for (auto& dirent : findAllFiles())
for (auto& dirent : findAllFiles())
{
if (dirent->filename == path[0])
return std::move(dirent);
if (dirent->filename == path[0])
return std::move(dirent);
}
throw FileNotFoundException();
}
throw FileNotFoundException();
}
private:
const AcornDfsProto& _config;

View File

@@ -147,6 +147,30 @@ public:
return bytes;
}
void putFile(const Path& path, const Bytes& data)
{
AdfMount m(this);
if (path.size() == 0)
throw BadPathException();
auto* vol = m.getVolume();
changeDirButOne(vol, path);
auto* file = adfOpenFile(vol, (char*)path.back().c_str(), (char*)"w");
if (!file)
throw CannotWriteException();
unsigned pos = 0;
while (pos != data.size())
{
long done = adfWriteFile(
file, data.size() - pos, (uint8_t*)data.cbegin() + pos);
pos += done;
}
adfCloseFile(file);
}
private:
class AdfEntry
{
@@ -270,6 +294,8 @@ public:
RETCODE adfNativeWriteSector(
struct Device* dev, int32_t sector, int size, uint8_t* buffer)
{
Bytes bytes(buffer, size);
putLogicalSector(sector, bytes);
return RC_OK;
}

View File

@@ -33,9 +33,9 @@ void AppleSingle::parse(const Bytes& bytes)
case 9:
{
Bytes finderinfo = bytes.slice(offset, length);
type = finderinfo.slice(0, 4);
creator = finderinfo.slice(4, 4);
Bytes finderinfo = bytes.slice(offset, length);
type = finderinfo.slice(0, 4);
creator = finderinfo.slice(4, 4);
break;
}

View File

@@ -27,32 +27,32 @@ static constexpr int DIRECTORY_SECTORS = 8;
class Brother120Dirent : public Dirent
{
public:
Brother120Dirent(int inode, const Bytes& bytes)
{
ByteReader br(bytes);
filename = br.read(8);
filename = filename.substr(0, filename.find(' '));
Brother120Dirent(int inode, const Bytes& bytes)
{
ByteReader br(bytes);
filename = br.read(8);
filename = filename.substr(0, filename.find(' '));
this->inode = inode;
brother_type = br.read_8();
start_sector = br.read_be16();
length = br.read_8() * SECTOR_SIZE;
file_type = TYPE_FILE;
mode = "";
}
this->inode = inode;
brother_type = br.read_8();
start_sector = br.read_be16();
length = br.read_8() * SECTOR_SIZE;
file_type = TYPE_FILE;
mode = "";
}
public:
int inode;
int brother_type;
uint32_t start_sector;
int inode;
int brother_type;
uint32_t start_sector;
};
class Brother120Filesystem : public Filesystem
{
public:
Brother120Filesystem(
const Brother120FsProto& config, std::shared_ptr<SectorInterface> sectors):
Filesystem(sectors),
Brother120Filesystem(const Brother120FsProto& config,
std::shared_ptr<SectorInterface> sectors):
Filesystem(sectors),
_config(config)
{
}
@@ -64,97 +64,98 @@ public:
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
{
if (!path.empty())
throw FileNotFoundException();
if (!path.empty())
throw FileNotFoundException();
std::vector<std::unique_ptr<Dirent>> result;
for (auto& dirent : findAllFiles())
result.push_back(std::move(dirent));
std::vector<std::unique_ptr<Dirent>> result;
for (auto& dirent : findAllFiles())
result.push_back(std::move(dirent));
return result;
}
Bytes getFile(const Path& path)
{
auto fat = readFat();
auto dirent = findFile(path);
int sector = dirent->start_sector;
Bytes getFile(const Path& path)
{
auto fat = readFat();
auto dirent = findFile(path);
int sector = dirent->start_sector;
Bytes data;
ByteWriter bw(data);
while ((sector != 0) && (sector != 0xffff))
{
bw += getLogicalSector(sector - 1);
sector = fat.at(sector);
}
Bytes data;
ByteWriter bw(data);
while ((sector != 0) && (sector != 0xffff))
{
bw += getLogicalSector(sector - 1);
sector = fat.at(sector);
}
return data;
}
return data;
}
std::map<std::string, std::string> getMetadata(const Path& path)
{
std::map<std::string, std::string> attributes;
std::map<std::string, std::string> getMetadata(const Path& path)
{
std::map<std::string, std::string> attributes;
auto dirent = findFile(path);
attributes["filename"] = dirent->filename;
attributes["length"] = fmt::format("{}", dirent->length);
attributes["type"] = "file";
attributes["mode"] = dirent->mode;
attributes["brother120.inode"] = fmt::format("{}", dirent->inode);
attributes["brother120.start_sector"] = fmt::format("{}", dirent->start_sector);
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
auto dirent = findFile(path);
attributes["filename"] = dirent->filename;
attributes["length"] = fmt::format("{}", dirent->length);
attributes["type"] = "file";
attributes["mode"] = dirent->mode;
attributes["brother120.inode"] = fmt::format("{}", dirent->inode);
attributes["brother120.start_sector"] =
fmt::format("{}", dirent->start_sector);
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
return attributes;
}
return attributes;
}
private:
std::vector<uint32_t> readFat()
{
Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS);
ByteReader br(bytes);
std::vector<uint32_t> table;
std::vector<uint32_t> readFat()
{
Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS);
ByteReader br(bytes);
std::vector<uint32_t> table;
table.push_back(0xffff);
for (int sector = 1; sector != SECTOR_COUNT; sector++)
table.push_back(br.read_be16());
table.push_back(0xffff);
for (int sector = 1; sector != SECTOR_COUNT; sector++)
table.push_back(br.read_be16());
return table;
}
return table;
}
std::vector<std::unique_ptr<Brother120Dirent>> findAllFiles()
{
std::vector<std::unique_ptr<Brother120Dirent>> result;
int inode = 0;
for (int block = 0; block < DIRECTORY_SECTORS; block++)
{
auto bytes = getLogicalSector(block);
for (int d = 0; d < SECTOR_SIZE/16; d++, inode++)
{
Bytes buffer = bytes.slice(d*16, 16);
if (buffer[0] == 0xf0)
continue;
std::vector<std::unique_ptr<Brother120Dirent>> result;
int inode = 0;
for (int block = 0; block < DIRECTORY_SECTORS; block++)
{
auto bytes = getLogicalSector(block);
for (int d = 0; d < SECTOR_SIZE / 16; d++, inode++)
{
Bytes buffer = bytes.slice(d * 16, 16);
if (buffer[0] == 0xf0)
continue;
result.push_back(
std::make_unique<Brother120Dirent>(inode, buffer));
}
}
result.push_back(
std::make_unique<Brother120Dirent>(inode, buffer));
}
}
return result;
}
std::unique_ptr<Brother120Dirent> findFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
std::unique_ptr<Brother120Dirent> findFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
for (auto& dirent : findAllFiles())
for (auto& dirent : findAllFiles())
{
if (dirent->filename == path[0])
return std::move(dirent);
if (dirent->filename == path[0])
return std::move(dirent);
}
throw FileNotFoundException();
}
throw FileNotFoundException();
}
private:
const Brother120FsProto& _config;

View File

@@ -6,11 +6,11 @@
enum
{
DEL,
SEQ,
PRG,
USR,
REL
DEL,
SEQ,
PRG,
USR,
REL
};
static std::string fromPetscii(const Bytes& bytes)
@@ -30,25 +30,31 @@ static std::string fromPetscii(const Bytes& bytes)
static std::string toMode(uint8_t cbm_type)
{
std::stringstream ss;
if (cbm_type & 0x40)
ss << 'L';
if (cbm_type & 0x80)
ss << 'S';
return ss.str();
std::stringstream ss;
if (cbm_type & 0x40)
ss << 'L';
if (cbm_type & 0x80)
ss << 'S';
return ss.str();
}
static std::string toFileType(uint8_t cbm_type)
{
switch (cbm_type & 0x0f)
{
case DEL: return "DEL";
case SEQ: return "SEQ";
case PRG: return "PRG";
case USR: return "USR";
case REL: return "REL";
default: return fmt::format("[bad type {:x}]", cbm_type & 0x0f);
}
switch (cbm_type & 0x0f)
{
case DEL:
return "DEL";
case SEQ:
return "SEQ";
case PRG:
return "PRG";
case USR:
return "USR";
case REL:
return "REL";
default:
return fmt::format("[bad type {:x}]", cbm_type & 0x0f);
}
}
class CbmfsDirent : public Dirent
@@ -63,7 +69,7 @@ public:
start_track = br.read_8();
start_sector = br.read_8();
auto filenameBytes = br.read(16).split(0xa0)[0];
auto filenameBytes = br.read(16).split(0xa0)[0];
filename = fromPetscii(filenameBytes);
side_track = br.read_8();
side_sector = br.read_8();
@@ -103,8 +109,8 @@ public:
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
{
if (path.size() != 0)
throw BadPathException();
if (path.size() != 0)
throw BadPathException();
std::vector<std::unique_ptr<Dirent>> results;
uint8_t t = _config.directory_track();
@@ -131,64 +137,64 @@ public:
std::map<std::string, std::string> getMetadata(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
auto de = findFile(unhex(path[0]));
if (!de)
throw FileNotFoundException();
std::map<std::string, std::string> attributes;
attributes["filename"] = de->filename;
attributes["length"] = fmt::format("{}", de->length);
attributes["mode"] = de->mode;
attributes["type"] = "file";
attributes["cbmfs.type"] = toFileType(de->cbm_type);
attributes["cbmfs.start_track"] = fmt::format("{}", de->start_track);
attributes["cbmfs.start_sector"] = fmt::format("{}", de->start_sector);
attributes["cbmfs.side_track"] = fmt::format("{}", de->side_track);
attributes["cbmfs.side_sector"] = fmt::format("{}", de->side_sector);
attributes["cbmfs.recordlen"] = fmt::format("{}", de->recordlen);
attributes["cbmfs.sectors"] = fmt::format("{}", de->sectors);
return attributes;
}
Bytes getFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
auto de = findFile(unhex(path[0]));
if (!de)
throw FileNotFoundException();
if (de->cbm_type == REL)
throw UnimplementedFilesystemException("cannot read .REL files");
auto de = findFile(unhex(path[0]));
if (!de)
throw FileNotFoundException();
Bytes bytes;
ByteWriter bw(bytes);
std::map<std::string, std::string> attributes;
attributes["filename"] = de->filename;
attributes["length"] = fmt::format("{}", de->length);
attributes["mode"] = de->mode;
attributes["type"] = "file";
attributes["cbmfs.type"] = toFileType(de->cbm_type);
attributes["cbmfs.start_track"] = fmt::format("{}", de->start_track);
attributes["cbmfs.start_sector"] = fmt::format("{}", de->start_sector);
attributes["cbmfs.side_track"] = fmt::format("{}", de->side_track);
attributes["cbmfs.side_sector"] = fmt::format("{}", de->side_sector);
attributes["cbmfs.recordlen"] = fmt::format("{}", de->recordlen);
attributes["cbmfs.sectors"] = fmt::format("{}", de->sectors);
return attributes;
}
Bytes getFile(const Path& path)
{
if (path.size() != 1)
throw BadPathException();
auto de = findFile(unhex(path[0]));
if (!de)
throw FileNotFoundException();
if (de->cbm_type == REL)
throw UnimplementedFilesystemException("cannot read .REL files");
Bytes bytes;
ByteWriter bw(bytes);
uint8_t t = de->start_track - 1;
uint8_t s = de->start_sector;
for (;;)
for (;;)
{
auto b = getSector(t, 0, s);
if (b[0])
bw += b.slice(2);
else
{
bw += b.slice(2, b[1]);
break;
}
if (b[0])
bw += b.slice(2);
else
{
bw += b.slice(2, b[1]);
break;
}
t = b[0] - 1;
s = b[1];
}
return bytes;
}
return bytes;
}
private:
std::unique_ptr<CbmfsDirent> findFile(const std::string& filename)
{
std::unique_ptr<CbmfsDirent> findFile(const std::string& filename)
{
uint8_t t = _config.directory_track();
uint8_t s = 1;
while (t != 0xff)
@@ -201,17 +207,17 @@ private:
if (dbuf[2] == 0)
continue;
auto de = std::make_unique<CbmfsDirent>(dbuf);
if (de->filename == filename)
return de;
auto de = std::make_unique<CbmfsDirent>(dbuf);
if (de->filename == filename)
return de;
}
t = b[0] - 1;
s = b[1];
}
return nullptr;
}
return nullptr;
}
private:
const CbmfsProto& _config;

View File

@@ -116,6 +116,30 @@ public:
return bytes;
}
void putFile(const Path& path, const Bytes& bytes)
{
mount();
auto pathstr = path.to_str();
FIL fil;
FRESULT res =
f_open(&fil, pathstr.c_str(), FA_WRITE | FA_CREATE_ALWAYS);
throwError(res);
unsigned remaining = bytes.size();
char* ptr = (char*)bytes.cbegin();
while (remaining != 0)
{
UINT done;
res = f_write(&fil, ptr, remaining, &done);
throwError(res);
remaining -= done;
ptr += done;
}
f_close(&fil);
}
public:
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
{
@@ -126,7 +150,9 @@ public:
DRESULT diskWrite(const BYTE* buffer, LBA_t sector, UINT count)
{
return RES_WRPRT;
Bytes bytes(buffer, count * getLogicalSectorSize());
putLogicalSector(sector, bytes);
return RES_OK;
}
DRESULT diskIoctl(BYTE cmd, void* buffer)
@@ -163,6 +189,16 @@ private:
case FR_NO_PATH:
throw FileNotFoundException();
case FR_INVALID_NAME:
throw BadPathException();
case FR_DENIED:
case FR_EXIST:
case FR_WRITE_PROTECTED:
case FR_MKFS_ABORTED:
case FR_LOCKED:
throw CannotWriteException();
case FR_NO_FILESYSTEM:
throw BadFilesystemException();

View File

@@ -4,14 +4,20 @@
#include "lib/readerwriter.h"
#include "lib/decoders/decoders.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/layout.h"
#include "lib/proto.h"
#include "lib/mapper.h"
class FluxSectorInterface : public SectorInterface
{
public:
FluxSectorInterface(std::shared_ptr<FluxSource> fluxSource,
std::shared_ptr<FluxSink> fluxSink,
std::shared_ptr<AbstractEncoder> encoder,
std::shared_ptr<AbstractDecoder> decoder):
_fluxSource(fluxSource),
_fluxSink(fluxSink),
_encoder(encoder),
_decoder(decoder)
{
}
@@ -20,47 +26,124 @@ public:
std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId)
{
auto it = _changedSectors.get(track, side, sectorId);
if (it)
return it;
trackid_t trackid(track, side);
if (_loadedtracks.find(trackid) == _loadedtracks.end())
populateSectors(track, side);
populateSectors(track, side);
return _sectorstore.get(track, side, sectorId);
return _readSectors.get(track, side, sectorId);
}
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId)
{
trackid_t trackid(track, side);
if (_loadedtracks.find(trackid) == _loadedtracks.end())
populateSectors(track, side);
_changedtracks.insert(trackid);
return _changedSectors.put(track, side, sectorId);
}
_changedtracks.insert(trackid);
return _sectorstore.put(track, side, sectorId);
void flush()
{
for (const auto& trackid : _changedtracks)
{
unsigned track = trackid.first;
unsigned side = trackid.second;
auto layoutdata = Layout::getLayoutOfTrack(track, side);
auto sectors = Layout::getSectorsInTrack(layoutdata);
config.mutable_tracks()->Clear();
config.mutable_tracks()->set_start(track);
config.mutable_heads()->Clear();
config.mutable_heads()->set_start(side);
/* Check to see if we have all sectors of this track in the
* changesectors image. */
if (imageContainsAllSectorsOf(
_changedSectors, track, side, sectors))
{
/* Just write directly from the changedsectors image. */
writeDiskCommand(_changedSectors,
*_encoder,
*_fluxSink,
&*_decoder,
&*_fluxSource);
}
else
{
/* Only a few sectors have changed. Do we need to populate the
* track? */
if (_loadedtracks.find(trackid) == _loadedtracks.end())
populateSectors(track, side);
/* Now merge the loaded track with the changed one, and write
* the result back. */
Image image;
for (const unsigned sector : sectors)
{
auto s = image.put(track, side, sector);
if (_changedSectors.contains(track, side, sector))
s->data =
_changedSectors.get(track, side, sector)->data;
else
s->data = _readSectors.get(track, side, sector)->data;
}
writeDiskCommand(
image, *_encoder, *_fluxSink, &*_decoder, &*_fluxSource);
}
}
}
private:
bool imageContainsAllSectorsOf(const Image& image,
unsigned track,
unsigned side,
const std::vector<unsigned>& sectors)
{
for (unsigned sector : sectors)
{
if (!image.contains(track, side, sector))
return false;
}
return true;
}
void populateSectors(unsigned track, unsigned side)
{
auto location = Mapper::computeLocationFor(track, side);
auto trackdata =
readAndDecodeTrack(*_fluxSource, *_decoder, location);
auto location = Mapper::computeLocationFor(track, side);
auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
for (const auto& sector : trackdata->sectors)
*_sectorstore.put(track, side, sector->logicalSector) = *sector;
_loadedtracks.insert(trackid_t(track, side));
for (const auto& sector : trackdata->sectors)
*_readSectors.put(track, side, sector->logicalSector) = *sector;
_loadedtracks.insert(trackid_t(track, side));
}
std::shared_ptr<FluxSource> _fluxSource;
std::shared_ptr<FluxSink> _fluxSink;
std::shared_ptr<AbstractEncoder> _encoder;
std::shared_ptr<AbstractDecoder> _decoder;
typedef std::pair<unsigned, unsigned> trackid_t;
Image _sectorstore;
Image _readSectors;
Image _changedSectors;
std::set<trackid_t> _loadedtracks;
std::set<trackid_t> _changedtracks;
};
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(
std::shared_ptr<FluxSource> fluxSource, std::shared_ptr<AbstractDecoder> decoder)
std::shared_ptr<FluxSource> fluxSource,
std::shared_ptr<FluxSink> fluxSink,
std::shared_ptr<AbstractEncoder> encoder,
std::shared_ptr<AbstractDecoder> decoder)
{
return std::make_unique<FluxSectorInterface>(fluxSource, decoder);
return std::make_unique<FluxSectorInterface>(
fluxSource, fluxSink, encoder, decoder);
}

View File

@@ -5,27 +5,27 @@
class ImageSectorInterface : public SectorInterface
{
public:
ImageSectorInterface(std::shared_ptr<Image> image):
_image(image)
{}
ImageSectorInterface(std::shared_ptr<Image> image): _image(image) {}
public:
std::shared_ptr<const Sector> get(unsigned track, unsigned side, unsigned sectorId)
{
return _image->get(track, side, sectorId);
}
std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId)
{
return _image->get(track, side, sectorId);
}
std::shared_ptr<Sector> put(unsigned track, unsigned side, unsigned sectorId)
{
return _image->put(track, side, sectorId);
}
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId)
{
return _image->put(track, side, sectorId);
}
private:
std::shared_ptr<Image> _image;
std::shared_ptr<Image> _image;
};
std::unique_ptr<SectorInterface> SectorInterface::createImageSectorInterface(std::shared_ptr<Image> image)
std::unique_ptr<SectorInterface> SectorInterface::createImageSectorInterface(
std::shared_ptr<Image> image)
{
return std::make_unique<ImageSectorInterface>(image);
return std::make_unique<ImageSectorInterface>(image);
}

View File

@@ -130,8 +130,8 @@ public:
hfsdirent de;
hfs_fstat(file, &de);
a.creator = Bytes(de.u.file.creator);
a.type = Bytes(de.u.file.type);
a.creator = Bytes(de.u.file.creator);
a.type = Bytes(de.u.file.type);
hfs_setfork(file, 0);
a.data = readBytes(file);
@@ -141,6 +141,38 @@ public:
return a.render();
}
void putFile(const Path& path, const Bytes& bytes)
{
HfsMount m(this);
if (path.size() == 0)
throw BadPathException();
AppleSingle a;
try
{
a.parse(bytes);
}
catch (const InvalidFileException& e)
{
throw UnimplementedFilesystemException(
"you can only write valid AppleSingle encoded files");
}
auto pathstr = ":" + path.to_str(":");
hfs_delete(_vol, pathstr.c_str());
HfsFile file(hfs_create(_vol,
pathstr.c_str(),
(const char*)a.type.cbegin(),
(const char*)a.creator.cbegin()));
if (!file)
throw CannotWriteException();
hfs_setfork(file, 0);
writeBytes(file, a.data);
hfs_setfork(file, 1);
writeBytes(file, a.rsrc);
}
private:
Bytes readBytes(hfsfile* file)
{
@@ -161,6 +193,17 @@ private:
return bytes;
}
void writeBytes(hfsfile* file, const Bytes& bytes)
{
unsigned pos = 0;
while (pos != bytes.size())
{
unsigned long done =
hfs_write(file, bytes.cbegin() + pos, bytes.size() - pos);
pos += done;
}
}
private:
class HfsMount
{
@@ -183,23 +226,39 @@ private:
class HfsFile
{
public:
HfsFile(hfsfile* file): _file(file) {}
~HfsFile() { hfs_close(_file); }
HfsFile(hfsfile* file): _file(file) {}
~HfsFile()
{
if (_file)
hfs_close(_file);
}
operator hfsfile*() const
{
return _file;
}
operator hfsfile* () const { return _file; }
private:
hfsfile* _file;
hfsfile* _file;
};
class HfsDir
{
public:
HfsDir(hfsdir* dir): _dir(dir) {}
~HfsDir() { hfs_closedir(_dir); }
HfsDir(hfsdir* dir): _dir(dir) {}
~HfsDir()
{
if (_dir)
hfs_closedir(_dir);
}
operator hfsdir*() const
{
return _dir;
}
operator hfsdir* () const { return _dir; }
private:
hfsdir* _dir;
hfsdir* _dir;
};
private:
@@ -227,7 +286,10 @@ private:
void** priv, const void* buffer, unsigned long len);
unsigned long hfsWrite(const void* buffer, unsigned long len)
{
return -1;
Bytes bytes((const uint8_t*)buffer, len * 512);
putLogicalSector(_seek, bytes);
_seek += len;
return len;
}
private:

View File

@@ -4,7 +4,9 @@
class Image;
class Sector;
class FluxSource;
class FluxSink;
class AbstractDecoder;
class AbstractEncoder;
class SectorInterface
{
@@ -21,6 +23,8 @@ public:
std::shared_ptr<Image> image);
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
std::shared_ptr<FluxSource> fluxSource,
std::shared_ptr<FluxSink> fluxSink,
std::shared_ptr<AbstractEncoder> encoder,
std::shared_ptr<AbstractDecoder> decoder);
};

View File

@@ -33,6 +33,57 @@ std::string Path::to_str(const std::string sep) const
return join(*this, sep);
}
void Filesystem::create()
{
throw UnimplementedFilesystemException();
}
FilesystemStatus Filesystem::check()
{
throw UnimplementedFilesystemException();
}
std::vector<std::unique_ptr<Dirent>> Filesystem::list(const Path& path)
{
throw UnimplementedFilesystemException();
}
Bytes Filesystem::getFile(const Path& path)
{
throw UnimplementedFilesystemException();
}
void Filesystem::putFile(const Path& path, const Bytes& data)
{
throw UnimplementedFilesystemException();
}
std::map<std::string, std::string> Filesystem::getMetadata(const Path& path)
{
throw UnimplementedFilesystemException();
}
void Filesystem::putMetadata(
const Path& path, const std::map<std::string, std::string>& metadata)
{
throw UnimplementedFilesystemException();
}
void Filesystem::createDirectory(const Path& path)
{
throw UnimplementedFilesystemException();
}
void Filesystem::deleteFile(const Path& path)
{
throw UnimplementedFilesystemException();
}
void Filesystem::flush()
{
_sectors->flush();
}
Filesystem::Filesystem(std::shared_ptr<SectorInterface> sectors):
_sectors(sectors)
{
@@ -125,8 +176,19 @@ void Filesystem::putLogicalSector(uint32_t number, const Bytes& data)
if (number >= _locations.size())
throw BadFilesystemException();
auto& i = _locations[number];
_sectors->put(std::get<0>(i), std::get<1>(i), std::get<2>(i))->data = data;
unsigned pos = 0;
while (pos < data.size())
{
auto& it = _locations[number];
int track = std::get<0>(it);
int side = std::get<1>(it);
int sector = std::get<2>(it);
int sectorSize = Layout::getLayoutOfTrack(track, side).sector_size();
_sectors->put(track, side, sector)->data = data.slice(pos, sectorSize);
pos += sectorSize;
number++;
}
}
unsigned Filesystem::getOffsetOfSector(

View File

@@ -60,6 +60,12 @@ public:
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
};
class CannotWriteException : public FilesystemException
{
public:
CannotWriteException(): FilesystemException("Cannot write file") {}
};
class ReadOnlyFilesystemException : public FilesystemException
{
public:
@@ -95,51 +101,17 @@ public:
class Filesystem
{
public:
virtual void create()
{
throw UnimplementedFilesystemException();
}
virtual FilesystemStatus check()
{
throw UnimplementedFilesystemException();
}
virtual std::vector<std::unique_ptr<Dirent>> list(const Path& path)
{
throw UnimplementedFilesystemException();
}
virtual Bytes getFile(const Path& path)
{
throw UnimplementedFilesystemException();
}
virtual void putFile(const Path& path, const Bytes& data)
{
throw UnimplementedFilesystemException();
}
virtual std::map<std::string, std::string> getMetadata(const Path& path)
{
throw UnimplementedFilesystemException();
}
virtual void create();
virtual FilesystemStatus check();
virtual std::vector<std::unique_ptr<Dirent>> list(const Path& path);
virtual Bytes getFile(const Path& path);
virtual void putFile(const Path& path, const Bytes& data);
virtual std::map<std::string, std::string> getMetadata(const Path& path);
virtual void putMetadata(
const Path& path, const std::map<std::string, std::string>& metadata)
{
throw UnimplementedFilesystemException();
}
virtual void createDirectory(const Path& path)
{
throw UnimplementedFilesystemException();
}
virtual void deleteFile(const Path& path)
{
throw UnimplementedFilesystemException();
}
const Path& path, const std::map<std::string, std::string>& metadata);
virtual void createDirectory(const Path& path);
virtual void deleteFile(const Path& path);
void flush();
protected:
Filesystem(std::shared_ptr<SectorInterface> sectors);
@@ -149,7 +121,7 @@ protected:
Bytes getLogicalSector(uint32_t number, uint32_t count = 1);
void putLogicalSector(uint32_t number, const Bytes& data);
unsigned getOffsetOfSector(unsigned track, unsigned side, unsigned sector);
unsigned getOffsetOfSector(unsigned track, unsigned side, unsigned sector);
unsigned getLogicalSectorCount();
unsigned getLogicalSectorSize(unsigned track = 0, unsigned side = 0);

View File

@@ -2,60 +2,70 @@ syntax = "proto2";
import "lib/common.proto";
message AcornDfsProto {
enum Flavour {
UNDEFINED = 0;
ACORN_DFS = 1;
}
message AcornDfsProto
{
enum Flavour
{
UNDEFINED = 0;
ACORN_DFS = 1;
}
optional Flavour flavour = 1 [default = ACORN_DFS, (help) = "which flavour of DFS to implement"];
optional Flavour flavour = 1
[ default = ACORN_DFS, (help) = "which flavour of DFS to implement" ];
}
message Brother120FsProto {
message Brother120FsProto {}
message FatFsProto {}
message CpmFsProto
{
message Location
{
optional uint32 track = 1 [ (help) = "track number" ];
optional uint32 side = 2 [ (help) = "side number" ];
optional uint32 sector = 3 [ (help) = "sector ID" ];
}
message Padding
{
optional uint32 amount = 1
[ (help) = "number of sectors of padding to insert" ];
optional uint32 every = 2
[ (help) = "insert padding after this many sectors" ];
}
optional Location filesystem_start = 1
[ (help) = "position of the start of the filesystem" ];
optional int32 block_size = 2 [ (help) = "allocation block size" ];
optional int32 dir_entries = 3
[ (help) = "number of entries in the directory" ];
optional Padding padding = 4
[ (help) = "wasted sectors not considered part of the filesystem" ];
}
message FatFsProto {
message AmigaFfsProto {}
message MacHfsProto {}
message CbmfsProto
{
optional uint32 directory_track = 1 [
default = 17,
(help) = "which track the directory is on (zero-based numbering)"
];
}
message CpmFsProto {
message Location {
optional uint32 track = 1 [(help) = "track number"];
optional uint32 side = 2 [(help) = "side number"];
optional uint32 sector = 3 [(help) = "sector ID"];
}
message Padding {
optional uint32 amount = 1 [(help) = "number of sectors of padding to insert"];
optional uint32 every = 2 [(help) = "insert padding after this many sectors"];
}
optional Location filesystem_start = 1 [(help) = "position of the start of the filesystem"];
optional int32 block_size = 2 [(help) = "allocation block size"];
optional int32 dir_entries = 3 [(help) = "number of entries in the directory"];
optional Padding padding = 4 [(help) = "wasted sectors not considered part of the filesystem"];
message FilesystemProto
{
oneof filesystem
{
AcornDfsProto acorndfs = 1;
Brother120FsProto brother120 = 2;
FatFsProto fatfs = 3;
CpmFsProto cpmfs = 4;
AmigaFfsProto amigaffs = 5;
MacHfsProto machfs = 6;
CbmfsProto cbmfs = 7;
}
}
message AmigaFfsProto {
}
message MacHfsProto {
}
message CbmfsProto {
optional uint32 directory_track = 1
[default = 17,
(help) = "which track the directory is on (zero-based numbering)"];
}
message FilesystemProto {
oneof filesystem {
AcornDfsProto acorndfs = 1;
Brother120FsProto brother120 = 2;
FatFsProto fatfs = 3;
CpmFsProto cpmfs = 4;
AmigaFfsProto amigaffs = 5;
MacHfsProto machfs = 6;
CbmfsProto cbmfs = 7;
}
}

View File

@@ -7,6 +7,7 @@ FLUXENGINE_SRCS = \
src/fe-getfileinfo.cc \
src/fe-inspect.cc \
src/fe-ls.cc \
src/fe-putfile.cc \
src/fe-rawread.cc \
src/fe-rawwrite.cc \
src/fe-read.cc \

View File

@@ -18,8 +18,8 @@
static FlagGroup flags({&fileFlags});
static StringFlag directory({"-p", "--path"}, "path to work on", "");
static StringFlag output({"-o", "--output"}, "local filename to write to", "");
static StringFlag directory({"-p", "--path"}, "disk path to work on", "");
static StringFlag output({"-l", "--local"}, "local filename to write to", "");
int mainGetFile(int argc, const char* argv[])
{

51
src/fe-putfile.cc Normal file
View File

@@ -0,0 +1,51 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sector.h"
#include "proto.h"
#include "readerwriter.h"
#include "imagereader/imagereader.h"
#include "imagewriter/imagewriter.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/decoders/decoders.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include "lib/vfs/sectorinterface.h"
#include "lib/vfs/vfs.h"
#include "src/fileutils.h"
#include <google/protobuf/text_format.h>
#include <fstream>
static FlagGroup flags({&fileFlags});
static StringFlag path({"-p", "--path"}, "path to work on", "");
static StringFlag input({"-l", "--local"}, "local filename to read from", "");
int mainPutFile(int argc, const char* argv[])
{
if (argc == 1)
showProfiles("putfile", formats);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
try
{
std::string inputFilename = input;
if (inputFilename.empty())
Error() << "you must supply a local file to read from";
Path outputFilename(path);
if (outputFilename.size() == 0)
Error() << "you must supply a destination path to write to";
auto data = Bytes::readFromFile(inputFilename);
auto filesystem = createFilesystemFromConfig();
filesystem->putFile(outputFilename, data);
filesystem->flush();
}
catch (const FilesystemException& e)
{
Error() << e.message;
}
return 0;
}

View File

@@ -86,7 +86,7 @@ int mainWrite(int argc, const char* argv[])
if (verify && config.has_flux_source() && config.flux_source().has_drive())
fluxSource = FluxSource::create(config.flux_source());
writeDiskCommand(image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
return 0;
}

View File

@@ -5,7 +5,9 @@
#include "proto.h"
#include "readerwriter.h"
#include "lib/decoders/decoders.h"
#include "lib/encoders/encoders.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/fluxsink/fluxsink.h"
#include "lib/imagereader/imagereader.h"
#include "fmt/format.h"
#include "fluxengine.h"
@@ -32,6 +34,8 @@ static StringFlag flux({"-f", "--flux"},
{
FluxSource::updateConfigForFilename(
config.mutable_flux_source(), value);
FluxSink::updateConfigForFilename(
config.mutable_flux_sink(), value);
});
std::unique_ptr<Filesystem> createFilesystemFromConfig()
@@ -40,8 +44,10 @@ std::unique_ptr<Filesystem> createFilesystemFromConfig()
if (config.has_flux_source())
{
std::shared_ptr<FluxSource> fluxSource(FluxSource::create(config.flux_source()));
std::shared_ptr<FluxSink> fluxSink(FluxSink::create(config.flux_sink()));
std::shared_ptr<AbstractEncoder> encoder(AbstractEncoder::create(config.encoder()));
std::shared_ptr<AbstractDecoder> decoder(AbstractDecoder::create(config.decoder()));
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, decoder);
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, fluxSink, encoder, decoder);
}
else
{

View File

@@ -10,6 +10,7 @@ extern command_cb mainGetFile;
extern command_cb mainGetFileInfo;
extern command_cb mainInspect;
extern command_cb mainLs;
extern command_cb mainPutFile;
extern command_cb mainRawRead;
extern command_cb mainRawWrite;
extern command_cb mainRead;
@@ -40,6 +41,7 @@ static std::vector<Command> commands =
{ "ls", mainLs, "Show files on disk (or image).", },
{ "getfile", mainGetFile, "Read a file off a disk (or image).", },
{ "getfileinfo", mainGetFileInfo, "Read file metadata off a disk (or image).", },
{ "putfile", mainPutFile, "Write a file to disk (or image).", },
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
{ "seek", mainSeek, "Moves the disk head.", },
{ "test", mainTest, "Various testing commands.", },

View File

@@ -157,7 +157,7 @@ void MainWindow::OnWriteFluxButton(wxCommandEvent&)
decoder = AbstractDecoder::create(config.decoder());
fluxSource = FluxSource::create(config.flux_source());
}
writeDiskCommand(image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
});
}
catch (const ErrorException& e)