2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Disk.h"
5#include "pedigree/kernel/syscallError.h"
6#include "pedigree/kernel/utilities/Pointers.h"
9#include "Ext2Filesystem.h"
15#include "pedigree/kernel/process/Thread.h"
16#include "pedigree/kernel/processor/Processor.h"
17#include "pedigree/kernel/processor/ProcessorInformation.h"
20int Ext2Filesystem::currentIoError() {
22 const int error = errno;
25 const int error = thread ? thread->getErrno() : 0;
27 return error ? error : Error::IoError;
30QuotaStatus Ext2Filesystem::quotaIoStatus() {
31 switch (currentIoError()) {
32 case Error::OutOfMemory:
33 return QuotaStatus::NoMemory;
34 case Error::ValueTooLarge:
35 return QuotaStatus::Overflow;
36 case Error::FileTooLarge:
37 return QuotaStatus::TooLarge;
38 case Error::NoSpaceLeftOnDevice:
39 return QuotaStatus::NoSpace;
40 case Error::QuotaExceeded:
41 return QuotaStatus::Limit;
43 return QuotaStatus::IoError;
47bool Ext2Filesystem::quotaSucceeded(QuotaStatus status) {
48 if (status == QuotaStatus::Success)
50 syscallError(quotaError(status));
54bool Ext2Filesystem::isQuotaFile(uint32_t inode) {
56 const auto* charge = m_Quota.find(inode);
57 return charge && charge->exempt;
60QuotaStatus Ext2Filesystem::prepareQuotaInodeLocked(uint32_t inode) {
61 if (m_Quota.find(inode))
62 return QuotaStatus::Success;
63 const uint32_t total = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_count);
64 const uint32_t perGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
65 if (!inode || !perGroup || (total && inode > total) ||
67 return QuotaStatus::Invalid;
69 const Inode* metadata = getInode(inode);
71 return quotaIoStatus();
72 return m_Quota.track(inode, Ext2Owner::uid(*metadata), Ext2Owner::gid(*metadata),
73 static_cast<uint64_t
>(LITTLE_TO_HOST32(metadata->i_blocks)) * 512);
76QuotaStatus Ext2Filesystem::scanQuotaInodesLocked() {
77 const uint32_t perGroup = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_per_group);
78 const uint32_t total = LITTLE_TO_HOST32(
m_pSuperblock->s_inodes_count);
79 uint32_t first = LITTLE_TO_HOST32(
m_pSuperblock->s_first_ino);
86 return QuotaStatus::IoError;
89 if (!ensureFreeInodeBitmapLoaded(group))
90 return quotaIoStatus();
91 for (uint32_t index = 0; index < perGroup; ++index) {
92 const uint64_t inode =
static_cast<uint64_t
>(group) * perGroup + index + 1;
95 if (inode < first && inode != 2)
99 return QuotaStatus::IoError;
100 const auto*
byte =
reinterpret_cast<const uint8_t*
>(
m_pInodeBitmaps[group][field] +
102 if (!(*
byte & (1U << (index % 8))))
105 const auto status = prepareQuotaInodeLocked(
static_cast<uint32_t
>(inode));
106 if (status != QuotaStatus::Success)
110 return QuotaStatus::Success;
113QuotaStatus Ext2Filesystem::flushQuotaLocked(QuotaType type) {
114 auto* file = m_QuotaFiles[
static_cast<size_t>(type)];
116 return QuotaStatus::Success;
118 bool prepared =
false;
119 for (
unsigned attempt = 0; attempt < 8; ++attempt) {
123 const auto& entries = m_Quota.table(type).entries();
124 required = entries.count();
125 if (required <= snapshot.size()) {
126 for (
const auto& entry : entries) {
128 snapshot.pushBack(entry);
134 if (!snapshot.tryReserve(required,
false))
135 return QuotaStatus::NoMemory;
139 return QuotaStatus::Busy;
140 QuotaStatus result = QuotaStatus::Success;
141 for (
const auto& entry : snapshot) {
142 const auto status = file->writeQuotaRecord(entry.id, entry.record);
143 if (result == QuotaStatus::Success)
149 return QuotaStatus::IoError;
153 return QuotaStatus::IoError;
155 if (result != QuotaStatus::Success)
159 for (
const auto& saved : snapshot) {
160 auto* entry = m_Quota.table(type).find(saved.id);
161 if (entry && QuotaTable::sameRecord(entry->record, saved.record))
162 entry->dirty =
false;
165 return QuotaStatus::Success;
168QuotaStatus Ext2Filesystem::flushQuotas() {
170 QuotaStatus result = QuotaStatus::Success;
171 const QuotaType types[] = {QuotaType::User, QuotaType::Group};
172 for (
auto type : types) {
173 const auto status = flushQuotaLocked(type);
174 if (result == QuotaStatus::Success)
183 if (!tryAcquireOperation(operation)) {
184 return QuotaStatus::IoError;
186 if (request.type != QuotaType::User && request.type != QuotaType::Group)
187 return QuotaStatus::Invalid;
189 return QuotaStatus::IoError;
191 const size_t index =
static_cast<size_t>(request.type);
192 if (request.operation == QuotaOperation::Enable) {
194 return QuotaStatus::ReadOnly;
195 if (request.format != Quota::OldFormat)
196 return QuotaStatus::Unsupported;
197 if (!quotaFile || quotaFile->getFilesystem() !=
this || quotaFile->
isDirectory() ||
199 return QuotaStatus::Invalid;
200 if (m_QuotaFiles[index])
201 return QuotaStatus::Busy;
206 metadata = getInode(quotaFile->getInode());
209 return QuotaStatus::IoError;
210 if ((LITTLE_TO_HOST16(metadata->i_mode) & 0xf000) != EXT2_S_IFREG)
211 return QuotaStatus::Invalid;
212 auto* file =
new Ext2File(quotaFile->
getName(), quotaFile->getInode(), metadata,
this);
214 return QuotaStatus::NoMemory;
215 if (!file->valid()) {
217 return QuotaStatus::NoMemory;
220 auto status = file->beginQuota(loaded);
221 if (status == QuotaStatus::Success) {
223 status = scanQuotaInodesLocked();
224 if (status == QuotaStatus::Success) {
225 const uint64_t slots = file->maximumFileSize() / QuotaOld::RecordSize;
226 const uint32_t maximumId = slots > 0x100000000ULL ? 0xffffffffU : slots - 1;
227 status = m_Quota.enable(request.type, loaded, maximumId);
230 if (status == QuotaStatus::Success) {
231 m_QuotaFiles[index] = file;
232 status = flushQuotaLocked(request.type);
234 if (status != QuotaStatus::Success) {
235 file->endQuota(request.type,
false);
236 m_QuotaFiles[index] =
nullptr;
242 if (!m_QuotaFiles[index])
243 return request.operation == QuotaOperation::Sync ? QuotaStatus::Success
244 : QuotaStatus::NotEnabled;
245 if (request.operation == QuotaOperation::Disable) {
247 for (
unsigned attempt = 0; attempt < 8; ++attempt) {
248 const auto status = flushQuotaLocked(request.type);
249 if (status != QuotaStatus::Success)
251#if defined(PEDIGREE_BUILDUTILS)
252 if (m_QuotaOffTestHook)
253 m_QuotaOffTestHook(m_QuotaOffTestContext);
255 const auto finished = m_QuotaFiles[index]->endQuota(request.type,
true);
256 if (finished == QuotaStatus::Busy)
258 if (finished != QuotaStatus::Success)
260 delete m_QuotaFiles[index];
261 m_QuotaFiles[index] =
nullptr;
262 return QuotaStatus::Success;
264 return QuotaStatus::Busy;
266 if (request.operation == QuotaOperation::Sync)
267 return flushQuotaLocked(request.type);
268 if (request.operation == QuotaOperation::GetFormat) {
269 response.format = Quota::OldFormat;
270 return QuotaStatus::Success;
272 if (request.operation == QuotaOperation::Get) {
274 const auto* entry = m_Quota.table(request.type).find(request.id);
275 response.record = entry ? entry->record :
QuotaRecord();
276 response.record.valid = Quota::Supported;
277 return QuotaStatus::Success;
279 if (request.operation == QuotaOperation::Set) {
281 return QuotaStatus::ReadOnly;
282 if ((
static_cast<uint64_t
>(request.id) + 1) * QuotaOld::RecordSize >
283 m_QuotaFiles[index]->maximumFileSize())
284 return QuotaStatus::Overflow;
288 status = m_Quota.table(request.type).set(request.id, request.record);
290 return status == QuotaStatus::Success ? flushQuotaLocked(request.type) : status;
292 return QuotaStatus::Unsupported;
295bool Ext2Filesystem::closeQuotaFiles(
bool discardOnFailure) {
297 bool succeeded =
true;
298 const QuotaType types[] = {QuotaType::User, QuotaType::Group};
299 for (
auto type : types) {
300 const size_t index =
static_cast<size_t>(type);
301 if (!m_QuotaFiles[index])
303 if (!isDeviceRemoved() && flushQuotaLocked(type) != QuotaStatus::Success) {
305 ERROR(
"Ext2: quota writeback failed at filesystem teardown");
306 if (!discardOnFailure)
309 m_QuotaFiles[index]->endQuota(type,
false);
310 delete m_QuotaFiles[index];
311 m_QuotaFiles[index] =
nullptr;
virtual MUST_USE_RESULT bool syncAll()
size_t m_nGroupDescriptors
Vector< size_t > * m_pInodeBitmaps
Superblock * m_pSuperblock
virtual bool isSocket() const
virtual bool isDirectory()
virtual bool isPipe() const
static ProcessorInformation & information()
A vector / dynamic array.