Files
ESP8266lib/ext/sd/fat32/FS.h
2021-02-21 21:04:11 +01:00

236 lines
7.2 KiB
C++

#pragma once
#include <cstdint>
#include <functional>
#include <vector>
#include "Structs.h"
#include "DirHandle.h"
// https://www.pjrc.com/tech/8051/ide/fat32.html
namespace FAT32 {
template <typename BlockDev> 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<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.startOfFAT1 + (clusterNr * sizeof(ClusterNr));
dev.write(pos, sizeof(ClusterNr), reinterpret_cast<const uint8_t*>(&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);
}
};
}