The Pedigree Project 0.1
Ext2Filesystem-sync.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Disk.h"
5#include "pedigree/kernel/process/TerminationDeferral.h"
6#include "pedigree/kernel/utilities/Vector.h"
7#include "pedigree/kernel/utilities/assert.h"
8
9#include "Ext2Directory.h"
10#include "Ext2File.h"
11#include "Ext2Filesystem.h"
12#include "Ext2Node.h"
13#include "ext2.h"
14
15bool Ext2Filesystem::beginWritableMount() {
16 if (checkOptionalFeature(0x4) || checkRequiredFeature(~size_t(0x2)) ||
17 checkReadOnlyFeature(~size_t(0x3))) {
18 ERROR("Ext2: unsupported filesystem features prevent a writable mount");
19 return false;
20 }
21 m_MountState = LITTLE_TO_HOST16(m_pSuperblock->s_state);
22 m_pSuperblock->s_state = HOST_TO_LITTLE16(m_MountState & ~EXT2_STATE_CLEAN);
23 m_pDisk->write(1024ULL);
24#if !CRIPPLE_HDD || defined(EXT2_STANDALONE)
25 if (!m_pDisk->sync(1024ULL, false)) {
26 ERROR("Ext2: could not persist the writable mount state");
27 return false;
28 }
29#endif
30 return true;
31}
32
33Filesystem::SyncStatus Ext2Filesystem::shutdown() {
34 TerminationDeferral lifetime;
35 if (m_ShutdownComplete)
36 return SyncStatus::Success;
37 if (m_bReadOnly)
38 return sync();
40 return SyncStatus::IoError;
41
42 auto status = sync();
43 if (status != SyncStatus::Success)
44 return status;
45 if (!closeQuotaFiles(false))
46 return SyncStatus::IoError;
47 delete m_pRoot;
48 m_pRoot = nullptr;
49 // Releasing directory aliases can retire orphan allocations and quota owners.
50 // All of those writes, and callback retirement, precede the clean marker.
51 status = sync();
52 if (status != SyncStatus::Success)
53 return status;
54 for (auto it = m_InodeStates.begin(); it != m_InodeStates.end(); ++it) {
55 auto* state = it.value();
56 if (state->references || state->pageLoans || state->syncReferences || state->orphan)
57 return SyncStatus::IoError;
58 if (state->cache && !state->cache->fill.shutdown())
59 return SyncStatus::IoError;
60 }
61 for (auto it = m_InodeStates.begin(); it != m_InodeStates.end(); ++it)
62 delete it.value();
63 m_InodeStates.clear();
64 if (!m_pDisk->syncAll() || m_TeardownFailed)
65 return SyncStatus::IoError;
66 // This driver cannot certify journal recovery or unknown incompatible formats.
67 if (checkOptionalFeature(0x4) || checkRequiredFeature(~size_t(0x2)) ||
68 checkReadOnlyFeature(~size_t(0x3)))
69 return SyncStatus::Unsupported;
70
71 // Draining this mount's writes cannot certify pre-existing filesystem damage.
72 // Leave the unchecked marker in place, but do not prevent an orderly shutdown.
73 if (m_MountState != EXT2_STATE_CLEAN) {
74 m_ShutdownComplete = true;
75 return SyncStatus::Success;
76 }
77
78 m_pSuperblock->s_state = HOST_TO_LITTLE16(m_MountState);
79 m_pDisk->write(1024ULL);
80 if (!m_pDisk->sync(1024ULL, false) || !m_pDisk->syncAll()) {
81 m_pSuperblock->s_state = HOST_TO_LITTLE16(m_MountState & ~EXT2_STATE_CLEAN);
82 m_pDisk->write(1024ULL);
83 if (!m_pDisk->sync(1024ULL, false))
84 ERROR("Ext2: could not restore the unchecked state after shutdown I/O failure");
85 return SyncStatus::IoError;
86 }
87 m_ShutdownComplete = true;
88 return SyncStatus::Success;
89}
90
92 public:
93 explicit SyncSnapshot(Ext2Filesystem& filesystem) : filesystem(filesystem) {}
95 for (const auto& entry : entries)
96 filesystem.releaseSyncState(entry.inode, entry.state);
97 }
98
99 struct Entry {
100 uint32_t inode;
101 Ext2InodeState* state;
102 };
103 Ext2Filesystem& filesystem;
104 Vector<Entry> entries;
105};
106
107void Ext2Filesystem::releaseSyncState(uint32_t inode, Ext2InodeState* state) {
108 assert(__atomic_load_n(&state->syncReferences, __ATOMIC_RELAXED));
109 __atomic_sub_fetch(&state->syncReferences, size_t(1), __ATOMIC_RELEASE);
110 // The last admission must provide wipe() a node when final unlink raced sync.
111 Ext2Node admission(inode, state->metadata, this, *state);
112}
113
114Filesystem::SyncStatus Ext2Filesystem::sync() {
115 TerminationDeferral lifetime;
116 if (m_bReadOnly)
117 return SyncStatus::Success;
118 if (!m_pDisk || !m_pSuperblock || !m_BlockSize)
119 return SyncStatus::IoError;
120
121 const auto quotaStatus = flushQuotas();
122
123 SyncSnapshot snapshot(*this);
124 for (;;) {
125 size_t required;
126 {
127 LockGuard<Mutex> registry(m_InodeStateLock);
128 required = m_InodeStates.count();
129 if (required <= snapshot.entries.size()) {
130 for (auto it = m_InodeStates.begin(); it != m_InodeStates.end(); ++it) {
131 Ext2InodeState* state = it.value();
132 // Dormant identities without a cache have discarded their block maps.
133 // Pinning those would suppress the next opener's mapping reload.
134 if (!state->references && !state->cache)
135 continue;
136 if (state->references == ~size_t(0))
137 return SyncStatus::NoMemory;
138 ++state->references;
139 __atomic_add_fetch(&state->syncReferences, size_t(1), __ATOMIC_RELEASE);
140 snapshot.entries.pushBack({it.key(), state});
141 }
142 break;
143 }
144 }
145 // Allocation can re-enter filesystem caches under memory pressure.
146 if (!snapshot.entries.tryReserve(required, false))
147 return SyncStatus::NoMemory;
148 }
149
150 bool succeeded = quotaStatus == QuotaStatus::Success;
151 for (const auto& entry : snapshot.entries) {
152 LockGuard<Mutex> data(entry.state->dataLock);
153 if (!entry.state->allocationValid)
154 succeeded = false;
155 if (entry.state->cache)
156 succeeded =
157 entry.state->cache->fill.syncAll(Ext2File::sharedFillBatchCallback, entry.state) &&
158 succeeded;
159 }
160
161 {
162#if THREADS || defined(STANDALONE_MUTEXES)
163 LockGuard<Mutex> allocation(m_WriteLock);
164#endif
165 // A whole-disk drain must not bypass failed xattr dependency ordering.
166 if (!flushAttributeWritesLocked())
167 return SyncStatus::IoError;
168 succeeded = m_pDisk->syncAll() && succeeded;
169 }
170 return succeeded ? SyncStatus::Success
171 : quotaStatus == QuotaStatus::NoMemory ? SyncStatus::NoMemory
172 : SyncStatus::IoError;
173}
virtual bool sync(uint64_t location, bool async)
Definition Disk.cc:358
virtual void write(uint64_t location)
Definition Disk.cc:340
virtual MUST_USE_RESULT bool syncAll()
Definition Disk.cc:375
virtual SyncStatus sync()
virtual SyncStatus shutdown()
Superblock * m_pSuperblock
Disk * m_pDisk
Definition Filesystem.h:180
bool m_bReadOnly
Definition Filesystem.h:178
Iterator begin()
Definition Tree.h:402
void clear()
Definition Tree.h:383
Iterator end()
Definition Tree.h:427
size_t count() const
Definition Tree.h:142
A vector / dynamic array.
Definition Vector.h:33
#define assert(x)
Definition assert.h:39