The Pedigree Project 0.1
MemoryExtendedAttributes.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "MemoryExtendedAttributes.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/utilities/assert.h"
6#include "pedigree/kernel/utilities/new"
7#include "pedigree/kernel/utilities/utility.h"
8
9namespace {
11
12XattrStatus validateName(const StringView& name) {
13 if (!name.length() || name.length() > Xattr::MaximumNameLength)
14 return XattrStatus::Range;
15 if (!name.str())
16 return XattrStatus::Invalid;
17 for (size_t i = 0; i < name.length(); ++i)
18 if (!name[i])
19 return XattrStatus::Invalid;
20 if (name.length() < 5 || !name.substring(0, 5).compare("user.", 5))
21 return XattrStatus::Unsupported;
22 return name.length() == 5 ? XattrStatus::Invalid : XattrStatus::Success;
23}
24} // namespace
25
27 Entry* next;
28 size_t nameLength, valueLength, bytes;
29 char* name() {
30 return reinterpret_cast<char*>(this + 1);
31 }
32 void* value() {
33 return name() + nameLength + 1;
34 }
35 static Entry* create(const StringView& name, const void* value, size_t length) {
36 const size_t bytes = sizeof(Entry) + name.length() + 1 + length;
37 auto* storage = new uint8_t[bytes];
38 if (!storage)
39 return nullptr;
40 auto* entry = new (storage) Entry{nullptr, name.length(), length, bytes};
41 MemoryCopy(entry->name(), name.str(), name.length());
42 entry->name()[name.length()] = 0;
43 if (length)
44 MemoryCopy(entry->value(), value, length);
45 return entry;
46 }
47 static void destroy(Entry* entry) {
48 if (!entry)
49 return;
50 entry->~Entry();
51 delete[] reinterpret_cast<uint8_t*>(entry);
52 }
53};
54
56 Entry* entry;
58 Entry::destroy(entry);
59 }
60};
61
62size_t MemoryExtendedAttributes::Quota::used() const {
63 return __atomic_load_n(&m_Used, __ATOMIC_ACQUIRE);
64}
65
66bool MemoryExtendedAttributes::Quota::increase(size_t amount) {
67 size_t previous = used();
68 do {
69 if (previous > m_Limit || amount > m_Limit - previous)
70 return false;
71 } while (!__atomic_compare_exchange_n(&m_Used, &previous, previous + amount, false,
72 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE));
73 return true;
74}
75
76void MemoryExtendedAttributes::Quota::decrease(size_t amount) {
77 const size_t previous = __atomic_fetch_sub(&m_Used, amount, __ATOMIC_ACQ_REL);
78 assert(previous >= amount);
79}
80
81MemoryExtendedAttributes::Quota& MemoryExtendedAttributes::globalQuota() {
82 return sharedQuota;
83}
84
85MemoryExtendedAttributes::MemoryExtendedAttributes(Quota& quota) : m_Lock(), m_Quota(quota) {}
86
87MemoryExtendedAttributes::~MemoryExtendedAttributes() {
88 // The inode's final owner has already excluded calls into this store.
89 while (m_Entries) {
90 Entry* next = m_Entries->next;
91 Entry::destroy(m_Entries);
92 m_Entries = next;
93 }
94 m_Quota.decrease(m_Bytes);
95}
96
97MemoryExtendedAttributes::Entry** MemoryExtendedAttributes::find(const StringView& name) {
98 Entry** slot = &m_Entries;
99 while (*slot && !name.compare((*slot)->name(), (*slot)->nameLength))
100 slot = &(*slot)->next;
101 return slot;
102}
103
104XattrStatus MemoryExtendedAttributes::get(const StringView& name, void* buffer, size_t capacity,
105 size_t& required) {
106 TerminationDeferral lifetime;
107 required = 0;
108 const auto valid = validateName(name);
109 if (valid != XattrStatus::Success)
110 return valid;
111 LockGuard<Mutex> guard(m_Lock);
112 Entry* entry = *find(name);
113 if (!entry)
114 return XattrStatus::Missing;
115 required = entry->valueLength;
116 if (!capacity)
117 return XattrStatus::Success;
118 if (capacity < required)
119 return XattrStatus::Range;
120 if (required) {
121 if (!buffer)
122 return XattrStatus::Invalid;
123 MemoryCopy(buffer, entry->value(), required);
124 }
125 return XattrStatus::Success;
126}
127
128XattrStatus MemoryExtendedAttributes::list(void* buffer, size_t capacity, size_t& required) {
129 TerminationDeferral lifetime;
130 LockGuard<Mutex> guard(m_Lock);
131 required = 0;
132 for (Entry* entry = m_Entries; entry; entry = entry->next)
133 required += entry->nameLength + 1;
134 if (!capacity)
135 return XattrStatus::Success;
136 if (capacity < required)
137 return XattrStatus::Range;
138 if (required && !buffer)
139 return XattrStatus::Invalid;
140 auto* output = static_cast<char*>(buffer);
141 for (Entry* entry = m_Entries; entry; entry = entry->next) {
142 MemoryCopy(output, entry->name(), entry->nameLength + 1);
143 output += entry->nameLength + 1;
144 }
145 return XattrStatus::Success;
146}
147
148XattrStatus MemoryExtendedAttributes::set(const StringView& name, const void* value, size_t length,
149 unsigned flags) {
150 TerminationDeferral lifetime;
151 if ((flags & ~(Xattr::Create | Xattr::Replace)) || (length && !value))
152 return XattrStatus::Invalid;
153 const auto valid = validateName(name);
154 if (valid != XattrStatus::Success)
155 return valid;
156 if (length > Xattr::MaximumValueLength)
157 return XattrStatus::Range;
158 PreparedEntry prepared{Entry::create(name, value, length)};
159 if (!prepared.entry)
160 return XattrStatus::NoMemory;
161 Entry* retired = nullptr;
162 {
163 LockGuard<Mutex> guard(m_Lock);
164 Entry** slot = find(name);
165 retired = *slot;
166 if (retired && (flags & Xattr::Create))
167 return XattrStatus::Exists;
168 if (!retired && (flags & Xattr::Replace))
169 return XattrStatus::Missing;
170 const size_t oldBytes = retired ? retired->bytes : 0;
171 const size_t newBytes = prepared.entry->bytes;
172 const size_t retainedBytes = m_Bytes - oldBytes;
173 if ((!retired && m_Count == MaximumEntries) || newBytes > MaximumStoreBytes - retainedBytes)
174 return XattrStatus::NoSpace;
175 if (newBytes > oldBytes && !m_Quota.increase(newBytes - oldBytes))
176 return XattrStatus::NoSpace;
177 prepared.entry->next = retired ? retired->next : nullptr;
178 *slot = prepared.entry;
179 prepared.entry = nullptr;
180 m_Bytes = retainedBytes + newBytes;
181 if (!retired)
182 ++m_Count;
183 if (oldBytes > newBytes)
184 m_Quota.decrease(oldBytes - newBytes);
185 }
186 Entry::destroy(retired);
187 return XattrStatus::Success;
188}
189
190XattrStatus MemoryExtendedAttributes::remove(const StringView& name) {
191 TerminationDeferral lifetime;
192 const auto valid = validateName(name);
193 if (valid != XattrStatus::Success)
194 return valid;
195 Entry* retired;
196 {
197 LockGuard<Mutex> guard(m_Lock);
198 Entry** slot = find(name);
199 retired = *slot;
200 if (!retired)
201 return XattrStatus::Missing;
202 *slot = retired->next;
203 --m_Count;
204 m_Bytes -= retired->bytes;
205 m_Quota.decrease(retired->bytes);
206 }
207 Entry::destroy(retired);
208 return XattrStatus::Success;
209}
#define assert(x)
Definition assert.h:39