The Pedigree Project 0.1
Ext2File-quota.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/syscallError.h"
4
5#include "Ext2File.h"
6#include "Ext2Filesystem.h"
7#include "ext2.h"
8
9QuotaStatus Ext2File::beginQuota(QuotaTable& loaded) {
10 auto writes = lockWrites();
11 {
12 LockGuard<Mutex> data(m_State->dataLock);
13 LockGuard<Mutex> inode(m_State->writebackLock);
14 LockGuard<Mutex> allocation(m_pExt2Fs->m_WriteLock);
15 if (m_State->quotaFile || __atomic_load_n(&m_State->pageLoans, __ATOMIC_ACQUIRE))
16 return QuotaStatus::Busy;
17 if (!m_State->allocationValid || !LITTLE_TO_HOST16(m_pInode->i_links_count))
18 return QuotaStatus::Invalid;
19 if (!m_nSize || m_nSize % QuotaOld::RecordSize)
20 return QuotaStatus::Invalid;
21 auto status = m_pExt2Fs->prepareQuotaInodeLocked(m_InodeNumber);
22 if (status != QuotaStatus::Success)
23 return status;
24 status = m_pExt2Fs->m_Quota.exempt(m_InodeNumber, true);
25 if (status != QuotaStatus::Success)
26 return status;
27 __atomic_store_n(&m_State->quotaFile, true, __ATOMIC_RELEASE);
28 m_OwnsQuotaProtection = true;
29 }
30
31 // The shared write lock stabilizes this snapshot. The active flag rejects
32 // aliases, future mapping loans and namespace changes until quota-off.
33 uint8_t bytes[QuotaOld::RecordSize];
34 for (uint64_t offset = 0; offset < getSize(); offset += sizeof(bytes)) {
35 syscallError(0);
36 if (read(offset, sizeof(bytes), reinterpret_cast<uintptr_t>(bytes)) != sizeof(bytes))
37 return m_pExt2Fs->quotaIoStatus();
38 if (!offset && QuotaOld::looksLikeNewFormat(bytes, sizeof(bytes)))
39 return QuotaStatus::Invalid;
40 QuotaRecord record;
41 auto status = QuotaOld::decode(bytes, sizeof(bytes), record);
42 if (status != QuotaStatus::Success)
43 return status;
44 if (record.blockSoftLimit || record.inodeSoftLimit ||
45 (offset && (record.blockTime || record.inodeTime)))
46 return QuotaStatus::Unsupported;
47 if (offset && !record.blockHardLimit && !record.inodeHardLimit)
48 continue;
49 auto* entry = loaded.prepare(static_cast<uint32_t>(offset / sizeof(bytes)));
50 if (!entry)
51 return QuotaStatus::NoMemory;
52 entry->record = record;
53 }
54 return QuotaStatus::Success;
55}
56
57QuotaStatus Ext2File::endQuota(QuotaType type, bool requireClean) {
58 auto writes = lockWrites();
59 LockGuard<Mutex> data(m_State->dataLock);
60 LockGuard<Mutex> inode(m_State->writebackLock);
61 LockGuard<Mutex> allocation(m_pExt2Fs->m_WriteLock);
62 if (!m_OwnsQuotaProtection)
63 return QuotaStatus::Success;
64 if (requireClean) {
65 for (const auto& entry : m_pExt2Fs->m_Quota.table(type).entries()) {
66 if (entry.dirty)
67 return QuotaStatus::Busy;
68 }
69 }
70 const auto status = m_pExt2Fs->m_Quota.exempt(m_InodeNumber, false);
71 if (status != QuotaStatus::Success)
72 return status;
73 m_pExt2Fs->m_Quota.disable(type);
74 __atomic_store_n(&m_State->quotaFile, false, __ATOMIC_RELEASE);
75 m_OwnsQuotaProtection = false;
76 return QuotaStatus::Success;
77}
78
79QuotaStatus Ext2File::writeQuotaRecord(uint32_t id, const QuotaRecord& record) {
80 uint8_t bytes[QuotaOld::RecordSize];
81 const auto status = QuotaOld::encode(record, bytes, sizeof(bytes));
82 if (status != QuotaStatus::Success)
83 return status;
84 const uint64_t offset = static_cast<uint64_t>(id) * sizeof(bytes);
85 if (offset > maximumFileSize() || sizeof(bytes) > maximumFileSize() - offset) {
86 return QuotaStatus::TooLarge;
87 }
88 auto writes = lockWrites();
89 // Only prepareWrite examines this flag, under this same shared write lock.
90 // Other mutation entrypoints reject an active quota file unconditionally.
91 m_State->quotaInternalWrite = true;
92 syscallError(0);
93 const uint64_t written = writes.write(offset, sizeof(bytes), reinterpret_cast<uintptr_t>(bytes));
94 m_State->quotaInternalWrite = false;
95 return written == sizeof(bytes) ? QuotaStatus::Success : m_pExt2Fs->quotaIoStatus();
96}
virtual uint64_t maximumFileSize() const override
Definition Ext2File.cc:173
WriteGuard lockWrites()
Definition File.cc:393
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:230