mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Add creeate, put and delete support to the CP/M filesystem driver.
This commit is contained in:
317
lib/vfs/cpmfs.cc
317
lib/vfs/cpmfs.cc
@@ -1,19 +1,24 @@
|
|||||||
#include "lib/globals.h"
|
#include "lib/globals.h"
|
||||||
#include "lib/vfs/vfs.h"
|
#include "lib/vfs/vfs.h"
|
||||||
#include "lib/config.pb.h"
|
#include "lib/config.pb.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
class CpmFsFilesystem : public Filesystem
|
class CpmFsFilesystem : public Filesystem, public HasBitmap, public HasMount
|
||||||
{
|
{
|
||||||
class Entry
|
class Entry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Entry(const Bytes& bytes, int map_entry_size)
|
Entry(const Bytes& bytes, int map_entry_size, unsigned index):
|
||||||
|
index(index)
|
||||||
{
|
{
|
||||||
|
if (bytes[0] == 0xe5)
|
||||||
|
deleted = true;
|
||||||
|
|
||||||
user = bytes[0] & 0x0f;
|
user = bytes[0] & 0x0f;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << (char)(user + '0') << ':';
|
|
||||||
|
|
||||||
for (int i = 1; i <= 8; i++)
|
for (int i = 1; i <= 8; i++)
|
||||||
{
|
{
|
||||||
@@ -64,13 +69,117 @@ class CpmFsFilesystem : public Filesystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bytes toBytes(int map_entry_size) const
|
||||||
|
{
|
||||||
|
Bytes bytes(32);
|
||||||
|
ByteWriter bw(bytes);
|
||||||
|
|
||||||
|
if (deleted)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
bw.write_8(0xe5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bw.write_8(user);
|
||||||
|
|
||||||
|
/* Encode the filename. */
|
||||||
|
|
||||||
|
for (int i = 1; i < 12; i++)
|
||||||
|
bytes[i] = 0x20;
|
||||||
|
for (char c : filename)
|
||||||
|
{
|
||||||
|
if (islower(c))
|
||||||
|
throw BadPathException();
|
||||||
|
if (c == '.')
|
||||||
|
{
|
||||||
|
if (bw.pos >= 9)
|
||||||
|
throw BadPathException();
|
||||||
|
bw.seek(9);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((bw.pos == 9) || (bw.pos == 12))
|
||||||
|
throw BadPathException();
|
||||||
|
bw.write_8(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the mode. */
|
||||||
|
|
||||||
|
if (mode.find('R') != std::string::npos)
|
||||||
|
bytes[9] |= 0x80;
|
||||||
|
if (mode.find('S') != std::string::npos)
|
||||||
|
bytes[10] |= 0x80;
|
||||||
|
if (mode.find('A') != std::string::npos)
|
||||||
|
bytes[11] |= 0x80;
|
||||||
|
|
||||||
|
/* EX, S1, S2, RC */
|
||||||
|
|
||||||
|
bw.seek(12);
|
||||||
|
bw.write_8(extent & 0x1f); /* EX */
|
||||||
|
bw.write_8(0); /* S1 */
|
||||||
|
bw.write_8(extent >> 5); /* S2 */
|
||||||
|
bw.write_8(records); /* RC */
|
||||||
|
|
||||||
|
/* Allocation map. */
|
||||||
|
|
||||||
|
switch (map_entry_size)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
bw.write_8(allocation_map[i]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
bw.write_le16(allocation_map[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeFilename(const std::string& filename)
|
||||||
|
{
|
||||||
|
static std::regex FORMATTER("(?:(1?[0-9]):)?([^ .]+)\\.?([^ .]*)");
|
||||||
|
std::smatch results;
|
||||||
|
bool matched = std::regex_match(filename, results, FORMATTER);
|
||||||
|
if (!matched)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
std::string user = results[1];
|
||||||
|
std::string stem = results[2];
|
||||||
|
std::string ext = results[3];
|
||||||
|
|
||||||
|
if (stem.size() > 8)
|
||||||
|
throw BadPathException();
|
||||||
|
if (ext.size() > 3)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
this->user = std::stoi(user);
|
||||||
|
if (this->user > 15)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
if (ext.empty())
|
||||||
|
this->filename = stem;
|
||||||
|
else
|
||||||
|
this->filename = fmt::format("{}.{}", stem, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string combinedFilename() const
|
||||||
|
{
|
||||||
|
return fmt::format("{}:{}", user, filename);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
unsigned index;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string mode;
|
std::string mode;
|
||||||
unsigned user;
|
unsigned user;
|
||||||
unsigned extent;
|
unsigned extent;
|
||||||
unsigned records;
|
unsigned records;
|
||||||
std::vector<unsigned> allocation_map;
|
std::vector<unsigned> allocation_map;
|
||||||
|
bool deleted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -83,7 +192,8 @@ public:
|
|||||||
|
|
||||||
uint32_t capabilities() const override
|
uint32_t capabilities() const override
|
||||||
{
|
{
|
||||||
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
|
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_PUTFILE | OP_DELETE |
|
||||||
|
OP_GETDIRENT | OP_CREATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata() override
|
std::map<std::string, std::string> getMetadata() override
|
||||||
@@ -94,7 +204,7 @@ public:
|
|||||||
for (int d = 0; d < _config.dir_entries(); d++)
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
{
|
{
|
||||||
auto entry = getEntry(d);
|
auto entry = getEntry(d);
|
||||||
if (!entry)
|
if (entry->deleted)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (unsigned block : entry->allocation_map)
|
for (unsigned block : entry->allocation_map)
|
||||||
@@ -112,6 +222,17 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void create(bool, const std::string&) override
|
||||||
|
{
|
||||||
|
auto& start = _config.filesystem_start();
|
||||||
|
_filesystemStart =
|
||||||
|
getOffsetOfSector(start.track(), start.side(), start.sector());
|
||||||
|
_sectorSize = getLogicalSectorSize(start.track(), start.side());
|
||||||
|
|
||||||
|
_directory = Bytes{0xe5} * (_config.dir_entries() * 32);
|
||||||
|
putCpmBlock(0, _directory);
|
||||||
|
}
|
||||||
|
|
||||||
FilesystemStatus check() override
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
return FS_OK;
|
||||||
@@ -127,15 +248,15 @@ public:
|
|||||||
for (int d = 0; d < _config.dir_entries(); d++)
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
{
|
{
|
||||||
auto entry = getEntry(d);
|
auto entry = getEntry(d);
|
||||||
if (!entry)
|
if (entry->deleted)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& dirent = map[entry->filename];
|
auto& dirent = map[entry->combinedFilename()];
|
||||||
if (!dirent)
|
if (!dirent)
|
||||||
{
|
{
|
||||||
dirent = std::make_unique<Dirent>();
|
dirent = std::make_unique<Dirent>();
|
||||||
dirent->path = {entry->filename};
|
dirent->filename = entry->combinedFilename();
|
||||||
dirent->filename = entry->filename;
|
dirent->path = {dirent->filename};
|
||||||
dirent->mode = entry->mode;
|
dirent->mode = entry->mode;
|
||||||
dirent->length = 0;
|
dirent->length = 0;
|
||||||
dirent->file_type = TYPE_FILE;
|
dirent->file_type = TYPE_FILE;
|
||||||
@@ -190,9 +311,9 @@ public:
|
|||||||
for (int d = 0; d < _config.dir_entries(); d++)
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
{
|
{
|
||||||
entry = getEntry(d);
|
entry = getEntry(d);
|
||||||
if (!entry)
|
if (entry->deleted)
|
||||||
continue;
|
continue;
|
||||||
if (path[0] != entry->filename)
|
if (path[0] != entry->combinedFilename())
|
||||||
continue;
|
continue;
|
||||||
if (entry->extent < logicalExtent)
|
if (entry->extent < logicalExtent)
|
||||||
continue;
|
continue;
|
||||||
@@ -201,7 +322,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entry)
|
if (entry->deleted)
|
||||||
{
|
{
|
||||||
if (logicalExtent == 0)
|
if (logicalExtent == 0)
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
@@ -236,8 +357,116 @@ public:
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
public:
|
||||||
void mount()
|
void putFile(const Path& path, const Bytes& bytes) override
|
||||||
|
{
|
||||||
|
mount();
|
||||||
|
if (path.size() != 1)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
/* Test to see if the file already exists. */
|
||||||
|
|
||||||
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Entry> entry = getEntry(d);
|
||||||
|
if (entry->deleted)
|
||||||
|
continue;
|
||||||
|
if (path[0] == entry->combinedFilename())
|
||||||
|
throw CannotWriteException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write blocks, one at a time. */
|
||||||
|
|
||||||
|
std::unique_ptr<Entry> entry;
|
||||||
|
ByteReader br(bytes);
|
||||||
|
while (!br.eof())
|
||||||
|
{
|
||||||
|
unsigned extent = br.pos / 0x4000;
|
||||||
|
Bytes block = br.read(_config.block_size());
|
||||||
|
|
||||||
|
/* Allocate a block and write it. */
|
||||||
|
|
||||||
|
auto bit = std::find(_bitmap.begin(), _bitmap.end(), false);
|
||||||
|
if (bit == _bitmap.end())
|
||||||
|
throw DiskFullException();
|
||||||
|
*bit = true;
|
||||||
|
unsigned blocknum = bit - _bitmap.begin();
|
||||||
|
putCpmBlock(blocknum, block);
|
||||||
|
|
||||||
|
/* Do we need a new directory entry? */
|
||||||
|
|
||||||
|
if (!entry ||
|
||||||
|
entry->allocation_map[std::size(entry->allocation_map) - 1])
|
||||||
|
{
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
entry->records = 0x80;
|
||||||
|
putEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.reset();
|
||||||
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
|
{
|
||||||
|
entry = getEntry(d);
|
||||||
|
if (entry->deleted)
|
||||||
|
break;
|
||||||
|
entry.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
throw DiskFullException();
|
||||||
|
entry->deleted = false;
|
||||||
|
entry->changeFilename(path[0]);
|
||||||
|
entry->extent = extent;
|
||||||
|
entry->mode = "";
|
||||||
|
std::fill(entry->allocation_map.begin(),
|
||||||
|
entry->allocation_map.end(),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hook up the block in the allocation map. */
|
||||||
|
|
||||||
|
auto mit = std::find(
|
||||||
|
entry->allocation_map.begin(), entry->allocation_map.end(), 0);
|
||||||
|
*mit = blocknum;
|
||||||
|
}
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
entry->records = ((bytes.size() & 0x3fff) + 127) / 128;
|
||||||
|
putEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteFile(const Path& path) override
|
||||||
|
{
|
||||||
|
mount();
|
||||||
|
if (path.size() != 1)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
/* Remove all dirents for this file. */
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
|
{
|
||||||
|
auto entry = getEntry(d);
|
||||||
|
if (entry->deleted)
|
||||||
|
continue;
|
||||||
|
if (path[0] != entry->combinedFilename())
|
||||||
|
continue;
|
||||||
|
entry->deleted = true;
|
||||||
|
putEntry(entry);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
throw FileNotFoundException();
|
||||||
|
unmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void mount() override
|
||||||
{
|
{
|
||||||
auto& start = _config.filesystem_start();
|
auto& start = _config.filesystem_start();
|
||||||
_filesystemStart =
|
_filesystemStart =
|
||||||
@@ -268,26 +497,71 @@ private:
|
|||||||
_blocksPerLogicalExtent = 16384 / _config.block_size();
|
_blocksPerLogicalExtent = 16384 / _config.block_size();
|
||||||
|
|
||||||
_directory = getCpmBlock(0, _dirBlocks);
|
_directory = getCpmBlock(0, _dirBlocks);
|
||||||
|
|
||||||
|
/* Create the allocation bitmap. */
|
||||||
|
|
||||||
|
_bitmap.clear();
|
||||||
|
_bitmap.resize(_filesystemBlocks);
|
||||||
|
for (int d = 0; d < _dirBlocks; d++)
|
||||||
|
_bitmap[d] = true;
|
||||||
|
for (int d = 0; d < _config.dir_entries(); d++)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Entry> entry = getEntry(d);
|
||||||
|
if (entry->deleted)
|
||||||
|
continue;
|
||||||
|
for (unsigned block : entry->allocation_map)
|
||||||
|
{
|
||||||
|
if (block >= _filesystemBlocks)
|
||||||
|
throw BadFilesystemException();
|
||||||
|
if (block)
|
||||||
|
_bitmap[block] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unmount()
|
||||||
|
{
|
||||||
|
putCpmBlock(0, _directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
std::unique_ptr<Entry> getEntry(unsigned d)
|
std::unique_ptr<Entry> getEntry(unsigned d)
|
||||||
{
|
{
|
||||||
auto bytes = _directory.slice(d * 32, 32);
|
auto bytes = _directory.slice(d * 32, 32);
|
||||||
if (bytes[0] == 0xe5)
|
return std::make_unique<Entry>(bytes, _allocationMapSize, d);
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return std::make_unique<Entry>(bytes, _allocationMapSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getCpmBlock(uint32_t number, uint32_t count = 1)
|
void putEntry(std::unique_ptr<Entry>& entry)
|
||||||
{
|
{
|
||||||
unsigned sector = number * _blockSectors;
|
ByteWriter bw(_directory);
|
||||||
|
bw.seek(entry->index * 32);
|
||||||
|
bw.append(entry->toBytes(_allocationMapSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned computeSector(uint32_t block) const
|
||||||
|
{
|
||||||
|
unsigned sector = block * _blockSectors;
|
||||||
if (_config.has_padding())
|
if (_config.has_padding())
|
||||||
sector += (sector / _config.padding().every()) *
|
sector += (sector / _config.padding().every()) *
|
||||||
_config.padding().amount();
|
_config.padding().amount();
|
||||||
|
return sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes getCpmBlock(uint32_t block, uint32_t count = 1)
|
||||||
|
{
|
||||||
return getLogicalSector(
|
return getLogicalSector(
|
||||||
sector + _filesystemStart, _blockSectors * count);
|
computeSector(block) + _filesystemStart, _blockSectors * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putCpmBlock(uint32_t block, const Bytes& bytes)
|
||||||
|
{
|
||||||
|
putLogicalSector(computeSector(block) + _filesystemStart, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<bool> getBitmapForDebugging() override
|
||||||
|
{
|
||||||
|
return _bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -303,6 +577,7 @@ private:
|
|||||||
uint32_t _blocksPerLogicalExtent;
|
uint32_t _blocksPerLogicalExtent;
|
||||||
int _allocationMapSize;
|
int _allocationMapSize;
|
||||||
Bytes _directory;
|
Bytes _directory;
|
||||||
|
std::vector<bool> _bitmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Filesystem> Filesystem::createCpmFsFilesystem(
|
std::unique_ptr<Filesystem> Filesystem::createCpmFsFilesystem(
|
||||||
|
|||||||
@@ -277,4 +277,18 @@ public:
|
|||||||
static std::unique_ptr<Filesystem> createFilesystemFromConfig();
|
static std::unique_ptr<Filesystem> createFilesystemFromConfig();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Used for tests only. */
|
||||||
|
|
||||||
|
class HasBitmap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::vector<bool> getBitmapForDebugging() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HasMount
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void mount() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
120
tests/cpmfs.cc
120
tests/cpmfs.cc
@@ -37,14 +37,22 @@ namespace
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::ostream& operator<<(std::ostream& stream, const Bytes& bytes)
|
||||||
|
{
|
||||||
|
stream << '\n';
|
||||||
|
hexdump(stream, bytes);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
static Bytes createDirent(const std::string& filename,
|
static Bytes createDirent(const std::string& filename,
|
||||||
int extent,
|
int extent,
|
||||||
int records,
|
int records,
|
||||||
const std::initializer_list<int> blocks)
|
const std::initializer_list<int> blocks,
|
||||||
|
int user = 0)
|
||||||
{
|
{
|
||||||
Bytes dirent;
|
Bytes dirent;
|
||||||
ByteWriter bw(dirent);
|
ByteWriter bw(dirent);
|
||||||
bw.write_8(0);
|
bw.write_8(user);
|
||||||
bw.append(filename);
|
bw.append(filename);
|
||||||
while (bw.pos != 12)
|
while (bw.pos != 12)
|
||||||
bw.write_8(' ');
|
bw.write_8(' ');
|
||||||
@@ -69,6 +77,21 @@ static void setBlock(
|
|||||||
sectors->put(block, 0, i)->data = data.slice(i * 256, 256);
|
sectors->put(block, 0, i)->data = data.slice(i * 256, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Bytes getBlock(
|
||||||
|
const std::shared_ptr<SectorInterface>& sectors, int block, int length)
|
||||||
|
{
|
||||||
|
Bytes bytes;
|
||||||
|
ByteWriter bw(bytes);
|
||||||
|
|
||||||
|
for (int i = 0; i < (length + 127) / 128; i++)
|
||||||
|
{
|
||||||
|
auto sector = sectors->get(block, 0, i);
|
||||||
|
bw.append(sector->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
static void testPartialExtent()
|
static void testPartialExtent()
|
||||||
{
|
{
|
||||||
auto sectors = std::make_shared<TestSectorInterface>();
|
auto sectors = std::make_shared<TestSectorInterface>();
|
||||||
@@ -113,6 +136,93 @@ static void testLogicalExtents()
|
|||||||
AssertThat(data[0x4000 * 2], Equals(3));
|
AssertThat(data[0x4000 * 2], Equals(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testBitmap()
|
||||||
|
{
|
||||||
|
auto sectors = std::make_shared<TestSectorInterface>();
|
||||||
|
auto fs = Filesystem::createCpmFsFilesystem(
|
||||||
|
globalConfig()->filesystem(), sectors);
|
||||||
|
|
||||||
|
setBlock(sectors,
|
||||||
|
0,
|
||||||
|
createDirent("FILE", 1, 128, {1, 0, 0, 0, 0, 0, 0, 0, 2}) +
|
||||||
|
createDirent("FILE", 2, 128, {4}) + (blank_dirent * 62));
|
||||||
|
|
||||||
|
dynamic_cast<HasMount*>(fs.get())->mount();
|
||||||
|
std::vector<bool> bitmap =
|
||||||
|
dynamic_cast<HasBitmap*>(fs.get())->getBitmapForDebugging();
|
||||||
|
AssertThat(bitmap,
|
||||||
|
Equals(std::vector<bool>{
|
||||||
|
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testPutGet()
|
||||||
|
{
|
||||||
|
auto sectors = std::make_shared<TestSectorInterface>();
|
||||||
|
auto fs = Filesystem::createCpmFsFilesystem(
|
||||||
|
globalConfig()->filesystem(), sectors);
|
||||||
|
fs->create(true, "volume");
|
||||||
|
|
||||||
|
fs->putFile(Path("0:FILE1"), Bytes{1, 2, 3, 4});
|
||||||
|
fs->putFile(Path("0:FILE2"), Bytes{5, 6, 7, 8});
|
||||||
|
|
||||||
|
dynamic_cast<HasMount*>(fs.get())->mount();
|
||||||
|
std::vector<bool> bitmap =
|
||||||
|
dynamic_cast<HasBitmap*>(fs.get())->getBitmapForDebugging();
|
||||||
|
AssertThat(bitmap,
|
||||||
|
Equals(std::vector<bool>{
|
||||||
|
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
|
||||||
|
|
||||||
|
auto directory = getBlock(sectors, 0, 256).slice(0, 64);
|
||||||
|
AssertThat(directory,
|
||||||
|
Equals(createDirent("FILE1", 0, 1, {1}) +
|
||||||
|
createDirent("FILE2", 0, 1, {2})));
|
||||||
|
|
||||||
|
auto file1 = getBlock(sectors, 1, 8).slice(0, 8);
|
||||||
|
AssertThat(file1, Equals(Bytes{1, 2, 3, 4, 0, 0, 0, 0}));
|
||||||
|
|
||||||
|
auto file2 = getBlock(sectors, 2, 8).slice(0, 8);
|
||||||
|
AssertThat(file2, Equals(Bytes{5, 6, 7, 8, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testPutBigFile()
|
||||||
|
{
|
||||||
|
auto sectors = std::make_shared<TestSectorInterface>();
|
||||||
|
auto fs = Filesystem::createCpmFsFilesystem(
|
||||||
|
globalConfig()->filesystem(), sectors);
|
||||||
|
fs->create(true, "volume");
|
||||||
|
|
||||||
|
Bytes filedata;
|
||||||
|
ByteWriter bw(filedata);
|
||||||
|
while (filedata.size() < 0x9000)
|
||||||
|
bw.write_le32(bw.pos);
|
||||||
|
|
||||||
|
fs->putFile(Path("0:BIGFILE"), filedata);
|
||||||
|
|
||||||
|
auto directory = getBlock(sectors, 0, 256).slice(0, 64);
|
||||||
|
AssertThat(directory,
|
||||||
|
Equals(createDirent("BIGFILE",
|
||||||
|
0,
|
||||||
|
0x80,
|
||||||
|
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}) +
|
||||||
|
createDirent("BIGFILE", 2, 0x20, {17, 18})));
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto testDelete()
|
||||||
|
{
|
||||||
|
auto sectors = std::make_shared<TestSectorInterface>();
|
||||||
|
auto fs = Filesystem::createCpmFsFilesystem(
|
||||||
|
globalConfig()->filesystem(), sectors);
|
||||||
|
fs->create(true, "volume");
|
||||||
|
|
||||||
|
fs->putFile(Path("0:FILE1"), Bytes{1, 2, 3, 4});
|
||||||
|
fs->putFile(Path("0:FILE2"), Bytes{5, 6, 7, 8});
|
||||||
|
fs->deleteFile(Path("0:FILE1"));
|
||||||
|
|
||||||
|
auto directory = getBlock(sectors, 0, 256).slice(0, 64);
|
||||||
|
AssertThat(directory,
|
||||||
|
Equals((Bytes{0xe5} * 32) + createDirent("FILE2", 0, 1, {2})));
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -124,7 +234,7 @@ int main(void)
|
|||||||
|
|
||||||
layout {
|
layout {
|
||||||
format_type: FORMATTYPE_80TRACK
|
format_type: FORMATTYPE_80TRACK
|
||||||
tracks: 10
|
tracks: 20
|
||||||
sides: 1
|
sides: 1
|
||||||
layoutdata {
|
layoutdata {
|
||||||
sector_size: 256
|
sector_size: 256
|
||||||
@@ -148,6 +258,10 @@ int main(void)
|
|||||||
|
|
||||||
testPartialExtent();
|
testPartialExtent();
|
||||||
testLogicalExtents();
|
testLogicalExtents();
|
||||||
|
testBitmap();
|
||||||
|
testPutGet();
|
||||||
|
testPutBigFile();
|
||||||
|
testDelete();
|
||||||
}
|
}
|
||||||
catch (const ErrorException& e)
|
catch (const ErrorException& e)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user