The Pedigree Project 0.1
QuotaTable.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/utilities/assert.h"
3#include "pedigree/kernel/utilities/utility.h"
4
5#include "QuotaTable.h"
6
7size_t QuotaTable::position(uint32_t id) const {
8 size_t first = 0, last = m_Entries.count();
9 while (first < last) {
10 const size_t middle = first + (last - first) / 2;
11 if (m_Entries[middle].id < id)
12 first = middle + 1;
13 else
14 last = middle;
15 }
16 return first;
17}
18
19QuotaTable::Entry* QuotaTable::find(uint32_t id) {
20 const size_t index = position(id);
21 return index < m_Entries.count() && m_Entries[index].id == id ? &m_Entries[index] : nullptr;
22}
23
24const QuotaTable::Entry* QuotaTable::find(uint32_t id) const {
25 const size_t index = position(id);
26 return index < m_Entries.count() && m_Entries[index].id == id ? &m_Entries[index] : nullptr;
27}
28
29QuotaTable::Entry* QuotaTable::prepare(uint32_t id) {
30 const size_t index = position(id);
31 if (index < m_Entries.count() && m_Entries[index].id == id)
32 return &m_Entries[index];
33 if (!m_Entries.tryReserve(m_Entries.count() + 1))
34 return nullptr;
35 Entry entry;
36 entry.id = id;
37 entry.record.valid = Quota::Supported;
38 m_Entries.insert(index, entry);
39 return &m_Entries[index];
40}
41
42QuotaStatus QuotaTable::set(uint32_t id, const QuotaRecord& requested) {
43 if (requested.valid & ~Quota::Limits)
44 return QuotaStatus::Unsupported;
45 if (((requested.valid & Quota::BlockLimits) && requested.blockSoftLimit) ||
46 ((requested.valid & Quota::InodeLimits) && requested.inodeSoftLimit))
47 return QuotaStatus::Unsupported;
48 if (((requested.valid & Quota::BlockLimits) && requested.blockHardLimit > 0xffffffffULL) ||
49 ((requested.valid & Quota::InodeLimits) && requested.inodeHardLimit > 0xffffffffULL))
50 return QuotaStatus::Overflow;
51 Entry* entry = prepare(id);
52 if (!entry)
53 return QuotaStatus::NoMemory;
54 if (requested.valid & Quota::BlockLimits)
55 entry->record.blockHardLimit = requested.blockHardLimit;
56 if (requested.valid & Quota::InodeLimits)
57 entry->record.inodeHardLimit = requested.inodeHardLimit;
58 entry->dirty = true;
59 return QuotaStatus::Success;
60}
61
62QuotaStatus QuotaTable::canCharge(const Entry& entry, uint64_t bytes, uint64_t inodes,
63 bool enforce) {
64 const auto& record = entry.record;
65 constexpr uint64_t MaximumSpace = 0xffffffffULL * 1024;
66 if (record.currentSpace > MaximumSpace || bytes > MaximumSpace - record.currentSpace ||
67 record.currentInodes > 0xffffffffULL || inodes > 0xffffffffULL - record.currentInodes)
68 return QuotaStatus::Overflow;
69 if (enforce &&
70 ((bytes && record.blockHardLimit &&
71 record.currentSpace + bytes > record.blockHardLimit * 1024) ||
72 (inodes && record.inodeHardLimit && record.currentInodes + inodes > record.inodeHardLimit)))
73 return QuotaStatus::Limit;
74 return QuotaStatus::Success;
75}
76
77void QuotaTable::charge(Entry& entry, uint64_t bytes, uint64_t inodes) {
78 entry.record.currentSpace += bytes;
79 entry.record.currentInodes += inodes;
80 entry.dirty = true;
81}
82
83void QuotaTable::refund(Entry& entry, uint64_t bytes, uint64_t inodes) {
84 assert(bytes <= entry.record.currentSpace && inodes <= entry.record.currentInodes);
85 entry.record.currentSpace -= bytes;
86 entry.record.currentInodes -= inodes;
87 entry.dirty = true;
88}
89
90bool QuotaTable::sameRecord(const QuotaRecord& a, const QuotaRecord& b) {
91 return a.blockHardLimit == b.blockHardLimit && a.blockSoftLimit == b.blockSoftLimit &&
92 a.currentSpace == b.currentSpace && a.inodeHardLimit == b.inodeHardLimit &&
93 a.inodeSoftLimit == b.inodeSoftLimit && a.currentInodes == b.currentInodes &&
94 a.blockTime == b.blockTime && a.inodeTime == b.inodeTime && a.valid == b.valid;
95}
96
97void QuotaTable::clear() {
98 m_Entries.clear();
99}
100void QuotaTable::swap(QuotaTable& other) {
101 m_Entries.swap(other.m_Entries);
102}
103
104QuotaStatus QuotaOld::decode(const void* bytes, size_t length, QuotaRecord& record) {
105 if (!bytes || length != RecordSize)
106 return QuotaStatus::Invalid;
107 uint32_t words[8];
108 MemoryCopy(words, bytes, RecordSize);
109 record = QuotaRecord();
110 record.blockHardLimit = words[0];
111 record.blockSoftLimit = words[1];
112 record.currentSpace = static_cast<uint64_t>(words[2]) * 1024;
113 record.inodeHardLimit = words[3];
114 record.inodeSoftLimit = words[4];
115 record.currentInodes = words[5];
116 record.blockTime = words[6];
117 record.inodeTime = words[7];
118 record.valid = Quota::Supported;
119 return QuotaStatus::Success;
120}
121
122QuotaStatus QuotaOld::encode(const QuotaRecord& record, void* bytes, size_t length) {
123 if (!bytes || length != RecordSize)
124 return QuotaStatus::Invalid;
125 if (record.blockHardLimit > 0xffffffffULL || record.blockSoftLimit > 0xffffffffULL ||
126 record.currentSpace > 0xffffffffULL * 1024 || record.inodeHardLimit > 0xffffffffULL ||
127 record.inodeSoftLimit > 0xffffffffULL || record.currentInodes > 0xffffffffULL ||
128 record.blockTime > 0xffffffffULL || record.inodeTime > 0xffffffffULL)
129 return QuotaStatus::Overflow;
130 const uint32_t words[8] = {static_cast<uint32_t>(record.blockHardLimit),
131 static_cast<uint32_t>(record.blockSoftLimit),
132 static_cast<uint32_t>((record.currentSpace + 1023) / 1024),
133 static_cast<uint32_t>(record.inodeHardLimit),
134 static_cast<uint32_t>(record.inodeSoftLimit),
135 static_cast<uint32_t>(record.currentInodes),
136 static_cast<uint32_t>(record.blockTime),
137 static_cast<uint32_t>(record.inodeTime)};
138 MemoryCopy(bytes, words, RecordSize);
139 return QuotaStatus::Success;
140}
141
142bool QuotaOld::looksLikeNewFormat(const void* bytes, size_t length) {
143 if (!bytes || length < sizeof(uint32_t))
144 return false;
145 uint32_t magic;
146 MemoryCopy(&magic, bytes, sizeof(magic));
147 magic = LITTLE_TO_HOST32(magic);
148 return magic == 0xd9c01f11U || magic == 0xd9c01927U;
149}
#define assert(x)
Definition assert.h:39