2#include "pedigree/kernel/utilities/assert.h"
6Ext2QuotaLedger::~Ext2QuotaLedger() {
7 for (
auto it = m_Inodes.begin(); it != m_Inodes.end(); ++it)
12 return m_Inodes.lookup(inode);
15QuotaStatus Ext2QuotaLedger::prepareCharge(uint32_t uid, uint32_t gid, uint64_t bytes,
16 uint64_t inodes,
bool enforce) {
17 const uint32_t ids[] = {uid, gid};
18 for (
size_t type = 0; type < 2; ++type) {
21 if (ids[type] > m_MaximumId[type])
22 return QuotaStatus::Overflow;
23 auto* entry = m_Tables[type].prepare(ids[type]);
25 return QuotaStatus::NoMemory;
26 const auto status = QuotaTable::canCharge(*entry, bytes, inodes, enforce);
27 if (status != QuotaStatus::Success)
30 return QuotaStatus::Success;
33void Ext2QuotaLedger::charge(uint32_t uid, uint32_t gid, uint64_t bytes, uint64_t inodes) {
34 const uint32_t ids[] = {uid, gid};
35 for (
size_t type = 0; type < 2; ++type) {
37 QuotaTable::charge(*m_Tables[type].find(ids[type]), bytes, inodes);
41void Ext2QuotaLedger::refund(uint32_t uid, uint32_t gid, uint64_t bytes, uint64_t inodes) {
42 const uint32_t ids[] = {uid, gid};
43 for (
size_t type = 0; type < 2; ++type) {
45 QuotaTable::refund(*m_Tables[type].find(ids[type]), bytes, inodes);
49QuotaStatus Ext2QuotaLedger::track(uint32_t inode, uint32_t uid, uint32_t gid, uint64_t bytes) {
51 return QuotaStatus::Success;
52 const auto status = prepareCharge(uid, gid, bytes, 1,
false);
53 if (status != QuotaStatus::Success)
55 auto* entry =
new InodeCharge;
57 return QuotaStatus::NoMemory;
61 if (!m_Inodes.tryInsert(inode, entry)) {
63 return QuotaStatus::NoMemory;
65 charge(uid, gid, bytes, 1);
66 return QuotaStatus::Success;
69QuotaStatus Ext2QuotaLedger::create(uint32_t inode, uint32_t uid, uint32_t gid) {
71 const auto status = prepareCharge(uid, gid, 0, 1,
true);
72 return status == QuotaStatus::Success ? track(inode, uid, gid, 0) : status;
75QuotaStatus Ext2QuotaLedger::reserve(uint32_t inode, uint64_t bytes) {
76 auto* entry = find(inode);
78 if (bytes > ~uint64_t(0) - entry->bytes)
79 return QuotaStatus::Overflow;
81 const auto status = prepareCharge(entry->uid, entry->gid, bytes, 0,
true);
82 if (status != QuotaStatus::Success)
84 charge(entry->uid, entry->gid, bytes, 0);
86 entry->bytes += bytes;
87 return QuotaStatus::Success;
90void Ext2QuotaLedger::refund(uint32_t inode, uint64_t bytes) {
91 auto* entry = find(inode);
96 assert(bytes <= entry->bytes);
98 refund(entry->uid, entry->gid, bytes, 0);
99 entry->bytes -= bytes;
102void Ext2QuotaLedger::forget(uint32_t inode) {
103 auto* entry = find(inode);
107 refund(entry->uid, entry->gid, entry->bytes, 1);
108 m_Inodes.remove(inode);
112QuotaStatus Ext2QuotaLedger::transfer(uint32_t inode, uint32_t uid, uint32_t gid) {
113 auto* entry = find(inode);
115 const uint32_t oldIds[] = {entry->uid, entry->gid}, newIds[] = {uid, gid};
116 if (!entry->exempt) {
117 for (
size_t type = 0; type < 2; ++type) {
118 if (!m_Enabled[type] || oldIds[type] == newIds[type])
120 if (newIds[type] > m_MaximumId[type])
121 return QuotaStatus::Overflow;
122 auto* destination = m_Tables[type].prepare(newIds[type]);
124 return QuotaStatus::NoMemory;
125 const auto status = QuotaTable::canCharge(*destination, entry->bytes, 1,
true);
126 if (status != QuotaStatus::Success)
129 for (
size_t type = 0; type < 2; ++type) {
130 if (!m_Enabled[type] || oldIds[type] == newIds[type])
132 QuotaTable::refund(*m_Tables[type].find(oldIds[type]), entry->bytes, 1);
133 QuotaTable::charge(*m_Tables[type].find(newIds[type]), entry->bytes, 1);
138 return QuotaStatus::Success;
141QuotaStatus Ext2QuotaLedger::exempt(uint32_t inode,
bool exempt) {
142 auto* entry = find(inode);
144 if (entry->exempt == exempt)
145 return QuotaStatus::Success;
147 refund(entry->uid, entry->gid, entry->bytes, 1);
151 charge(entry->uid, entry->gid, entry->bytes, 1);
153 entry->exempt = exempt;
154 return QuotaStatus::Success;
157QuotaStatus Ext2QuotaLedger::enable(QuotaType type,
QuotaTable& loaded, uint32_t maximumId) {
158 const size_t index =
static_cast<size_t>(type);
159 if (m_Enabled[index])
160 return QuotaStatus::Busy;
161 for (
auto& entry : loaded.entries()) {
162 entry.record.currentSpace = 0;
163 entry.record.currentInodes = 0;
166 for (
auto it = m_Inodes.begin(); it != m_Inodes.end(); ++it) {
167 const auto&
usage = *it.value();
168 if ((index ?
usage.gid :
usage.uid) > maximumId)
169 return QuotaStatus::Overflow;
170 auto* destination = loaded.prepare(index ?
usage.gid :
usage.uid);
172 return QuotaStatus::NoMemory;
175 const auto status = QuotaTable::canCharge(*destination,
usage.bytes, 1,
false);
176 if (status != QuotaStatus::Success)
178 QuotaTable::charge(*destination,
usage.bytes, 1);
180 m_Tables[index].swap(loaded);
181 m_MaximumId[index] = maximumId;
182 m_Enabled[index] =
true;
183 return QuotaStatus::Success;
186void Ext2QuotaLedger::disable(QuotaType type) {
187 const size_t index =
static_cast<size_t>(type);
188 m_Enabled[index] =
false;
189 m_Tables[index].clear();
192bool Ext2QuotaLedger::enabled(QuotaType type)
const {
193 return m_Enabled[
static_cast<size_t>(type)];
196QuotaTable& Ext2QuotaLedger::table(QuotaType type) {
197 return m_Tables[
static_cast<size_t>(type)];