huge work on FAT32, initial support for writing

This commit is contained in:
2021-02-14 21:35:47 +01:00
parent 6aa951190e
commit da12992ae8
14 changed files with 609 additions and 91 deletions

View File

@@ -46,7 +46,7 @@ private:
template<typename... Args> static void add(char level, const char* module, const char* fmt, Args... args) {
char buf[128];
char buf[4096];
char* dst = buf;
dst = dst + sprintf(dst, "%c[%-10s] ", level, module);

View File

@@ -24,7 +24,7 @@ public:
}
/** write size bytes starting at addr using data from src, supports writing partial blocks by reading them first */
uint32_t write(AbsPos addr, uint32_t size, uint8_t* src) {
uint32_t write(AbsPos addr, uint32_t size, const uint8_t* src) {
Log::addInfo(NAME, "write(%d @ %d)", size, addr);
@@ -122,8 +122,8 @@ public:
}
/** write a single block of 512 bytes. addr = byteAddr/512 */
bool writeSingleBlock(LBA512 addr, uint8_t* dst) {
return dev.writeSingleBlock(addr, dst);
bool writeSingleBlock(LBA512 addr, const uint8_t* src) {
return dev.writeSingleBlock(addr, src);
}

View File

@@ -39,7 +39,7 @@ ADD_DEFINITIONS(
-fstack-protector-all
-g3
-g
-O0
-DWITH_TESTS

View File

@@ -20,8 +20,73 @@ public:
}
DirEntryAt cur() {
AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry));
DirEntry* dirEntry = reinterpret_cast<DirEntry*>(buf + (curEntryInSector * sizeof(DirEntry)));
return DirEntryAt(pos, dirEntry);
}
DirEntry* next() {
/** get the next usable entry within the current directory */
DirEntryAt nextUsable() {
while(true) {
DirEntryAt dea = next(false);
// check it
if (!dea.isValid()) {return DirEntryAt::invalid();}
if (dea.isLongFileName()) {continue;}
if (dea.isUnused()) {continue;}
if (dea.isEndOfDirectory()) {return DirEntryAt::invalid();}
// usable!
return dea;
}
}
/** get (or create) a free entry within the current directory */
DirEntryAt nextFree() {
while(true) {
DirEntryAt dea = next(true);
// check it
if (!dea.isValid()) {return DirEntryAt::invalid();}
if (dea.isUnused()) {
// switch from "unused" to something usable
//dea.setName("NEW.ONE");
//dea.entry.attr.raw = 0;
//return dea;
}
if (dea.isEndOfDirectory()) {
// add a new EndOfDirectory entry afterwards
DirEntryAt dea2 = next(true);
dea2.setEndOfDirectory();
fs.write(dea2);
// switch from "end of directory" to something usable
dea.setName("NEW.ONE");
dea.entry.attr.raw = 0;
return dea;
}
}
}
private:
DirEntryAt next(bool allocIfNeeded) {
while(true) {
@@ -35,8 +100,23 @@ public:
// end of cluster reached?
if (curSectorInCluster >= fs.desc.sectorsPerCluster) {
// find next cluster
curCluster = fs.getNextCluster(curCluster); // number of the next cluster (if any)
// find next cluster (if any)
const ClusterNr nextCluster = fs.getNextCluster(curCluster);
// reached end of clusters?
if (nextCluster.isEndOfClusters()) {
// do not alloc new entries? -> done here
if (!allocIfNeeded) {
return DirEntryAt::invalid();
} else {
curCluster = fs.allocFreeCluster(curCluster);
}
} else {
curCluster = nextCluster;
}
curSectorInCluster = 0;
}
@@ -47,18 +127,10 @@ public:
}
// the current entry
DirEntry* dirEntry = reinterpret_cast<DirEntry*>(buf + (sizeof(DirEntry) * curEntryInSector));
DirEntryAt dea = cur();
++curEntryInSector;
// check it
if (dirEntry->isLongFileName()) {continue;}
if (dirEntry->isUnused()) {continue;}
if (dirEntry->isEndOfDirectory()) {
return nullptr;
}
// usable!
return dirEntry;
return dea;
}

View File

@@ -15,25 +15,51 @@ namespace FAT32 {
BlockDev& dev;
AbsOffset offset;
FSDesc desc;
Precomputed tmp;
bool valid = false;
#include "FreeClusterIterator.h"
FreeClusterIterator fci;
public:
#include "File.h"
#include "File2.h"
#include "DirIterator.h"
#include "WriteFile.h"
#include "FreeClusterIterator.h"
/** ctor with the absolute offset addr (in bytes) */
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset) {
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset), fci(*this) {
init();
}
void setup(uint32_t totalSize) {
uint8_t buf[512] = {0};
buf[0x1FE] = 0x55;
buf[0x1FF] = 0xAA;
FSHeader* header = (FSHeader*) buf;
header->bytesPerSector = 512;
header->numberOfFATs = 2;
header->numReservedSectors = 32;
header->rootDirFirstCluster = 2;
header->mediaDescriptor = 0xF8; // hard disk
header->sectorsInPartition = totalSize / 512;
header->sectorsPerCluster = 8;
header->sectorsPerFAT = 64; // 8 MB
dev.write(offset, 512, (const uint8_t*)header);
init();
// mark the root-dir cluster as used
setNextCluster(2, END_OF_CLUSTERS);
}
/** is the detected FS valid? */
bool isValid() const {
return valid;
@@ -45,19 +71,39 @@ namespace FAT32 {
}
/** open the given file for reading*/
File open(const DirEntry& de) {
return File(*this, de.size, de.getFirstCluster());
File open(const DirEntryAt& dea) {
return File(*this, dea.getSize(), dea.getFirstCluster());
}
// /** create a new file for writing, in the given directory */
// WriteFile newFile(DirEntry& de, const uint32_t size) {
// if (!de.isDirectory()) {return nullptr;}
// uint32_t allocSize = 0;
// FreeClusterIterator fci(*this);
// while(allocSize < size) {
// ClusterNr = fci.next();
// }
// }
/** open the given file for reading*/
File2 open2(const DirEntryAt& dea) {
return File2(*this, dea);
}
/** create a new file for writing, in the given directory */
File2 create2(const char* name) {
ClusterNr dirStartCluster = 2; // TODO
//if (!dea.isValid()) {Log::addError(NAME, "create2(): not a valid DirEntry");}
//if (!dea.isDirectory()) {Log::addError(NAME, "create2(): not a directory");}
DirIterator di(*this, dirStartCluster);
// allocate a new directory entry for the file
DirEntryAt dea2 = di.nextFree();
dea2.setName(name);
dea2.setSize(0);
// allocate the file's first cluster
ClusterNr firstCluster = allocFreeCluster(0);
dea2.setFirstCluster(firstCluster);
// write the file description
write(dea2);
return File2(*this, dea2);
}
private:
@@ -102,18 +148,40 @@ namespace FAT32 {
}
/** determine the ClusterNr following the given ClusterNr */
ClusterNr getNextCluster(ClusterNr clusterNr) {
const AbsPos pos = tmp.startOfFAT + ((clusterNr) * sizeof(uint32_t));
ClusterNr getNextCluster(const ClusterNr clusterNr) {
const AbsPos pos = tmp.startOfFAT + (clusterNr * sizeof(ClusterNr));
ClusterNr next = 0;
dev.read(pos, 4, reinterpret_cast<uint8_t*>(&next));
Log::addInfo(NAME, "nextCluster(%d) -> %d", clusterNr, next);
dev.read(pos, sizeof(ClusterNr), reinterpret_cast<uint8_t*>(&next));
Log::addInfo(NAME, "getNextCluster(%d) -> %d", clusterNr, next);
return next;
}
/** set the ClusterNr following clusterNr */
void setNextCluster(const ClusterNr clusterNr, const ClusterNr next) {
const AbsPos pos = tmp.startOfFAT + (clusterNr * sizeof(ClusterNr));
dev.write(pos, sizeof(ClusterNr), reinterpret_cast<const uint8_t*>(&next));
Log::addInfo(NAME, "setNextCluster(%d) -> %d", clusterNr, next);
}
/** convert ClusterNr into an absolute position on disk */
AbsPos clusterToAbsPos(ClusterNr clusterNr) {
return tmp.startOfFirstDataCluster + ((clusterNr - 2) * desc.sectorsPerCluster * desc.bytesPerSector);
}
/** allocate a new empty cluster, and register it. if prevNr == 0, it has NO previous cluster (first in line) */
ClusterNr allocFreeCluster(ClusterNr prevNr = 0) {
ClusterNr newCluster = fci.next(); // get/find a free cluster
if (prevNr > 0) {
setNextCluster(prevNr, newCluster); // follows the previous cluster (if any)
}
setNextCluster(newCluster, END_OF_CLUSTERS);// no clusters follow the new cluster
return newCluster;
}
/** write the given DirEntry back to disk */
void write(const DirEntryAt& dea) {
dev.write(dea.posOnDisk, sizeof(DirEntry), (const uint8_t*)&dea.entry);
}
};
}

View File

@@ -7,13 +7,15 @@ class File {
uint32_t totalSize;
uint32_t curAbsPos = 0; // position withithin the whole file
uint32_t curCluster = 0; // cluster we are currently reading
ClusterNr curCluster = 0; // cluster we are currently reading
uint32_t posInCluster = 0; // position within the current cluster
public:
File(FS& fs, uint32_t size, uint32_t firstCluster) : fs(fs), totalSize(size), curCluster(firstCluster) {
File(FS& fs, uint32_t size, ClusterNr firstCluster) : fs(fs), totalSize(size), curCluster(firstCluster) {
Log::addInfo(NAME, "init @ cluster %d", firstCluster);
}

179
ext/sd/fat32/File2.h Normal file
View File

@@ -0,0 +1,179 @@
class File2 {
static constexpr const char* NAME = "FAT32_File2";
static constexpr const uint32_t F_EOF = 0xFFFFFFFF;
FS& fs;
DirEntryAt dea;
uint32_t curAbsPos = 0; // absolute position withithin the file
uint32_t posInCluster = 0; // position within the current cluster
uint32_t iCurCluster = 0; // current cluster index (within list of clusters)
std::vector<ClusterNr> clusters; // all clusters the file uses
public:
File2(FS& fs, const DirEntryAt& dea) : fs(fs), dea(dea) {
Log::addInfo(NAME, "init @ cluster %d", getFirstCluster());
getAllClusters(dea.getFirstCluster());
}
/** the file's USED size */
uint32_t getSize() const {return dea.getSize();}
/** the file's ALLOCATED size */
uint32_t getAllocatedSize() const {return clusters.size() * fs.tmp.bytesPerCluster;}
/** the file's first cluster */
ClusterNr getFirstCluster() const {return dea.getFirstCluster();}
/** get the file's name */
const std::string& getName() const {return dea.getName();}
/** read x bytes from the file */
uint32_t read(uint32_t size, uint8_t* dst, std::function<void(int)> callback) {
Log::addInfo(NAME, "read %d bytes", size);
uint32_t remaining = size;
uint32_t totalRead = 0;
while(remaining) {
const uint32_t read = _read(remaining, dst);
if (read == F_EOF) {
Log::addInfo(NAME, "EOF"); break;
}
remaining -= read;
dst += read;
totalRead += read;
callback(totalRead*100/size);
}
return totalRead;
}
uint32_t write(uint32_t size, const uint8_t* src, std::function<void(int)> callback) {
Log::addInfo(NAME, "write %d bytes", size);
uint32_t remaining = size;
uint32_t totalWritten = 0;
while(remaining) {
const uint32_t written = _write(remaining, src);
remaining -= written;
src += written;
totalWritten += written;
callback(totalWritten*100/size);
}
// update the file header (size might have changed)
fs.write(dea);
return totalWritten;
}
private:
void setSize(uint32_t size) {dea.setSize(size);}
/** fetch the list of all clusters the file is using */
void getAllClusters(ClusterNr startCluster) {
// initial cluster
clusters.push_back(startCluster);
// all following clusters
while(true) {
startCluster = fs.getNextCluster(startCluster);
if (!startCluster.isEndOfClusters()) {
clusters.push_back(startCluster);
} else {
break;
}
}
// debug
char buf[1024];
char* dst = buf; dst += sprintf(dst, "clusters: ");
for (ClusterNr nr : clusters) {dst += sprintf(dst, "%d ", nr);}
Log::addInfo(NAME, buf);
}
int32_t _read(uint32_t readSize, uint8_t* dst) {
// EOF reached?
if (curAbsPos >= getSize()) {
return F_EOF;
}
// end of current cluster reached? -> switch to the next one
if (posInCluster == fs.tmp.bytesPerCluster) {
++iCurCluster;
posInCluster = 0;
Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]);
}
// how many bytes are left in the current cluster?
const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster;
// determine how many bytes to read
const uint32_t toRead = std::min(remainingInCluster, readSize);
const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster;
const uint32_t read = fs.dev.read(offset, toRead, dst);
posInCluster += read;
curAbsPos += read;
return read;
}
int32_t _write(uint32_t writeSize, const uint8_t* src) {
if (curAbsPos >= getAllocatedSize()) {
if (clusters.empty()) {
const ClusterNr newCluster = fs.allocFreeCluster(0);
clusters.push_back(newCluster);
Log::addInfo(NAME, "allocFirstCluster: %d", newCluster);
} else {
const ClusterNr prevCluster = clusters.back();
const ClusterNr newCluster = fs.allocFreeCluster(prevCluster);
clusters.push_back(newCluster);
Log::addInfo(NAME, "allocNextCluster(%d): %d", prevCluster, newCluster);
}
dea.setSize(dea.getSize() + fs.tmp.bytesPerCluster);
fs.write(dea);
}
// end of current cluster reached? -> switch to the next one
if (posInCluster == fs.tmp.bytesPerCluster) {
++iCurCluster;
posInCluster = 0;
Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]);
}
// how many bytes are left in the current cluster?
const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster;
// determine how many bytes to write
const uint32_t toWrite = std::min(remainingInCluster, writeSize);
const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster;
const uint32_t written = fs.dev.write(offset, toWrite, src);
posInCluster += written;
curAbsPos += written;
// adjust the number of bytes used
if (this->getSize() < curAbsPos) {this->setSize(curAbsPos);}
return written;
}
};

View File

@@ -4,10 +4,22 @@
class FreeClusterIterator {
FS& fs;
ClusterNr cur;
public:
FreeClusterIterator(FS& fs) : fs(fs) {
FreeClusterIterator(FS& fs) : fs(fs), cur(2) {
}
/** get the next free cluster that is available */
ClusterNr next() {
while(true) {
const ClusterNr tmp = fs.getNextCluster(cur);
if (tmp.isFree()) {return cur;}
++cur;
}
}

View File

@@ -1,11 +1,58 @@
#pragma once
#include <string.h>
#include <string>
#include "Types.h"
namespace FAT32 {
using ClusterNr = uint32_t;
static inline uint16_t getU8(const uint8_t* src) {return src[0];}
static inline uint16_t getU16(const uint8_t* src) {return src[0]<<0 | src[1]<<8;}
static inline uint32_t getU32(const uint8_t* src) {return src[0]<<0 | src[1]<<8 | src[2]<<16 | src[3]<<24;}
static inline size_t min(size_t a, size_t b) {return (a<b) ? a : b;}
struct ClusterNr {
uint32_t val;
ClusterNr() : val(0) {}
ClusterNr(uint32_t val) : val(val) {}
void operator = (const uint32_t val) {this->val = val;}
operator uint32_t() const {return val;}
inline bool isEndOfClusters() const {
return (val & 0x0FFFFFFF) >= 0xFFFFFF8; // only the lower 28 bits are used
}
inline bool isFree() const {
return val == 0;
}
void operator ++() {++val;}
};
const ClusterNr END_OF_CLUSTERS = 0xFFFFFFF;
/** header within the first 512 bytes */
struct FSHeader { // https://www.easeus.com/resource/fat32-disk-structure.htm
uint8_t dummy1[11];
uint16_t bytesPerSector; // @0x0B;
uint8_t sectorsPerCluster; // @0x0D;
uint16_t numReservedSectors; // @0x0E;
uint8_t numberOfFATs; // @0x10;
uint8_t dummy2[4];
uint8_t mediaDescriptor; // @0x15;
uint8_t dummy3[10];
uint32_t sectorsInPartition; // @0x20;
uint32_t sectorsPerFAT; // @0x24;
uint8_t dummy4[4];
uint32_t rootDirFirstCluster; // @0x2C
} __attribute__((packed));
struct FSDesc {
uint16_t bytesPerSector;
@@ -38,8 +85,8 @@ namespace FAT32 {
} bits;
} __attribute__((packed));
/** structure for a DirectoryEntry as defined in the FAT standard */
struct DirEntry {
unsigned char name[8];
unsigned char ext[3];
Attributes attr;
@@ -48,28 +95,69 @@ namespace FAT32 {
uint8_t dummy2[4];
uint16_t firstClusterLo;
uint32_t size; // file size in bytes
} __attribute__((packed));
bool isUnused() const {return name[0] == 0xE5;}
bool isDirectory() const {return attr.bits.directory;}
bool isEndOfDirectory() const {return name[0] == 0x00;}
bool isLongFileName() const {return (attr.raw & 0b1111) == 0b1111;}
/** combine a DirectoryEntry with its absolute location on disk */
struct DirEntryAt {
uint32_t getFirstCluster() const {return firstClusterHi<<16 | firstClusterLo<<0;}
AbsPos posOnDisk;
DirEntry entry;
bool valid;
/** ctor for an invalid entry */
DirEntryAt() : valid(false) {}
/** ctor for a valid entry */
DirEntryAt(AbsPos posOnDisk, DirEntry* entry) : posOnDisk(posOnDisk), entry(*entry), valid(true) {}
static DirEntryAt invalid() {return DirEntryAt();}
/** is this a valid entry? */
bool isValid() const {return valid;}
uint32_t getSize() const {return entry.size;}
void setSize(uint32_t size) {entry.size = size;}
bool isUnused() const {return entry.name[0] == 0xE5;}
bool isDirectory() const {return entry.attr.bits.directory;}
bool isEndOfDirectory() const {return entry.name[0] == 0x00;}
bool isLongFileName() const {return (entry.attr.raw & 0b1111) == 0b1111;}
void setEndOfDirectory() {entry.name[0] = 0x00;}
ClusterNr getFirstCluster() const {return entry.firstClusterHi<<16 | entry.firstClusterLo<<0;}
void setFirstCluster(ClusterNr nr) {entry.firstClusterHi = (uint16_t)(nr << 16); entry.firstClusterLo = (uint16_t)(nr << 0);}
std::string getName() const {
char buf[16];
uint8_t pos = 0;
for (uint8_t i = 0; i < 8; ++i) {if (name[i] != ' ') {buf[pos++] = name[i];}}
for (uint8_t i = 0; i < 8; ++i) {if (entry.name[i] != ' ') {buf[pos++] = entry.name[i];}}
buf[pos++] = '.';
for (uint8_t i = 0; i < 3; ++i) {if ( ext[i] != ' ') {buf[pos++] = ext[i];}}
for (uint8_t i = 0; i < 3; ++i) {if ( entry.ext[i] != ' ') {buf[pos++] = entry.ext[i];}}
buf[pos] = 0;
return std::string(buf);
}
} __attribute__((packed));
void setName(const std::string& str) {
// "zero" fill name and extension
memset(entry.name, ' ', 8);
memset(entry.ext, ' ', 3);
auto pos = str.find('.');
for (size_t i = 0; i < min(pos, str.length()); ++i) {
if (i >= 8) {break;}
entry.name[i] = str[i];
}
for (size_t i = 0; i < min(pos, str.length()); ++i) {
if (i >= 3) {break;}
entry.ext[i] = str[i+pos+1];
}
}
};
static inline uint16_t getU8(const uint8_t* src) {return src[0];}
static inline uint16_t getU16(const uint8_t* src) {return src[0]<<0 | src[1]<<8;}
static inline uint32_t getU32(const uint8_t* src) {return src[0]<<0 | src[1]<<8 | src[2]<<16 | src[3]<<24;}
}

View File

@@ -1,5 +0,0 @@
#pragma once
class WriteFile {
};

View File

@@ -17,35 +17,61 @@
#include <fstream>
//class Simu {
// FILE* f;
//public:
// Simu(const char* image) {
// f = fopen(image, "rw");
// if (!f) {throw std::runtime_error("failed to open");}
// }
// uint32_t readSingleBlock(LBA512 addr, uint8_t* dst) {
// Log::addInfo("SD", "read512(%d*512)", addr);
// fseek(f, addr*512, SEEK_SET);
// return fread(dst, 512, 1, f) * 512;
// }
// uint32_t writeSingleBlock(LBA512 addr, const uint8_t* src) {
// Log::addInfo("SD", "write512(%d*512)", addr);
// fseek(f, addr*512, SEEK_SET);
// return fwrite(src, 512, 1, f) * 512;
// }
//};
class Simu {
FILE* f;
uint8_t* data;
public:
Simu(const char* image) {
f = fopen(image, "rw");
FILE* f = fopen(image, "rw");
if (!f) {throw std::runtime_error("failed to open");}
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
fseek(f, 0L, SEEK_SET);
data = (uint8_t*) malloc(size);
fread(data, size, 1, f);
fclose(f);
}
// uint32_t readSingleBlock(uint32_t addr, uint32_t size, uint8_t* dst) {
// debugMod2("SD", "read(%d @ %d)", size, addr);
// fseek(f, addr, SEEK_SET);
// return fread(dst, size, 1, f) * size;
// }
uint32_t readSingleBlock(LBA512 addr, uint8_t* dst) {
Log::addInfo("SD", "read512(%d*512)", addr);
fseek(f, addr*512, SEEK_SET);
return fread(dst, 512, 1, f) * 512;
memcpy(dst, data+addr*512, 512);
return 512;
}
uint32_t writeSingleBlock(LBA512 addr, const uint8_t* src) {
Log::addInfo("SD", "write512(%d*512)", addr);
fseek(f, addr*512, SEEK_SET);
return fwrite(src, 512, 1, f) * 512;
memcpy(data+addr*512, src, 512);
return 512;
}
};
#include <gtest/gtest.h>
@@ -53,6 +79,7 @@ public:
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::GTEST_FLAG(filter) = "*TestCreate*";
return RUN_ALL_TESTS();
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
@@ -77,30 +104,39 @@ int main(int argc, char** argv) {
};
FAT32FS::DirIterator dir = fat.getRoot();
while(true) {
while(false) {
FAT32::DirEntry* de = dir.next();
if (!de) {break;}
std::cout << de->getName() << std::endl;
FAT32::DirEntryAt dea = dir.nextUsable();
if (!dea.isValid()) {break;}
if (1==0) {
FAT32FS::File f = fat.open(*de);
FAT32FS::File2 f = fat.open2(dea);
uint8_t* bufff = (uint8_t*) malloc(1024*1024);
uint32_t read = f.read(de->size, bufff, callback);
if (1==0) {
uint8_t* bufff = (uint8_t*) malloc(1024*1024);
uint32_t read = f.read(f.getSize(), bufff, callback);
std::string name = de->getName();
std::ofstream out("/tmp/ram/" + name);
out.write((char*)bufff, read);
out.close();
std::string name = f.getName();
std::ofstream out("/tmp/ram/" + name);
out.write((char*)bufff, read);
out.close();
free(bufff);
free(bufff);
break;
break;
}
}
}
//FAT32::DirEntryAt root = fat.getRoot().cur();
FAT32FS::File2 file = fat.create2("tmp1.txt");
uint8_t src[128];
file.write(128, src, [] (int percent) {} );
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
// diff /tmp/ram/KIRBY1.GB /apps/workspace/gbemu/tests/Kirby\'s\ Dream\ Land\ \(USA\,\ Europe\).gb

View File

@@ -6,7 +6,23 @@
struct TestDevice {
uint8_t buf[4096];
size_t size;
uint8_t* buf;
TestDevice(size_t size) : size(size) {
buf = (uint8_t*) malloc(size);
memset(buf, 0, size);
}
~TestDevice() {
free(buf);
}
void toFile(const std::string& name) {
FILE* f = fopen(name.c_str(), "w+");
fwrite(buf, size, 1, f);
fclose(f);
}
/** read a 512 byte block into dst */
bool readSingleBlock(LBA512 addr, uint8_t* dst) {
@@ -14,7 +30,7 @@ struct TestDevice {
return true;
}
bool writeSingleBlock(LBA512 addr, uint8_t* src) {
bool writeSingleBlock(LBA512 addr, const uint8_t* src) {
memcpy(buf+addr*512, src, 512);
return true;
}

View File

@@ -5,12 +5,12 @@
#include "../AccessHelper.h"
/*
TEST (TestAccessHelper, read) {
// read varying numbers of bytes at arbitrary locations
TestDevice dev;
TestDevice dev(4096);
AccessHelper<TestDevice> ah(dev);
for (size_t i = 0; i < sizeof(dev.buf); ++i) {dev.buf[i] = i;}
@@ -33,14 +33,14 @@ TEST (TestAccessHelper, read) {
}
}
*/
TEST (TestAccessHelper, write) {
// write varying numbers of bytes at arbitrary locations
const uint8_t MAGIC = 0xFF;
TestDevice dev;
TestDevice dev(4096);
AccessHelper<TestDevice> ah(dev);
// src data

View File

@@ -0,0 +1,50 @@
#include <gtest/gtest.h>
#include "Helper.h"
#include "../AccessHelper.h"
#include "../fat32/FS.h"
// dd if=/dev/zero of=/tmp/ram/orig.fat32 bs=8192 count=4096
// mkfs.vfat -F 32 -R 32 -s 8 orig.fat32
// hexdump -v -C -n 512 orig.fat32
TEST(TestCreate, structure) {
FAT32::FSHeader header;
ASSERT_EQ(0x0B, (uint8_t*)&header.bytesPerSector - (uint8_t*)&header);
ASSERT_EQ(0x0D, (uint8_t*)&header.sectorsPerCluster - (uint8_t*)&header);
ASSERT_EQ(0x0E, (uint8_t*)&header.numReservedSectors - (uint8_t*)&header);
ASSERT_EQ(0x10, (uint8_t*)&header.numberOfFATs - (uint8_t*)&header);
ASSERT_EQ(0x15, (uint8_t*)&header.mediaDescriptor - (uint8_t*)&header);
ASSERT_EQ(0x20, (uint8_t*)&header.sectorsInPartition - (uint8_t*)&header);
ASSERT_EQ(0x24, (uint8_t*)&header.sectorsPerFAT - (uint8_t*)&header);
ASSERT_EQ(0x2C, (uint8_t*)&header.rootDirFirstCluster - (uint8_t*)&header);
}
TEST (TestCreate, write) {
using BlockDev = AccessHelper<TestDevice>;
size_t size = 32*1024*1024;
TestDevice dev(size);
BlockDev bDev(dev);
FAT32::FS<BlockDev> fs(bDev, 0);
fs.setup(size);
const char* test = "This is a test";
FAT32::FS<BlockDev>::File2 f1 = fs.create2("test1.txt");
f1.write(strlen(test), (const uint8_t*)test, [](int percent) {});
FAT32::FS<BlockDev>::File2 f2 = fs.create2("test2.txt");
f2.write(strlen(test), (const uint8_t*)test, [](int percent) {});
dev.toFile("/tmp/ram/1.fat32");
// mount -t vfat 1.fat32 /mnt/fat/ && ls -l /mnt/fat/ && cat /mnt/fat/test1.txt && umount /mnt/fat
}