mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
342 lines
8.2 KiB
C++
342 lines
8.2 KiB
C++
#include "lib/globals.h"
|
|
#include "lib/vfs/vfs.h"
|
|
#include "lib/config.pb.h"
|
|
#include "lib/utils.h"
|
|
#include <fmt/format.h>
|
|
|
|
extern "C"
|
|
{
|
|
#include "ff.h"
|
|
#include "diskio.h"
|
|
}
|
|
|
|
class FatFsFilesystem;
|
|
static FatFsFilesystem* currentFatFs;
|
|
|
|
static std::string modeToString(BYTE attrib)
|
|
{
|
|
std::stringstream ss;
|
|
if (attrib & AM_RDO)
|
|
ss << 'R';
|
|
if (attrib & AM_HID)
|
|
ss << 'H';
|
|
if (attrib & AM_SYS)
|
|
ss << 'S';
|
|
if (attrib & AM_ARC)
|
|
ss << 'A';
|
|
return ss.str();
|
|
}
|
|
|
|
class FatFsFilesystem : public Filesystem
|
|
{
|
|
public:
|
|
FatFsFilesystem(
|
|
const FatFsProto& config, std::shared_ptr<SectorInterface> sectors):
|
|
Filesystem(sectors),
|
|
_config(config)
|
|
{
|
|
}
|
|
|
|
uint32_t capabilities() const
|
|
{
|
|
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
|
|
OP_GETDIRENT | OP_MOVE | OP_CREATEDIR | OP_DELETE;
|
|
}
|
|
|
|
std::map<std::string, std::string> getMetadata() override
|
|
{
|
|
mount();
|
|
|
|
std::map<std::string, std::string> attributes;
|
|
|
|
{
|
|
char buffer[34];
|
|
FRESULT res = f_getlabel("", buffer, nullptr);
|
|
throwError(res);
|
|
attributes[VOLUME_NAME] = buffer;
|
|
}
|
|
|
|
{
|
|
FATFS* fs;
|
|
DWORD free_clusters;
|
|
FRESULT res = f_getfree("", &free_clusters, &fs);
|
|
throwError(res);
|
|
int total = (fs->n_fatent - 2) + (fs->database / fs->csize);
|
|
attributes[TOTAL_BLOCKS] = std::to_string(total);
|
|
attributes[USED_BLOCKS] = std::to_string(total - free_clusters);
|
|
attributes[BLOCK_SIZE] =
|
|
std::to_string(fs->csize * getLogicalSectorSize(0));
|
|
}
|
|
|
|
return attributes;
|
|
}
|
|
|
|
void create(bool quick, const std::string& volumeName) override
|
|
{
|
|
if (!quick)
|
|
eraseEverythingOnDisk();
|
|
|
|
char buffer[FF_MAX_SS * 2];
|
|
currentFatFs = this;
|
|
MKFS_PARM parm = {
|
|
.fmt = FM_SFD | FM_ANY,
|
|
};
|
|
FRESULT res = f_mkfs("", &parm, buffer, sizeof(buffer));
|
|
throwError(res);
|
|
|
|
mount();
|
|
f_setlabel(volumeName.c_str());
|
|
}
|
|
|
|
FilesystemStatus check() override
|
|
{
|
|
return FS_OK;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
|
|
{
|
|
mount();
|
|
|
|
DIR dir;
|
|
auto pathstr = toUpper(path.to_str());
|
|
FRESULT res = f_opendir(&dir, pathstr.c_str());
|
|
std::vector<std::shared_ptr<Dirent>> results;
|
|
|
|
for (;;)
|
|
{
|
|
FILINFO filinfo;
|
|
res = f_readdir(&dir, &filinfo);
|
|
if (res != FR_OK)
|
|
throw BadFilesystemException();
|
|
if (filinfo.fname[0] == 0)
|
|
break;
|
|
|
|
results.push_back(toDirent(filinfo, path));
|
|
}
|
|
|
|
f_closedir(&dir);
|
|
return results;
|
|
}
|
|
|
|
std::shared_ptr<Dirent> getDirent(const Path& path) override
|
|
{
|
|
std::map<std::string, std::string> attributes;
|
|
|
|
mount();
|
|
auto pathstr = toUpper(path.to_str());
|
|
FILINFO filinfo;
|
|
FRESULT res = f_stat(pathstr.c_str(), &filinfo);
|
|
throwError(res);
|
|
|
|
return toDirent(filinfo, path.parent());
|
|
}
|
|
|
|
Bytes getFile(const Path& path) override
|
|
{
|
|
mount();
|
|
auto pathstr = toUpper(path.to_str());
|
|
FIL fil;
|
|
FRESULT res = f_open(&fil, pathstr.c_str(), FA_READ);
|
|
throwError(res);
|
|
|
|
Bytes bytes;
|
|
ByteWriter bw(bytes);
|
|
|
|
while (!f_eof(&fil))
|
|
{
|
|
uint8_t buffer[4096];
|
|
UINT done;
|
|
res = f_read(&fil, buffer, sizeof(buffer), &done);
|
|
throwError(res);
|
|
|
|
bw += Bytes(buffer, done);
|
|
}
|
|
|
|
f_close(&fil);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void putFile(const Path& path, const Bytes& bytes) override
|
|
{
|
|
mount();
|
|
auto pathstr = toUpper(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);
|
|
}
|
|
|
|
void deleteFile(const Path& path) override
|
|
{
|
|
mount();
|
|
auto pathstr = toUpper(path.to_str());
|
|
FRESULT res = f_unlink(pathstr.c_str());
|
|
throwError(res);
|
|
}
|
|
|
|
void moveFile(const Path& oldPath, const Path& newPath) override
|
|
{
|
|
mount();
|
|
auto oldPathStr = toUpper(oldPath.to_str());
|
|
auto newPathStr = toUpper(newPath.to_str());
|
|
FRESULT res = f_rename(oldPathStr.c_str(), newPathStr.c_str());
|
|
throwError(res);
|
|
}
|
|
|
|
void createDirectory(const Path& path)
|
|
{
|
|
mount();
|
|
auto pathStr = path.to_str();
|
|
FRESULT res = f_mkdir(pathStr.c_str());
|
|
throwError(res);
|
|
}
|
|
|
|
private:
|
|
std::shared_ptr<Dirent> toDirent(FILINFO& filinfo, const Path& parent)
|
|
{
|
|
auto dirent = std::make_shared<Dirent>();
|
|
dirent->filename = toUpper(filinfo.fname);
|
|
dirent->path = parent;
|
|
dirent->path.push_back(dirent->filename);
|
|
dirent->length = filinfo.fsize;
|
|
dirent->file_type =
|
|
(filinfo.fattrib & AM_DIR) ? TYPE_DIRECTORY : TYPE_FILE;
|
|
dirent->mode = modeToString(filinfo.fattrib);
|
|
|
|
dirent->attributes[Filesystem::FILENAME] = filinfo.fname;
|
|
dirent->attributes[Filesystem::LENGTH] = std::to_string(filinfo.fsize);
|
|
dirent->attributes[Filesystem::FILE_TYPE] =
|
|
(filinfo.fattrib & AM_DIR) ? "dir" : "file";
|
|
dirent->attributes[Filesystem::MODE] = modeToString(filinfo.fattrib);
|
|
return dirent;
|
|
}
|
|
|
|
public:
|
|
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
|
|
{
|
|
auto bytes = getLogicalSector(sector, count);
|
|
memcpy(buffer, bytes.cbegin(), bytes.size());
|
|
return RES_OK;
|
|
}
|
|
|
|
DRESULT diskWrite(const BYTE* buffer, LBA_t sector, UINT count)
|
|
{
|
|
Bytes bytes(buffer, count * getLogicalSectorSize());
|
|
putLogicalSector(sector, bytes);
|
|
return RES_OK;
|
|
}
|
|
|
|
DRESULT diskIoctl(BYTE cmd, void* buffer)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case GET_SECTOR_SIZE:
|
|
*(WORD*)buffer = getLogicalSectorSize();
|
|
break;
|
|
|
|
case GET_SECTOR_COUNT:
|
|
*(LBA_t*)buffer = getLogicalSectorCount();
|
|
break;
|
|
|
|
case CTRL_SYNC:
|
|
break;
|
|
|
|
default:
|
|
return RES_PARERR;
|
|
}
|
|
return RES_OK;
|
|
}
|
|
|
|
private:
|
|
void mount()
|
|
{
|
|
currentFatFs = this;
|
|
f_mount(&_fs, "", 1);
|
|
}
|
|
|
|
void throwError(FRESULT res)
|
|
{
|
|
switch (res)
|
|
{
|
|
case FR_OK:
|
|
return;
|
|
|
|
case FR_NO_FILE:
|
|
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();
|
|
|
|
default:
|
|
throw FilesystemException(
|
|
fmt::format("unknown fatfs error {}", res));
|
|
}
|
|
}
|
|
|
|
private:
|
|
const FatFsProto& _config;
|
|
FATFS _fs;
|
|
};
|
|
|
|
DSTATUS disk_initialize(BYTE)
|
|
{
|
|
return RES_OK;
|
|
}
|
|
|
|
DSTATUS disk_status(BYTE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
DRESULT disk_read(BYTE, BYTE* buffer, LBA_t sector, UINT count)
|
|
{
|
|
return currentFatFs->diskRead(buffer, sector, count);
|
|
}
|
|
|
|
DRESULT disk_write(BYTE, const BYTE* buffer, LBA_t sector, UINT count)
|
|
{
|
|
return currentFatFs->diskWrite(buffer, sector, count);
|
|
}
|
|
|
|
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buffer)
|
|
{
|
|
return currentFatFs->diskIoctl(cmd, buffer);
|
|
}
|
|
|
|
DWORD get_fattime(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
std::unique_ptr<Filesystem> Filesystem::createFatFsFilesystem(
|
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors)
|
|
{
|
|
return std::make_unique<FatFsFilesystem>(config.fatfs(), sectors);
|
|
}
|