3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/panic.h"
5#include "pedigree/kernel/utilities/utility.h"
7#include "Ext2Filesystem.h"
13size_t align4(
size_t size) {
14 return (size + 3) & ~size_t(3);
16const Entry* entryAt(
const void* block,
size_t offset) {
17 return reinterpret_cast<const Entry*
>(
static_cast<const uint8_t*
>(block) + offset);
19int compare(
unsigned index,
const char* name,
size_t length,
const Entry& entry) {
20 if (index != entry.nameIndex)
21 return index < entry.nameIndex ? -1 : 1;
22 if (length != entry.nameLength)
23 return length < entry.nameLength ? -1 : 1;
24 const auto* other =
reinterpret_cast<const uint8_t*
>(&entry + 1);
25 for (
size_t n = 0; n < length; ++n) {
26 const auto character =
static_cast<uint8_t
>(name[n]);
27 if (character != other[n])
28 return character < other[n] ? -1 : 1;
32bool last(
const Entry* entry) {
34 MemoryCopy(&end, entry,
sizeof(end));
38 if (!name.length() || name.length() > Xattr::MaximumNameLength)
39 return XattrStatus::Range;
40 for (
size_t n = 0; n < name.length(); ++n) {
42 return XattrStatus::Invalid;
44 if (name.length() < 5 || !name.substring(0, 5).compare(
"user.", 5))
45 return XattrStatus::Unsupported;
46 return name.length() == 5 ? XattrStatus::Invalid : XattrStatus::Success;
48uint32_t entryHash(
const Entry& entry,
const uint8_t* value) {
50 const auto* name =
reinterpret_cast<const uint8_t*
>(&entry + 1);
51 for (
size_t n = 0; n < entry.nameLength; ++n)
52 hash = (hash << 5) ^ (hash >> 27) ^ name[n];
53 for (
size_t n = 0; n < align4(LITTLE_TO_HOST32(entry.valueLength)); n += 4) {
55 MemoryCopy(&word, value + n,
sizeof(word));
56 hash = (hash << 16) ^ (hash >> 16) ^ LITTLE_TO_HOST32(word);
62XattrStatus validate(
const void* block,
size_t size) {
64 return XattrStatus::Success;
65 if (size <
sizeof(Header) + 4 || size > 4096)
66 return XattrStatus::IoError;
67 const auto* header =
static_cast<const Header*
>(block);
68 if (LITTLE_TO_HOST32(header->magic) != Magic || LITTLE_TO_HOST32(header->blocks) != 1 ||
69 !LITTLE_TO_HOST32(header->references) || LITTLE_TO_HOST32(header->references) > 1024)
70 return XattrStatus::IoError;
71 for (uint32_t reserved : header->reserved) {
73 return XattrStatus::Unsupported;
75 size_t cursor =
sizeof(Header), valuesBegin = size;
76 const Entry* previous =
nullptr;
77 while (cursor <= size - 4) {
78 const Entry* entry = entryAt(block, cursor);
80 return cursor + 4 <= valuesBegin ? XattrStatus::Success : XattrStatus::IoError;
81 if (size - cursor <
sizeof(Entry))
82 return XattrStatus::IoError;
83 const size_t span = align4(
sizeof(Entry) + entry->nameLength);
84 if (span > size - cursor - 4)
85 return XattrStatus::IoError;
86 if (!entry->nameIndex)
87 return XattrStatus::IoError;
88 if (entry->valueBlock)
89 return XattrStatus::Unsupported;
90 if (entry->nameIndex ==
User) {
91 if (!entry->nameLength || entry->nameLength > Xattr::MaximumNameLength - 5)
92 return XattrStatus::IoError;
93 const auto* name =
reinterpret_cast<const uint8_t*
>(entry + 1);
94 for (
size_t n = 0; n < entry->nameLength; ++n) {
96 return XattrStatus::IoError;
99 if (previous && compare(previous->nameIndex,
reinterpret_cast<const char*
>(previous + 1),
100 previous->nameLength, *entry) >= 0)
101 return XattrStatus::IoError;
102 const size_t length = LITTLE_TO_HOST32(entry->valueLength);
103 const size_t offset = LITTLE_TO_HOST16(entry->valueOffset);
104 if (length > size || offset > size || length > size - offset ||
105 (length && ((offset & 3) || align4(length) > size - offset)))
106 return XattrStatus::IoError;
108 for (
size_t prior =
sizeof(Header); prior < cursor;) {
109 const auto* other = entryAt(block, prior);
110 const size_t otherLength = LITTLE_TO_HOST32(other->valueLength);
111 const size_t otherOffset = LITTLE_TO_HOST16(other->valueOffset);
112 if (otherLength && offset < otherOffset + align4(otherLength) &&
113 otherOffset < offset + align4(length))
114 return XattrStatus::IoError;
115 prior += align4(
sizeof(Entry) + other->nameLength);
117 if (offset < valuesBegin)
118 valuesBegin = offset;
123 return XattrStatus::IoError;
126XattrStatus get(
const void* block,
size_t size,
const StringView& name,
void* output,
127 size_t capacity,
size_t& required) {
129 auto status = suffix(name);
130 if (status != XattrStatus::Success)
132 status = validate(block, size);
133 if (status != XattrStatus::Success || !block)
134 return status == XattrStatus::Success ? XattrStatus::Missing : status;
135 for (
size_t cursor =
sizeof(Header); !last(entryAt(block, cursor));) {
136 const Entry* entry = entryAt(block, cursor);
137 if (!compare(
User, name.str() + 5, name.length() - 5, *entry)) {
138 required = LITTLE_TO_HOST32(entry->valueLength);
139 if (capacity && capacity < required)
140 return XattrStatus::Range;
141 if (capacity && required && !output)
142 return XattrStatus::Invalid;
143 if (capacity && required)
145 static_cast<const uint8_t*
>(block) + LITTLE_TO_HOST16(entry->valueOffset),
147 return XattrStatus::Success;
149 cursor += align4(
sizeof(Entry) + entry->nameLength);
151 return XattrStatus::Missing;
154XattrStatus list(
const void* block,
size_t size,
void* output,
size_t capacity,
size_t& required) {
156 if (capacity && !output)
157 return XattrStatus::Invalid;
158 auto status = validate(block, size);
159 if (status != XattrStatus::Success || !block)
161 for (
size_t cursor =
sizeof(Header); !last(entryAt(block, cursor));) {
162 const Entry* entry = entryAt(block, cursor);
163 if (entry->nameIndex ==
User)
164 required += 6 + entry->nameLength;
165 cursor += align4(
sizeof(Entry) + entry->nameLength);
167 if (capacity && capacity < required)
168 return XattrStatus::Range;
170 return XattrStatus::Success;
171 auto* destination =
static_cast<char*
>(output);
172 for (
size_t cursor =
sizeof(Header); !last(entryAt(block, cursor));) {
173 const Entry* entry = entryAt(block, cursor);
174 if (entry->nameIndex ==
User) {
175 MemoryCopy(destination,
"user.", 5);
176 MemoryCopy(destination + 5, entry + 1, entry->nameLength);
177 destination[5 + entry->nameLength] = 0;
178 destination += 6 + entry->nameLength;
180 cursor += align4(
sizeof(Entry) + entry->nameLength);
182 return XattrStatus::Success;
185XattrStatus rebuild(
const void* oldBlock,
size_t size,
const StringView& name,
const void* value,
186 size_t length,
unsigned flags,
bool remove,
void* replacement,
bool& empty) {
187 auto status = suffix(name);
188 if (status != XattrStatus::Success)
190 if (flags & ~(Xattr::Create | Xattr::Replace))
191 return XattrStatus::Invalid;
193 return XattrStatus::Range;
194 if (!replacement || (length && !value))
195 return XattrStatus::Invalid;
196 status = validate(oldBlock, size);
197 if (status != XattrStatus::Success)
201 for (
size_t cursor =
sizeof(Header); !last(entryAt(oldBlock, cursor));) {
202 const auto* entry = entryAt(oldBlock, cursor);
203 exists |= !compare(
User, name.str() + 5, name.length() - 5, *entry);
204 cursor += align4(
sizeof(Entry) + entry->nameLength);
207 if (exists && (flags & Xattr::Create))
208 return XattrStatus::Exists;
209 if (!exists && (remove || (flags & Xattr::Replace)))
210 return XattrStatus::Missing;
211 ByteSet(replacement, 0, size);
212 auto* header =
static_cast<Header*
>(replacement);
213 header->magic = HOST_TO_LITTLE32(Magic);
214 header->blocks = header->references = HOST_TO_LITTLE32(1);
215 size_t cursor =
sizeof(Header), valueEnd = size;
217 bool unshareable =
false;
218 auto emit = [&](
unsigned index,
const char* entryName,
size_t nameLength,
const void* bytes,
219 size_t valueLength) {
220 const size_t entrySize = align4(
sizeof(Entry) + nameLength), valueSize = align4(valueLength);
221 if (valueSize > valueEnd || cursor + entrySize + 4 > valueEnd - valueSize)
223 valueEnd -= valueSize;
224 auto* entry =
reinterpret_cast<Entry*
>(
static_cast<uint8_t*
>(replacement) + cursor);
225 entry->nameIndex = index;
226 entry->nameLength = nameLength;
227 entry->valueOffset = HOST_TO_LITTLE16(valueLength ? valueEnd : 0);
228 entry->valueLength = HOST_TO_LITTLE32(valueLength);
229 MemoryCopy(entry + 1, entryName, nameLength);
230 auto* destination =
static_cast<uint8_t*
>(replacement) + valueEnd;
232 MemoryCopy(destination, bytes, valueLength);
233 const uint32_t entryValueHash = entryHash(*entry, destination);
234 entry->hash = HOST_TO_LITTLE32(entryValueHash);
235 unshareable |= !entryValueHash;
236 hash = (hash << 16) ^ (hash >> 16) ^ entryValueHash;
240 bool inserted = remove;
242 for (
size_t offset =
sizeof(Header); !last(entryAt(oldBlock, offset));) {
243 const auto* entry = entryAt(oldBlock, offset);
244 const int order = compare(
User, name.str() + 5, name.length() - 5, *entry);
245 if (!inserted && order <= 0) {
246 if (!emit(
User, name.str() + 5, name.length() - 5, value, length))
247 return XattrStatus::NoSpace;
251 !emit(entry->nameIndex,
reinterpret_cast<const char*
>(entry + 1), entry->nameLength,
252 static_cast<const uint8_t*
>(oldBlock) + LITTLE_TO_HOST16(entry->valueOffset),
253 LITTLE_TO_HOST32(entry->valueLength)))
254 return XattrStatus::NoSpace;
255 offset += align4(
sizeof(Entry) + entry->nameLength);
258 if (!inserted && !emit(
User, name.str() + 5, name.length() - 5, value, length))
259 return XattrStatus::NoSpace;
260 header->hash = HOST_TO_LITTLE32(unshareable ? 0 : hash);
261 empty = cursor ==
sizeof(Header);
262 return XattrStatus::Success;
266XattrStatus Ext2Node::getXattr(
const StringView& name,
void* output,
size_t capacity,
269 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
270 return XattrStatus::IoError;
275 if (!m_State->allocationValid)
276 return XattrStatus::IoError;
277 auto status = m_pExt2Fs->attributeFormatStatus();
278 if (status != XattrStatus::Success)
281 status = m_pExt2Fs->readAttributeBlockLocked(m_pInode, block);
282 return status == XattrStatus::Success
283 ? Ext2Ea::get(
reinterpret_cast<const void*
>(block.buffer), m_pExt2Fs->
m_BlockSize,
284 name, output, capacity, required)
287XattrStatus Ext2Node::listXattrs(
void* output,
size_t capacity,
size_t& required) {
289 if (!m_pExt2Fs->tryAcquireOperation(operation)) {
290 return XattrStatus::IoError;
295 if (!m_State->allocationValid)
296 return XattrStatus::IoError;
297 auto status = m_pExt2Fs->attributeFormatStatus();
298 if (status != XattrStatus::Success)
301 status = m_pExt2Fs->readAttributeBlockLocked(m_pInode, block);
302 return status == XattrStatus::Success
303 ? Ext2Ea::list(
reinterpret_cast<const void*
>(block.buffer), m_pExt2Fs->
m_BlockSize,
304 output, capacity, required)
308bool Ext2Node::decodeAllocation(
const Inode& inode, uint32_t blockSize, uint32_t& blocks,
309 bool& inlineSymlink) {
310 if (blockSize < 512 || blockSize > 4096 || (blockSize & (blockSize - 1)))
312 const uint32_t sectors = LITTLE_TO_HOST32(inode.i_blocks);
313 const uint32_t eaSectors = inode.i_file_acl ? blockSize / 512 : 0;
314 if (sectors < eaSectors || (sectors - eaSectors) % (blockSize / 512))
316 blocks = (sectors - eaSectors) / (blockSize / 512);
317 inlineSymlink = (LITTLE_TO_HOST16(inode.i_mode) & 0xf000) == EXT2_S_IFLNK && !blocks;
318 return !inlineSymlink || LITTLE_TO_HOST32(inode.i_size) <=
sizeof(inode.i_block);
321bool Ext2Node::encodeAllocation(uint32_t data, uint32_t indirect,
bool hasEa, uint32_t blockSize,
323 const uint64_t count = (
static_cast<uint64_t
>(data) + indirect + hasEa) * (blockSize / 512);
324 if (count > 0xffffffffULL)
330bool Ext2Node::isInlineSymlink()
const {
332 bool inlineSymlink =
false;
333 return decodeAllocation(*m_pInode, m_pExt2Fs->
m_BlockSize, blocks, inlineSymlink) &&
337void Ext2Node::updateAllocatedSectorCount() {
339 const bool valid = encodeAllocation(m_State->allocatedDataBlocks, m_nMetadataBlocks,
340 m_pInode->i_file_acl != 0, m_pExt2Fs->
m_BlockSize, sectors);
342 panic(
"Ext2: allocated sector count exceeds inode capacity");
344 m_pInode->i_blocks = HOST_TO_LITTLE32(sectors);
void EXPORTED_PUBLIC panic(const char *msg) NORETURN