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");
32 if (!beginDeviceRemoval(deviceAvailable)) {
36 for (
auto* state = m_StateList; state; state = state->next) {
38 if (!state->cache.fill.shutdown(Cache::ShutdownMode::DiscardDeferred)) {
41 state->retiring =
true;
42 state->metadataDirty =
false;
43 state->trimPending =
false;
44 state->retiredClusters.clear();
48 clearPendingAttributes();
50 for (
auto it = m_FatCache.begin(); it != m_FatCache.end(); ++it) {
51 delete[]
reinterpret_cast<uint8_t*
>(it.value());
54 m_DirtyFatSectors.clear();
59uintptr_t FatFilesystem::fileIdentifier(uint32_t cluster, uint32_t offset) {
61 return fileIdentifierLocked(slotKey(cluster, offset));
64uintptr_t FatFilesystem::fileIdentifierLocked(uint64_t slot) {
65 uintptr_t identifier = m_FileIdentifiers.lookup(slot);
68 assert(m_NextFileIdentifier >= 0x10000000);
69 identifier = m_NextFileIdentifier++;
70 m_FileIdentifiers.insert(slot, identifier);
76 uint32_t cluster, uint32_t offset,
77 Time::Timestamp accessed, Time::Timestamp modified,
78 Time::Timestamp changed) {
80 const bool published = publishedSlot(cluster, offset);
81 FatFile::State* state = published ? m_FileStates.lookup(slotKey(cluster, offset)) :
nullptr;
86 state->directoryCluster = cluster;
87 state->directoryOffset = offset;
88 state->accessed = accessed;
89 state->modified = modified;
90 state->changed = changed;
91 state->next = m_StateList;
94 state->identifier = fileIdentifierLocked(slotKey(cluster, offset));
95 m_FileStates.insert(slotKey(cluster, offset), state);
96 state->registered =
true;
102void FatFilesystem::releaseFileState(
FatFile* file) {
103 auto* state = file->m_State;
107 const bool available = tryAcquireOperation(operation);
109 !available || state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state);
110 if (available && m_pDisk && !syncFileMetadata(file)) {
111 WARNING(
"FAT: retaining pending metadata after close");
114 WARNING(
"FAT: retaining dirty file pages after close");
119 FatFile** alias = &state->aliases;
120 while (*alias && *alias != file)
121 alias = &(*alias)->m_NextAlias;
123 *alias = file->m_NextAlias;
124 retire = state->unlinked && !state->aliases;
126 state->retiring =
true;
128 if (retire && available) {
137void FatFilesystem::publishSize(
File* file,
size_t size) {
142 auto* regular =
static_cast<FatFile*
>(file);
144 regular->m_State->size = size;
145 regular->File::setSize(size);
146 for (
FatFile* alias = regular->m_State->aliases; alias; alias = alias->m_NextAlias)
147 alias->File::setSize(size);
150bool FatFilesystem::isNodeUnlinked(
File* file)
const {
154 return static_cast<FatSymlink*
>(file)->m_Unlinked;
155 return static_cast<FatFile*
>(file)->m_State->unlinked;
158void FatFilesystem::unlinkNode(
File* file) {
162 discardPendingAttributes(node->getDirCluster(), node->getDirOffset());
165 discardPendingAttributes(node->getDirCluster(), node->getDirOffset());
167 unlinkNonFileNode(file);
171 auto* state =
static_cast<FatFile*
>(file)->m_State;
172 state->unlinked =
true;
173 if (state->registered) {
174 m_FileStates.remove(slotKey(state->directoryCluster, state->directoryOffset));
175 m_FileIdentifiers.remove(slotKey(state->directoryCluster, state->directoryOffset));
176 state->registered =
false;
180void FatFilesystem::moveNode(
File* file, uint32_t cluster, uint32_t offset) {
184 movePendingAttributes(node->getDirCluster(), node->getDirOffset(), cluster, offset);
187 movePendingAttributes(node->getDirCluster(), node->getDirOffset(), cluster, offset);
189 moveNonFileNode(file, cluster, offset);
192 auto* state =
static_cast<FatFile*
>(file)->m_State;
193 if (state->registered) {
194 m_FileStates.remove(slotKey(state->directoryCluster, state->directoryOffset));
195 m_FileIdentifiers.remove(slotKey(state->directoryCluster, state->directoryOffset));
197 state->directoryCluster = cluster;
198 state->directoryOffset = offset;
199 if (!state->identifier)
200 state->identifier = fileIdentifierLocked(slotKey(cluster, offset));
202 m_FileIdentifiers.insert(slotKey(cluster, offset), state->identifier);
203 m_FileStates.insert(slotKey(cluster, offset), state);
204 state->registered =
true;
208void FatFilesystem::retireNode(
File* file) {
213 auto* regular =
static_cast<FatFile*
>(file);
214 for (uint32_t cluster : regular->m_RetiredClusters) {
215 if (!setClusterEntry(cluster, 0))
218 regular->m_RetiredClusters.
clear();
220 if (!releaseClusterChain(file->getInode(),
false)) {
222 ERROR(
"FAT: orphan allocation reclamation needs a FAT retry");
228 if (!tryAcquireOperation(operation)) {
229 return SyncStatus::IoError;
233 return SyncStatus::IoError;
235 return m_IoFailed ? SyncStatus::IoError : SyncStatus::Success;
239 for (
auto* state = m_StateList; state; state = state->next) {
240 if (!states.tryReserve(states.
count() + 1))
241 return SyncStatus::NoMemory;
245 bool succeeded =
true;
246 for (
auto* state : states) {
251 succeeded = state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state) && succeeded;
252 succeeded = syncFileMetadata(&file) && succeeded;
255 succeeded = syncPendingAttributes() && succeeded;
256 succeeded = syncFat() && succeeded;
257 succeeded = m_pDisk->syncAll() && succeeded;
258 return succeeded ? SyncStatus::Success : SyncStatus::IoError;
263 if (!tryAcquireOperation(operation)) {
264 return SyncStatus::IoError;
267 if (m_ShutdownComplete)
268 return SyncStatus::Success;
269 auto status =
sync();
270 if (status != SyncStatus::Success)
274 if (!drainFileStates(
true))
275 return SyncStatus::IoError;
280 if (status != SyncStatus::Success || m_IoFailed) {
281 return status == SyncStatus::Success ? SyncStatus::IoError : status;
283 m_ShutdownComplete =
true;
284 return SyncStatus::Success;
287bool FatFilesystem::drainFileStates(
bool checked) {
288 bool succeeded =
true;
289 while (m_StateList) {
290 auto* state = m_StateList;
291 if (checked && state->aliases)
293 if (!state->retiring && m_pDisk) {
295 if (!state->cache.fill.
syncAll(FatFile::checkedBatchCallback, state)) {
297 ERROR(
"FAT: dirty pages remain during unmount");
299 succeeded = syncFileMetadata(&file) && succeeded;
301 if (checked && (!succeeded || !state->cache.fill.
shutdown()))
303 m_StateList = state->next;
306 m_FileStates.clear();
307 m_FileIdentifiers.clear();
bool shutdown(ShutdownMode mode=ShutdownMode::WriteBack)
MUST_USE_RESULT bool syncAll()
bool deviceRemoved(bool deviceAvailable=false) override
SyncStatus sync() override
SyncStatus shutdown() override
virtual bool isDirectory()
virtual bool deviceRemoved(bool deviceAvailable=false)
A vector / dynamic array.
void pushBack(const T &value)
void clear(bool freeMem=false)