The Pedigree Project 0.1
Ext2Handles.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/utilities/utility.h"
4
5#include "Ext2File.h"
6#include "Ext2Filesystem.h"
7#include "ext2.h"
8#include "modules/system/vfs/VFS.h"
9
10namespace {
11constexpr int32_t HandleType = 0x50444731;
12constexpr uint32_t HandleLength = 24;
13
14bool hasUuid(const Superblock* superblock) {
15 uint8_t value = 0;
16 for (size_t i = 0; i < 16; ++i) {
17 value |= static_cast<uint8_t>(superblock->s_uuid[i]);
18 }
19 return value != 0;
20}
21
22uint32_t load32(const uint8_t* bytes) {
23 uint32_t value;
24 MemoryCopy(&value, bytes, sizeof(value));
25 return LITTLE_TO_HOST32(value);
26}
27
28void store32(uint8_t* bytes, uint32_t value) {
29 value = HOST_TO_LITTLE32(value);
30 MemoryCopy(bytes, &value, sizeof(value));
31}
32} // namespace
33
34FileHandleStatus Ext2Filesystem::fileHandleFsid(FileSystemId& result) {
35 result = {};
36 if (!m_pSuperblock || !hasUuid(m_pSuperblock)) {
37 return FileHandleStatus::Unsupported;
38 }
39 const auto* uuid = reinterpret_cast<const uint8_t*>(m_pSuperblock->s_uuid);
40 result.words[0] = load32(uuid) ^ load32(uuid + 8);
41 result.words[1] = load32(uuid + 4) ^ load32(uuid + 12);
42 if (!result.words[0] && !result.words[1]) {
43 result.words[1] = 1;
44 }
45 return FileHandleStatus::Success;
46}
47
48FileHandleStatus Ext2Filesystem::validateHandleInodeLocked(uint32_t number, uint32_t generation,
49 Inode*& inode) {
50 inode = nullptr;
51 const uint32_t perGroup = LITTLE_TO_HOST32(m_pSuperblock->s_inodes_per_group);
52 if (!number || number > LITTLE_TO_HOST32(m_pSuperblock->s_inodes_count) || !perGroup ||
53 (number - 1) / perGroup >= m_nGroupDescriptors) {
54 return FileHandleStatus::Stale;
55 }
56 const uint32_t group = (number - 1) / perGroup, index = (number - 1) % perGroup;
57 if (!ensureFreeInodeBitmapLoaded(group)) {
58 return FileHandleStatus::IoError;
59 }
60 const size_t field = (index / 8) / m_BlockSize;
61 const auto* byte =
62 reinterpret_cast<const uint8_t*>(m_pInodeBitmaps[group][field] + (index / 8) % m_BlockSize);
63 if (!(*byte & (1U << (index % 8)))) {
64 return FileHandleStatus::Stale;
65 }
66 inode = getInode(number);
67 if (!inode) {
68 return FileHandleStatus::IoError;
69 }
70 // The remaining fields can be initialized outside the allocation lock only
71 // while the inode has no links and cannot be decoded.
72 if (!LITTLE_TO_HOST16(inode->i_links_count) ||
73 LITTLE_TO_HOST32(inode->i_generation) != generation ||
74 (LITTLE_TO_HOST16(inode->i_mode) & 0xf000) != EXT2_S_IFREG) {
75 return FileHandleStatus::Stale;
76 }
77 return FileHandleStatus::Success;
78}
79
80FileHandleStatus Ext2Filesystem::encodeFileHandle(File& file, FileHandle& result) {
81 result = {};
82 if (file.getFilesystem() != this || !file.supportsRegularFileOperations() || !m_pSuperblock ||
83 !hasUuid(m_pSuperblock)) {
84 return FileHandleStatus::Unsupported;
85 }
86 const uintptr_t number = file.getInode();
87 if (!number || number > LITTLE_TO_HOST32(m_pSuperblock->s_inodes_count)) {
88 return FileHandleStatus::Stale;
89 }
90 LockGuard<Mutex> registry(m_InodeStateLock);
91 Ext2InodeState* state = m_InodeStates.lookup(number);
92 if (!state || state->orphan) {
93 return FileHandleStatus::Stale;
94 }
95 LockGuard<Mutex> metadata(state->writebackLock);
96#if THREADS || defined(STANDALONE_MUTEXES)
97 LockGuard<Mutex> allocation(m_WriteLock);
98#endif
99 Inode* inode = getInode(number);
100 if (!inode) {
101 return FileHandleStatus::IoError;
102 }
103 const uint32_t generation = LITTLE_TO_HOST32(inode->i_generation);
104 const auto status = validateHandleInodeLocked(number, generation, inode);
105 if (status != FileHandleStatus::Success) {
106 return status;
107 }
108 result.length = HandleLength;
109 result.type = HandleType;
110 MemoryCopy(result.bytes, m_pSuperblock->s_uuid, 16);
111 store32(result.bytes + 16, number);
112 store32(result.bytes + 20, generation);
113 return FileHandleStatus::Success;
114}
115
116FileHandleStatus Ext2Filesystem::decodeFileHandle(const FileHandle& handle, RetainedFile& result) {
117 result.reset();
118 if (!m_pSuperblock || !hasUuid(m_pSuperblock)) {
119 return FileHandleStatus::Unsupported;
120 }
121 if (handle.type != HandleType || handle.length != HandleLength ||
122 MemoryCompare(handle.bytes, m_pSuperblock->s_uuid, 16)) {
123 return FileHandleStatus::Stale;
124 }
125 const uint32_t number = load32(handle.bytes + 16), generation = load32(handle.bytes + 20);
126 Inode* inode = nullptr;
127 Ext2InodeState* admitted = nullptr;
128 {
129 LockGuard<Mutex> registry(m_InodeStateLock);
130 Ext2InodeState* state = m_InodeStates.lookup(number);
131 LockGuard<Mutex> metadata(state ? state->writebackLock : m_InodeStateLock, state != nullptr);
132#if THREADS || defined(STANDALONE_MUTEXES)
133 LockGuard<Mutex> allocation(m_WriteLock);
134#endif
135 const auto status = validateHandleInodeLocked(number, generation, inode);
136 if (status != FileHandleStatus::Success) {
137 return status;
138 }
139 if (state && state->orphan) {
140 return FileHandleStatus::Stale;
141 }
142 admitted = acquireInodeStateLocked(number, inode);
143 if (!admitted) {
144 return FileHandleStatus::NoMemory;
145 }
146 }
147
148 // This temporary node owns the admission reference, including orphan block
149 // retirement on a failed allocation racing the final unlink.
150 Ext2Node admission(number, inode, this, *admitted);
151 if (!admitted->allocationValid) {
152 return FileHandleStatus::IoError;
153 }
154 Ext2File* file = new Ext2File(String(), number, inode, this);
155 if (!file || !file->valid() || !VFS::instance().tryTrackFile(file)) {
156 delete file;
157 return FileHandleStatus::NoMemory;
158 }
159 result.adopt(file);
160 return FileHandleStatus::Success;
161}
size_t m_nGroupDescriptors
Vector< size_t > * m_pInodeBitmaps
virtual FileHandleStatus encodeFileHandle(File &, FileHandle &)
Superblock * m_pSuperblock
Definition File.h:75
bool supportsRegularFileOperations()
Definition File.cc:824
E lookup(const K &key) const
Definition Tree.h:193
bool tryTrackFile(File *pFile)
Definition VFS.cc:1632
static VFS & instance()
Definition VFS.cc:311
Definition ext2.h:152