#pragma once #include #include #include #include "Structs.h" #include "DirHandle.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; #include "UsedSpaceChecker.h" public: #include "File.h" #include "DirIterator.h" #include "DirHelper.h" /** ctor with the absolute offset addr (in bytes) */ FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset), fci(*this) { init(); } /** * setup a new filesystem with the given parameters * killFAT = zero both file allocation tables (reset all cluster numbers) */ void setup(uint32_t totalSize, const bool killFAT) { uint8_t buf[512] = {0}; buf[0x1FE] = 0x55; buf[0x1FF] = 0xAA; const uint32_t sectorsPerCluster = 8; const uint32_t sizePerCluster = 512 * sectorsPerCluster; const uint32_t sectorsPerFAT = totalSize / sizePerCluster * sizeof(ClusterNr) / 512; FSHeader* header = (FSHeader*) buf; header->bytesPerSector = 512; // always header->numberOfFATs = 2; // always header->numReservedSectors = 32; // always header->rootDirFirstCluster = 2; // always header->mediaDescriptor = 0xF8; // hard disk header->sectorsInPartition = totalSize / 512; header->sectorsPerCluster = sectorsPerCluster; // 4096 byte clusters header->sectorsPerFAT = sectorsPerFAT; // write the header to the underlying device dev.write(offset, 512, (const uint8_t*)header); // now update the internal state based on the newly created FS header... init(); if (killFAT) { // zero-out FAT1 uint8_t zeros[512] = {0}; AbsPos pos = tmp.startOfFAT1; for (uint32_t i = 0; i < header->sectorsPerFAT; ++i) { dev.write(pos + i * 512, 512, zeros); } // ...and mark the root-dir cluster as used setNextCluster(2, END_OF_CLUSTERS); // set the first entry within the root-dir to denote the end-of-the-root-dir (no entries at all) DirIterator di = getRoot(); DirHandle dh = di.next(); dh.setEndOfDirectory(); write(dh); } } /** is the detected FS valid? */ bool isValid() const { return valid; } /** get the total size of the filesystem (including FATs etc.) */ uint32_t getSize() const { return desc.sectorsPerFAT * desc.bytesPerSector / sizeof(ClusterNr) * tmp.bytesPerCluster; } /** get the size that is actually usable for data (total size - required overhead) */ uint32_t getSizeForData() const { return getSize() - (desc.numReservedSectors * desc.bytesPerSector) - (desc.numberOfFATs * desc.sectorsPerFAT * desc.bytesPerSector); } /** get the number of used data bytes */ uint32_t getSizeUsed() const { return UsedSpaceChecker::getNumUsedClusters(*this) * tmp.bytesPerCluster; } /** get an iterator for the root directory */ DirIterator getRoot() { return DirIterator(*this, 2); } /** open the given file */ File open(const DirHandle& h) { return File(*this, h); } /** create the given absolute folder */ DirHandle mkdirs(const char* absName) { DirHandle h = DirHelper::getOrCreate(*this, 2, absName, DirHelper::CreateType::DIR); return h; } /** get the entry with the given name */ DirHandle getHandle(const char* absName) { std::string tmp(absName); if (tmp == "/" || tmp == "") { DirEntry dummy; DirHandle dh(0, &dummy); dh.setFirstCluster(desc.rootDirFirstCluster); return dh; } else { DirHandle h = DirHelper::getOrCreate(*this, 2, absName, DirHelper::CreateType::NONE); return h; } } /** get or create a file with the given name */ File getOrCreateFile(const char* absName) { DirHandle h = DirHelper::getOrCreate(*this, 2, absName, DirHelper::CreateType::FILE); // new file -> allocate the first cluster if (h.getFirstCluster() == 0) { ClusterNr firstCluster = allocFreeCluster(0); h.setFirstCluster(firstCluster); write(h); } return File(*this, h); } 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.startOfFAT1 = offset + (desc.numReservedSectors * desc.bytesPerSector); tmp.startOfFAT2 = tmp.startOfFAT1 + desc.sectorsPerFAT * desc.bytesPerSector; tmp.startOfFirstDataCluster = offset + (desc.numReservedSectors * desc.bytesPerSector) + (desc.numberOfFATs * desc.sectorsPerFAT * desc.bytesPerSector); tmp.startOfFirstRootDirCluster = clusterToAbsPos(desc.rootDirFirstCluster); tmp.entriesPerFAT = desc.sectorsPerFAT * desc.bytesPerSector / sizeof(ClusterNr); 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); } /** determine the ClusterNr following the given ClusterNr */ ClusterNr getNextCluster(const ClusterNr clusterNr) const { const AbsPos pos = tmp.startOfFAT1 + (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.startOfFAT1 + (clusterNr * sizeof(ClusterNr)); dev.write(pos, sizeof(ClusterNr), reinterpret_cast(&next.val)); 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 all zeros to the given cluster */ void zeroOutCluster(ClusterNr nr) { uint8_t zeros[512] = {0}; AbsPos pos = clusterToAbsPos(nr); for (uint8_t i = 0; i < desc.sectorsPerCluster; ++i) { dev.write(pos + i * 512, 512, zeros); } } /** write the given DirHandle back to disk */ void write(const DirHandle& h) { dev.write(h.posOnDisk, sizeof(DirEntry), (const uint8_t*)&h.entry); } }; }