#pragma once #include #include #include "Structs.h" // https://www.pjrc.com/tech/8051/ide/fat32.html namespace FAT32 { template class FS { static constexpr const char* NAME = "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" /** ctor with the absolute offset addr (in bytes) */ 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; } /** get an iterator for the root directory */ DirIterator getRoot() { return DirIterator(*this, desc.rootDirFirstCluster); } /** open the given file for reading*/ File open(const DirEntryAt& dea) { return File(*this, dea.getSize(), dea.getFirstCluster()); } /** 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: void init() { Log::addInfo(NAME, "init @ %d", offset); uint8_t buf[512]; dev.read(offset, 512, buf); desc.bytesPerSector = getU16(&buf[0x0B]); desc.sectorsPerCluster = getU8(&buf[0x0D]); desc.numReservedSectors = getU16(&buf[0x0E]); desc.numberOfFATs = getU8(&buf[0x10]); desc.sectorsPerFAT = getU32(&buf[0x24]); desc.rootDirFirstCluster = getU32(&buf[0x2C]); // basic sanity check based on constants valid = (desc.bytesPerSector == 512) && (desc.numberOfFATs == 2) && (getU16(&buf[0x1FE]) == 0xAA55); tmp.bytesPerCluster = desc.sectorsPerCluster * desc.bytesPerSector; tmp.startOfFAT = offset + (desc.numReservedSectors * desc.bytesPerSector); tmp.startOfFirstDataCluster = offset + (desc.numReservedSectors * desc.bytesPerSector) + (desc.numberOfFATs * desc.sectorsPerFAT * desc.bytesPerSector); tmp.startOfFirstRootDirCluster = clusterToAbsPos(desc.rootDirFirstCluster); tmp.dirEntriesPerSector = desc.bytesPerSector / sizeof(DirEntry); Log::addInfo(NAME, "Bytes/Sector: %d, Sector/Cluster: %d, FATs: %d, RootDir: %d", desc.bytesPerSector, desc.sectorsPerCluster, desc.numberOfFATs, desc.rootDirFirstCluster); /* std::cout << (int)desc.bytesPerSector << std::endl; std::cout << (int)desc.sectorsPerCluster << std::endl; std::cout << (int)desc.numReservedSectors << std::endl; std::cout << (int)desc.numberOfFATs << std::endl; std::cout << (int)desc.sectorsPerFAT << std::endl; std::cout << (int)desc.rootDirFirstCluster << std::endl; std::cout << tmp.startOfFAT << std::endl; std::cout << tmp.startOfFirstDataCluster << std::endl; std::cout << tmp.startOfFirstRootDirCluster << std::endl; */ } /** determine the ClusterNr following the given ClusterNr */ ClusterNr getNextCluster(const ClusterNr clusterNr) { const AbsPos pos = tmp.startOfFAT + (clusterNr * sizeof(ClusterNr)); ClusterNr next = 0; dev.read(pos, sizeof(ClusterNr), reinterpret_cast(&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(&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); } }; }