20#include "FatFilesystem.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/machine/Disk.h"
23#include "pedigree/kernel/process/Scheduler.h"
24#include "pedigree/kernel/processor/types.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/utilities/StaticString.h"
27#include "pedigree/kernel/utilities/String.h"
28#include "pedigree/kernel/utilities/Tree.h"
30#include "pedigree/kernel/utilities/utility.h"
32#include "FatDirectory.h"
34#include "FatSymlink.h"
36#include "modules/Module.h"
37#include "modules/system/vfs/Directory.h"
38#include "modules/system/vfs/File.h"
39#include "modules/system/vfs/VFS.h"
45static bool isPowerOf2(uint32_t n) {
48 for (log = 0; log < 16; log++) {
51 return (n != 0) ? false :
true;
58FatFilesystem::FatFilesystem()
59 : m_FileMutationLock(),
73#if THREADS || defined(STANDALONE_MUTEXES)
82FatFilesystem::~FatFilesystem() {
88 if (!syncPendingAttributes())
89 ERROR(
"FAT: pending attributes remain during unmount");
90 clearPendingAttributes();
101 if (!superblock || superblock.size() < (36 +
sizeof(
Superblock32))) {
107 uint8_t* buffer = superblock.as<uint8_t>();
109 MemoryCopy(
reinterpret_cast<void*
>(&
m_Superblock),
reinterpret_cast<void*
>(buffer),
111 MemoryCopy(
reinterpret_cast<void*
>(&m_Superblock16),
reinterpret_cast<void*
>(buffer + 36),
113 MemoryCopy(
reinterpret_cast<void*
>(&m_Superblock32),
reinterpret_cast<void*
>(buffer + 36),
124 ERROR(
"FAT: Superblock not found on device " << devName <<
" [" <<
m_Superblock.BS_jmpBoot[0]
134 ERROR(
"FAT: SecPerClus not a power of 2 (" <<
m_Superblock.BPB_SecPerClus <<
")");
140 ERROR(
"FAT: Too many (or too few) FATs (" <<
m_Superblock.BPB_NumFATs <<
")");
152 uint32_t rootDirSectors =
161 uint32_t firstDataSector =
167 totDataSec = totSec - firstDataSector;
171 ERROR(
"FAT: SecPerClus is zero!");
174 uint32_t clusterCount = totDataSec /
m_Superblock.BPB_SecPerClus;
179 if (clusterCount < 4085) {
181 NOTICE(
"FAT: Device " << devName <<
" is type FAT12");
182 }
else if (clusterCount < 65525) {
184 NOTICE(
"FAT: Device " << devName <<
" is type FAT16");
187 NOTICE(
"FAT: Device " << devName <<
" is type FAT32");
200 m_RootDir.cluster = m_Superblock32.BPB_RootClus;
207 m_RootDirCount = rootDirSectors;
212 uint32_t sec = m_Superblock32.BPB_FsInfo;
230 const uint32_t cleanMask =
m_Type == FAT16 ? 0xc000 : 0x0c000000;
232 WARNING(
"FAT: volume was not clean at mount; filesystem check required");
248void FatFilesystem::loadRootDir() {
254 uint32_t cluster = 0;
256 cluster = m_RootDir.cluster;
259 info.creationTime = info.modifiedTime = info.accessedTime = 0;
268void FatFilesystem::cacheVolumeLabel() {
279 clus = m_RootDir.cluster;
285 ERROR(
"FAT: unable to read the root directory while finding its volume label");
290 bool endOfDir =
false;
292 for (i = 0; i < sz; i +=
sizeof(
Dir)) {
293 Dir* ent =
reinterpret_cast<Dir*
>(&buffer[i]);
295 if (ent->DIR_Name[0] == 0) {
300 if (ent->DIR_Attr & ATTR_VOLUME_ID) {
301 const String shortName(
reinterpret_cast<const char*
>(ent->DIR_Name), 11,
true);
312 if (clus == 0 &&
m_Type != FAT32)
325 if (!
readCluster(clus,
reinterpret_cast<uintptr_t
>(buffer)))
333 str +=
"no-volume-label@";
334 str.append(
reinterpret_cast<uintptr_t
>(
this), 16);
344uint64_t FatFilesystem::read(
File* pFile, uint64_t location, uint64_t size, uintptr_t buffer,
347 if (!tryAcquireOperation(operation)) {
358 uint32_t clus = pFile->getInode();
363 if (location >= pFile->getSize()) {
364 WARNING(
"FAT: Attempting to read past the EOF [loc=" << location <<
", sz=" << size
365 <<
", fsz=" << pFile->getSize() <<
"]");
369 uint64_t endOffset = location + size;
370 uint64_t finalSize = size;
371 if (endOffset > pFile->getSize()) {
372 finalSize = pFile->getSize() - location;
375 if ((finalSize == 0) || (finalSize > pFile->getSize())) {
376 WARNING(
"FAT: location + size > EOF");
384 uint32_t firstOffset = location % (
m_Superblock.BPB_SecPerClus *
391 uint64_t bytesRead = 0;
392 uint64_t currOffset = firstOffset;
393 clus = fileClusterAt(pFile, clusOffset);
399 uint8_t* destBuffer =
reinterpret_cast<uint8_t*
>(buffer);
404 if (!
readCluster(clus,
reinterpret_cast<uintptr_t
>(tmpBuffer))) {
410 size_t bytesToCopy = finalSize - bytesRead;
416 MemoryCopy(&destBuffer[bytesRead], &tmpBuffer[currOffset], bytesToCopy);
417 bytesRead += bytesToCopy;
420 if (bytesRead == finalSize) {
440 WARNING(
"FAT: read returning zero... Something's not right.");
447#if THREADS || defined(STANDALONE_MUTEXES)
454 const uint32_t first =
457 const uint32_t cluster = 2 + ((first - 2 + scanned) %
m_ClusterCount);
466 *persisted = succeeded;
479 SYSCALL_ERROR(NoSpaceLeftOnDevice);
485uint64_t FatFilesystem::write(
File* file, uint64_t location, uint64_t size, uintptr_t buffer,
488 if (!tryAcquireOperation(operation)) {
493 SYSCALL_ERROR(ReadOnlyFilesystem);
498 if (!buffer || file->
isDirectory() || location > UINT32_MAX || size > UINT32_MAX - location) {
499 SYSCALL_ERROR(InvalidArgument);
502 if (!syncFat() || !ensureCapacity(file, location + size)) {
503 SYSCALL_ERROR(IoError);
507 const size_t oldSize = file->getSize();
508 if (location > oldSize && !zeroRange(file, oldSize, location)) {
509 SYSCALL_ERROR(IoError);
512 uint32_t cluster = fileClusterAt(file, location /
m_BlockSize);
514 SYSCALL_ERROR(IoError);
519 uint64_t written = 0;
521 while (written < size) {
523 if (length > size - written)
524 length = size - written;
525 if (!
readCluster(cluster,
reinterpret_cast<uintptr_t
>(temporary)))
527 MemoryCopy(temporary + offset,
reinterpret_cast<void*
>(buffer + written), length);
528 if (!
writeCluster(cluster,
reinterpret_cast<uintptr_t
>(temporary)))
540 const size_t newSize = written && location + written > oldSize ? location + written : oldSize;
541 const bool metadataDirty = !file->
isSymlink() &&
static_cast<FatFile*
>(file)->m_MetadataDirty;
542 if ((newSize != oldSize || metadataDirty) && !updateFileMetadata(file, newSize)) {
543 SYSCALL_ERROR(IoError);
546 if (newSize != oldSize)
547 publishSize(file, newSize);
549 SYSCALL_ERROR(IoError);
553uint64_t FatFilesystem::allocatedBlocks(
File* file) {
555 if (!tryAcquireOperation(operation)) {
559 return (uint64_t(m_RootDirCount) *
m_Superblock.BPB_BytsPerSec + 511) / 512;
560 uint32_t count = 0, last = 0;
561 if (!chainExtent(file, count, last)) {
562 WARNING(
"FAT: unable to count an invalid cluster chain");
568bool FatFilesystem::ensureCapacity(
File* file,
size_t size) {
571 uint32_t count = 0, last = 0;
572 if (!chainExtent(file, count, last))
575 if (count >= required)
579 if (state && !state->clusters.tryReserve(required)) {
580 SYSCALL_ERROR(OutOfMemory);
583#if THREADS || defined(STANDALONE_MUTEXES)
588 bool complete =
true;
589 while (count < required) {
591 const uint32_t first =
594 const uint32_t cluster = 2 + (first - 2 + scanned) %
m_ClusterCount;
610 file->setInode(found);
613 state->chainRevision = __atomic_load_n(&m_ChainRevision, __ATOMIC_ACQUIRE);
619 static_cast<FatFile*
>(file)->m_MetadataDirty =
true;
623 return syncFat(
false) && complete;
626bool FatFilesystem::updateFileMetadata(
File* file,
size_t size) {
627 if (isNodeUnlinked(file))
629 uint32_t directoryCluster = 0, directoryOffset = 0;
633 directoryCluster = directory->getDirCluster();
634 directoryOffset = directory->getDirOffset();
637 directoryCluster = symlink->getDirCluster();
638 directoryOffset = symlink->getDirOffset();
640 regular =
static_cast<FatFile*
>(file);
641 regular->m_MetadataDirty =
true;
642 directoryCluster = regular->getDirCluster();
643 directoryOffset = regular->getDirOffset();
648 writeEntryAttributes(file, entry);
650 entry->DIR_FileSize = HOST_TO_LITTLE32(size);
651 const uintptr_t cluster = regular && !size ? 0 : file->getInode();
652 entry->DIR_FstClusLO = HOST_TO_LITTLE16(cluster & 0xFFFF);
653 entry->DIR_FstClusHI = HOST_TO_LITTLE16((cluster >> 16) & 0xFFFF);
656 if (regular && succeeded)
657 regular->m_MetadataDirty =
false;
661bool FatFilesystem::zeroRange(
File* file,
size_t begin,
size_t end) {
664 uint32_t cluster = fileClusterAt(file, begin /
m_BlockSize);
669 bool succeeded =
true;
675 while (begin < end) {
681 const size_t length = pedigree_std::min(
size_t(
m_BlockSize - offset), end - begin);
682 requests[pending++] = {
685 if (pending == Disk::MaxWriteBuffers)
694 return succeeded && durable;
697bool FatFilesystem::syncFileMetadata(
File* file) {
704 regular->copyStateAttributes();
705 if (isNodeUnlinked(file))
707 if (regular->m_MetadataDirty && !updateFileMetadata(file, file->getSize()))
713 if (clus == 0 &&
m_Type == FAT32)
717 uint8_t* dirBuffer =
new uint8_t[size];
733 const uint32_t size = m_RootDirCount *
m_Superblock.BPB_BytsPerSec;
743 bool secMethod =
false;
745 uint32_t sec = m_RootDir.sector;
756 return writeCluster(clus,
reinterpret_cast<uintptr_t
>(p));
761 if (offset > size || (size - offset) <
sizeof(
Dir))
768 Dir* ent =
reinterpret_cast<Dir*
>(&dirBuffer[offset]);
770 MemoryCopy(ret, ent,
sizeof(
Dir));
784 if (offset > size || (size - offset) <
sizeof(
Dir))
791 Dir* ent =
reinterpret_cast<Dir*
>(&dirBuffer[offset]);
792 MemoryCopy(ent, dir,
sizeof(
Dir));
800void FatFilesystem::cacheDirectoryContents(
File* pFile) {}
809 if (!tryAcquireOperation(operation)) {
818 size_t sz = (size > 512) ? 512 : size;
819 const uint64_t diskLocation =
820 (
static_cast<uint64_t
>(
m_Superblock.BPB_BytsPerSec) *
static_cast<uint64_t
>(sec)) + off;
822 if (!diskBuffer || diskBuffer.size() < sz) {
828 MemoryCopy(
reinterpret_cast<void*
>(buffer), diskBuffer.data(), sz);
844 if (!tryAcquireOperation(operation)) {
850 reinterpret_cast<void*
>(buffer), size);
852 return written && durable;
859uint8_t* FatFilesystem::getFatSector(uint32_t sector) {
860 uint8_t* bytes =
reinterpret_cast<uint8_t*
>(m_FatCache.
lookup(sector));
865 reinterpret_cast<uintptr_t
>(bytes))) {
869 m_FatCache.
insert(sector,
reinterpret_cast<uintptr_t
>(bytes));
876 const uint32_t offset =
877 m_Type == FAT12 ? cluster + cluster / 2 : cluster * (
m_Type == FAT16 ? 2 : 4);
878 const size_t length =
m_Type == FAT32 ? 4 : 2;
881 for (
size_t byte = 0;
byte < length; ++byte) {
882 const uint32_t sector = (offset + byte) /
m_Superblock.BPB_BytsPerSec;
883 const uint8_t* bytes = getFatSector(sector);
888 entry |= uint32_t(bytes[(offset +
byte) %
m_Superblock.BPB_BytsPerSec]) << (8 * byte);
892 return (entry >> ((cluster & 1) ? 4 : 0)) & 0x0FFF;
893 return entry & (
m_Type == FAT16 ? 0xFFFF : 0x0FFFFFFF);
899#if THREADS || defined(STANDALONE_MUTEXES)
902 const uint32_t offset =
903 m_Type == FAT12 ? cluster + cluster / 2 : cluster * (
m_Type == FAT16 ? 2 : 4);
904 const size_t length =
m_Type == FAT32 ? 4 : 2;
905 uint8_t* bytes[4] = {};
906 uint32_t original = 0;
908 for (
size_t byte = 0;
byte < length; ++byte) {
909 const uint32_t sector = (offset + byte) /
m_Superblock.BPB_BytsPerSec;
910 uint8_t* page = getFatSector(sector);
915 bytes[byte] = page + (offset + byte) %
m_Superblock.BPB_BytsPerSec;
916 original |= uint32_t(*bytes[
byte]) << (8 * byte);
919 value = cluster & 1 ? (original & 0x000F) | ((value & 0x0FFF) << 4)
920 : (original & 0xF000) | (value & 0x0FFF);
922 value = (original & 0xF0000000) | (value & 0x0FFFFFFF);
923 if (original != value)
924 __atomic_add_fetch(&m_ChainRevision, uint64_t(1), __ATOMIC_RELEASE);
925 for (
size_t byte = 0;
byte < length; ++byte) {
926 *bytes[byte] =
static_cast<uint8_t
>(value >> (8 * byte));
930 return !persist || syncFat(
false);
933bool FatFilesystem::syncFat(
bool bLock) {
935 if (!tryAcquireOperation(operation)) {
938#if THREADS || defined(STANDALONE_MUTEXES)
942 if (!m_DirtyFatSectors.
count())
944 if (!invalidateFsInfoHints())
946 const uint32_t sectorsPerFat =
951 bool succeeded =
true;
957 for (
auto it = m_DirtyFatSectors.
begin(); it != m_DirtyFatSectors.
end(); ++it) {
958 const uint32_t sector = it.key();
959 const uintptr_t bytes = m_FatCache.
lookup(sector);
964 for (
size_t copy = 0; copy < copies; ++copy) {
965 requests[pending++] = {
967 reinterpret_cast<void*
>(bytes),
m_Superblock.BPB_BytsPerSec,
false};
968 if (pending == Disk::MaxWriteBuffers)
974 if (!succeeded || !durable)
976 m_DirtyFatSectors.
clear();
984 if (!StringCompare(
static_cast<const char*
>(fn),
".") ||
985 !StringCompare(
static_cast<const char*
>(fn),
"..")) {
989 return String(
static_cast<const char*
>(ret));
998 size_t lastPeriod = ~0UL;
999 for (
size_t i = 0; i < fn.length(); ++i) {
1001 if (fn[i] ==
' ' || fn[i] ==
'"' || fn[i] ==
'/' || fn[i] ==
'\\' || fn[i] ==
'[' ||
1002 fn[i] ==
']' || fn[i] ==
':' || fn[i] ==
';' || fn[i] ==
'=' || fn[i] ==
',')
1004 else if (fn[i] ==
'.') {
1005 if ((i + 1) >= fn.length()) {
1012 filename += toUpper(fn[i]);
1017 if (lastPeriod > 8) {
1018 filename.truncate(6);
1023 else if (lastPeriod != ~0UL) {
1024 filename.truncate(lastPeriod);
1028 if (!filename.length()) {
1033 for (
size_t i = 0; i < 6; ++i) {
1034 if ((lastPeriod + i) >= fn.length())
1036 filename += toUpper(fn[lastPeriod + i]);
1042 return String(
static_cast<const char*
>(filename));
1047 for (
size_t i = 1; i < 4; ++i) {
1048 if ((lastPeriod + i) >= fn.length())
1050 ext += toUpper(fn[lastPeriod + i]);
1058 filename.append(ext);
1060 return String(
static_cast<const char*
>(filename));
1067 for (i = 0; i < 8; i++) {
1068 if (i >= filename.length())
1070 if (filename[i] !=
' ')
1071 ret += toLower(filename[i]);
1076 for (i = 0; i < 3; i++) {
1077 if ((8 + i) >= filename.length())
1079 if (filename[8 + i] !=
' ') {
1082 ret += toLower(filename[8 + i]);
1089 return String(
static_cast<const char*
>(ret));
1093 bool bDirectory, uint32_t dirClus,
bool publish) {
1095 SYSCALL_ERROR(ReadOnlyFilesystem);
1104 info.creationTime = Time::getTime();
1105 info.modifiedTime = info.creationTime;
1106 info.accessedTime = info.creationTime;
1114 pFile =
new FatDirectory(filename, dirClus,
this, parentDir, info);
1121 uint32_t clus = dirClus;
1124 if (!
writeCluster(clus,
reinterpret_cast<uintptr_t
>(buffer))) {
1130 }
while (!
isEof(clus));
1134 new FatFile(filename, info.accessedTime, info.modifiedTime, info.creationTime, 0,
this, 0,
1143 if (!parent->
addEntry(filename, pFile, (bDirectory ? 1 : 0))) {
1161 SYSCALL_ERROR(ReadOnlyFilesystem);
1187 if (!fatParent->
addEntry(filename, f, 1)) {
1199 SYSCALL_ERROR(ReadOnlyFilesystem);
1208 info.creationTime = Time::getTime();
1209 info.modifiedTime = info.creationTime;
1210 info.accessedTime = info.creationTime;
1219 filename, info.accessedTime, info.modifiedTime, info.creationTime, clus,
this, value.length(),
1226 if (value.length() && write(pFile, 0, value.length(),
1227 reinterpret_cast<uintptr_t
>(value.cstr())) != value.length()) {
1234 String symlinkFilename = filename;
1235 symlinkFilename += FatDirectory::symlinkSuffix();
1238 if (!fatParent->
addEntry(symlinkFilename, pFile, 0)) {
1253#if THREADS || defined(STANDALONE_MUTEXES)
1256 const uint32_t first = clus;
1260 ERROR(
"Invalid or cyclic FAT cluster chain during release");
1266 ERROR(
"Found a free cluster in an allocated FAT chain");
1275 bool succeeded =
true;
1282 return syncFat(
false) && succeeded;
1287 SYSCALL_ERROR(ReadOnlyFilesystem);
1294 if (child == parentDir) {
1295 SYSCALL_ERROR(InvalidArgument);
1300 const Directory::ReadStatus status = child->
isEmpty(empty);
1301 if (status != Directory::ReadStatus::Complete) {
1302 SYSCALL_ERROR(IoError);
1306 SYSCALL_ERROR(NotEmpty);
1313 child->markDetached();
1322#ifndef FAT_STANDALONE
1323static bool initFat() {
1328static void destroyFat() {
1329 if (!
VFS::instance().removeProbeCallback(&FatFilesystem::probe)) {
1330 FATAL(
"FAT probe callback was not registered during unload");
1334MODULE_INFO(
"fat", &initFat, &destroyFat,
"vfs");
static Directory * fromFile(File *pF)
Mutex & namespaceMutationLock()
ReadStatus isEmpty(bool &empty)
virtual BufferView read(uint64_t location)
virtual void getName(String &str)
virtual void unpin(uint64_t location)=0
virtual MUST_USE_RESULT bool writeFromBatch(WriteBuffer *buffers, size_t count)
virtual MUST_USE_RESULT bool syncData()
virtual bool removeEntry(const String &filename, File *pFile)
virtual bool addEntry(String filename, File *pFile, size_t type, bool publish=true)
File * getRoot() const override
uint32_t eofValue() const
bool writeSectorBlock(uint32_t sec, size_t size, uintptr_t buffer)
String convertFilenameTo(String filename) const
uint32_t findFreeCluster(bool *persisted=nullptr)
bool writeCluster(uint32_t block, uintptr_t buffer)
void * readDirectoryPortion(uint32_t clus) const
uint32_t m_FreeClusterHint
bool writeDirectoryPortion(uint32_t clus, void *p)
bool createFile(File *parent, const String &filename, uint32_t mask) override
String convertFilenameFrom(String filename) const
Dir * getDirectoryEntry(uint32_t clus, uint32_t offset) const
bool createDirectory(File *parent, const String &filename, uint32_t mask) override
bool removeNode(File *parent, const String &filename, File *file) override
bool isEof(uint32_t cluster) const
uint32_t getClusterEntry(uint32_t cluster, bool bLock=true)
const String & getVolumeLabel() const override
uint32_t getSectorNumber(uint32_t cluster) const
bool setClusterEntry(uint32_t cluster, uint32_t value, bool bLock=true, bool persist=true)
bool createSymlink(File *parent, const String &filename, const String &value) override
bool readSectorBlock(uint32_t sec, size_t size, uintptr_t buffer) const
bool readCluster(uint32_t block, uintptr_t buffer) const
bool initialise(Disk *pDisk) override
bool releaseClusterChain(uint32_t clus, bool lockFile=true)
bool writeDirectoryEntry(Dir *dir, uint32_t clus, uint32_t offset)
virtual bool isDirectory()
E lookup(const K &key) const
void insert(const K &key, const E &value)
void addProbeCallback(Filesystem::ProbeCallback callback)
void pushBack(const T &value)