2#include "pedigree/kernel/machine/Disk.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "FatFilesystem.h"
9uint64_t slotKey(uint32_t cluster, uint32_t offset) {
10 return (uint64_t(cluster) << 32) | offset;
12bool publishedSlot(uint32_t cluster, uint32_t offset) {
13 return cluster != 0xdeadbeef && offset != 0xbeefdead;
17FatFile::State::State(
FatFilesystem* owner) : filesystem(owner) {
18 cache.fill.setDirtyTracking(Cache::DirtyTracking::Explicit);
19 cache.fill.setCallback(checkedWriteCallback,
this);
20 cache.fill.setBackgroundWriteback(checkedBatchCallback);
23FatFile::State::~State() {
25 ERROR(
"FAT: unable to drain file cache during unmount");
28uintptr_t FatFilesystem::fileIdentifier(uint32_t cluster, uint32_t offset) {
30 return fileIdentifierLocked(slotKey(cluster, offset));
33uintptr_t FatFilesystem::fileIdentifierLocked(uint64_t slot) {
34 uintptr_t identifier = m_FileIdentifiers.lookup(slot);
37 assert(m_NextFileIdentifier >= 0x10000000);
38 identifier = m_NextFileIdentifier++;
39 m_FileIdentifiers.insert(slot, identifier);
45 uint32_t cluster, uint32_t offset,
46 Time::Timestamp accessed, Time::Timestamp modified,
47 Time::Timestamp changed) {
49 const bool published = publishedSlot(cluster, offset);
50 FatFile::State* state = published ? m_FileStates.lookup(slotKey(cluster, offset)) :
nullptr;
55 state->directoryCluster = cluster;
56 state->directoryOffset = offset;
57 state->accessed = accessed;
58 state->modified = modified;
59 state->changed = changed;
60 state->next = m_StateList;
63 state->identifier = fileIdentifierLocked(slotKey(cluster, offset));
64 m_FileStates.insert(slotKey(cluster, offset), state);
65 state->registered =
true;
71void FatFilesystem::releaseFileState(
FatFile* file) {
72 auto* state = file->m_State;
75 const bool written = state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state);
76 if (m_pDisk && !syncFileMetadata(file))
77 WARNING(
"FAT: retaining pending metadata after close");
79 WARNING(
"FAT: retaining dirty file pages after close");
84 FatFile** alias = &state->aliases;
85 while (*alias && *alias != file)
86 alias = &(*alias)->m_NextAlias;
88 *alias = file->m_NextAlias;
89 retire = state->unlinked && !state->aliases;
91 state->retiring =
true;
102void FatFilesystem::publishSize(
File* file,
size_t size) {
107 auto* regular =
static_cast<FatFile*
>(file);
109 regular->m_State->size = size;
110 regular->File::setSize(size);
111 for (
FatFile* alias = regular->m_State->aliases; alias; alias = alias->m_NextAlias)
112 alias->File::setSize(size);
115bool FatFilesystem::isNodeUnlinked(
File* file)
const {
119 return static_cast<FatSymlink*
>(file)->m_Unlinked;
120 return static_cast<FatFile*
>(file)->m_State->unlinked;
123void FatFilesystem::unlinkNode(
File* file) {
127 discardPendingAttributes(node->getDirCluster(), node->getDirOffset());
130 discardPendingAttributes(node->getDirCluster(), node->getDirOffset());
132 unlinkNonFileNode(file);
136 auto* state =
static_cast<FatFile*
>(file)->m_State;
137 state->unlinked =
true;
138 if (state->registered) {
139 m_FileStates.remove(slotKey(state->directoryCluster, state->directoryOffset));
140 m_FileIdentifiers.remove(slotKey(state->directoryCluster, state->directoryOffset));
141 state->registered =
false;
145void FatFilesystem::moveNode(
File* file, uint32_t cluster, uint32_t offset) {
149 movePendingAttributes(node->getDirCluster(), node->getDirOffset(), cluster, offset);
152 movePendingAttributes(node->getDirCluster(), node->getDirOffset(), cluster, offset);
154 moveNonFileNode(file, cluster, offset);
157 auto* state =
static_cast<FatFile*
>(file)->m_State;
158 if (state->registered) {
159 m_FileStates.remove(slotKey(state->directoryCluster, state->directoryOffset));
160 m_FileIdentifiers.remove(slotKey(state->directoryCluster, state->directoryOffset));
162 state->directoryCluster = cluster;
163 state->directoryOffset = offset;
164 if (!state->identifier)
165 state->identifier = fileIdentifierLocked(slotKey(cluster, offset));
167 m_FileIdentifiers.insert(slotKey(cluster, offset), state->identifier);
168 m_FileStates.insert(slotKey(cluster, offset), state);
169 state->registered =
true;
173void FatFilesystem::retireNode(
File* file) {
178 auto* regular =
static_cast<FatFile*
>(file);
179 for (uint32_t cluster : regular->m_RetiredClusters) {
180 if (!setClusterEntry(cluster, 0))
183 regular->m_RetiredClusters.
clear();
185 if (!releaseClusterChain(file->getInode(),
false)) {
187 ERROR(
"FAT: orphan allocation reclamation needs a FAT retry");
194 return SyncStatus::IoError;
196 return m_IoFailed ? SyncStatus::IoError : SyncStatus::Success;
200 for (
auto* state = m_StateList; state; state = state->next) {
201 if (!states.tryReserve(states.
count() + 1))
202 return SyncStatus::NoMemory;
206 bool succeeded =
true;
207 for (
auto* state : states) {
212 succeeded = state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state) && succeeded;
213 succeeded = syncFileMetadata(&file) && succeeded;
216 succeeded = syncPendingAttributes() && succeeded;
217 succeeded = syncFat() && succeeded;
218 succeeded = m_pDisk->syncAll() && succeeded;
219 return succeeded ? SyncStatus::Success : SyncStatus::IoError;
224 if (m_ShutdownComplete)
225 return SyncStatus::Success;
226 auto status =
sync();
227 if (status != SyncStatus::Success)
231 if (!drainFileStates(
true))
232 return SyncStatus::IoError;
235 if (status != SyncStatus::Success || m_IoFailed || !m_MountedClean)
236 return status == SyncStatus::Success ? SyncStatus::IoError : status;
237 m_ShutdownComplete =
true;
238 return SyncStatus::Success;
241bool FatFilesystem::drainFileStates(
bool checked) {
242 bool succeeded =
true;
243 while (m_StateList) {
244 auto* state = m_StateList;
245 if (checked && state->aliases)
247 if (!state->retiring && m_pDisk) {
249 if (!state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state)) {
251 ERROR(
"FAT: dirty pages remain during unmount");
253 succeeded = syncFileMetadata(&file) && succeeded;
255 if (checked && (!succeeded || !state->cache.fill.
shutdown()))
257 m_StateList = state->next;
260 m_FileStates.clear();
261 m_FileIdentifiers.clear();
MUST_USE_RESULT bool syncAll()
SyncStatus sync() override
SyncStatus shutdown() override
virtual bool isDirectory()
A vector / dynamic array.
void pushBack(const T &value)
void clear(bool freeMem=false)