The Pedigree Project 0.1
MountView-access.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/FilesystemAccess.h"
3#include "pedigree/kernel/syscallError.h"
4
5#include "MountView-internal.h"
6#ifndef VFS_STANDALONE
7#include "pedigree/kernel/Subsystem.h"
8#include "pedigree/kernel/process/Process.h"
9#include "pedigree/kernel/process/Thread.h"
10#include "pedigree/kernel/processor/Processor.h"
11#include "pedigree/kernel/processor/ProcessorInformation.h"
12#endif
13
14namespace {
15#ifndef VFS_STANDALONE
16Subsystem* currentPolicy() {
17 auto* thread = Processor::information().getCurrentThread();
18 auto* process = thread ? thread->getParent() : nullptr;
19 auto* subsystem = process ? process->getSubsystem() : nullptr;
20 return subsystem && subsystem->filesystemConstrained() ? subsystem : nullptr;
21}
22#endif
23uint64_t creationAccess(File* node) {
24 if (node->isDirectory()) {
25 return FilesystemAccess::MakeDir;
26 }
27 if (node->isSymlink()) {
28 return FilesystemAccess::MakeSym;
29 }
30 if (node->isSocket()) {
31 return FilesystemAccess::MakeSock;
32 }
33 if (node->isPipe() || node->isFifo()) {
34 return FilesystemAccess::MakeFifo;
35 }
36 if (node->isBlockDevice()) {
37 return FilesystemAccess::MakeBlock;
38 }
39 if (!node->supportsRegularFileOperations()) {
40 return FilesystemAccess::MakeChar;
41 }
42 return FilesystemAccess::MakeReg;
43}
44} // namespace
45
46bool VfsMountView::State::ancestors(const FilesystemPathRef& selected,
48 FilesystemPathRef current = selected;
49 for (size_t depth = 0; depth < 4096; ++depth) {
50 auto* item = path(current);
51 if (!item || !result.tryReserve(result.count() + 1)) {
52 return false;
53 }
54 result.pushBack(current);
55 if (!item->node()->isDirectory() && item->lookupParent && item->lookupName.length()) {
56 Directory::ChildLease selectedName;
57 const auto found = Directory::fromFile(item->lookupParent->node())
58 ->lookupChild(HashedStringView(item->lookupName), selectedName);
59 // A retained open path can outlive its name. Its direct inode rules still
60 // apply, but its previous directory must not grant a new open after rename.
61 if (found != Directory::LookupStatus::Found || selectedName.get() != item->node()) {
62 return true;
63 }
64 }
66 if (item->node() == item->attachment->root) {
67 VfsAttachmentRef parentAttachment;
69 {
70 LockGuard<Mutex> guard(graph);
71 auto* row = find(item->attachment->id);
72 if (row) {
73 parentAttachment = row->parent;
74 covered = row->covered;
75 }
76 }
77 if (!parentAttachment) {
78 return true;
79 }
80 if (!covered || !makePath(parentAttachment, covered->get(), next)) {
81 return false;
82 }
83 } else if (!parent(current, next)) {
84 return false;
85 }
86 if (view.samePath(current, next)) {
87 return true;
88 }
89 current = pedigree_std::move(next);
90 }
91 return false;
92}
93
94uint64_t VfsMountView::filesystemAccess(const FilesystemPathRef& path,
95 const VFS::NamespaceMutation* writer) {
96 auto* owner = fromPath(path);
97 if (owner && owner != this) {
98 return owner->filesystemAccess(path, writer);
99 }
100#ifndef VFS_STANDALONE
101 auto* policy = currentPolicy();
102 if (!policy) {
103 return ~uint64_t(0);
104 }
105 if (!writer) {
106 VFS::NamespaceMutation admission(m_Vfs);
107 return filesystemAccess(path, &admission);
108 }
109 if (!writer->protects(m_Vfs) || !m_State || !m_State->nodePath(path)) {
110 return 0;
111 }
112 // The subsystem distinguishes internal objects from missing path provenance.
113 if (!m_State->path(path)) {
114 return policy->filesystemAccess(&path, 1);
115 }
117 if (!m_State->ancestors(path, ancestry)) {
118 return 0;
119 }
120 return policy->filesystemAccess(ancestry.begin(), ancestry.count());
121#else
122 return ~uint64_t(0);
123#endif
124}
125
126bool VfsMountView::checkFilesystemAccess(const FilesystemPathRef& path, uint64_t access,
127 const VFS::NamespaceMutation* writer) {
128 auto* owner = fromPath(path);
129 if (owner && owner != this) {
130 return owner->checkFilesystemAccess(path, access, writer);
131 }
132 constexpr uint64_t mutations =
133 FilesystemAccess::WriteFile | FilesystemAccess::RemoveDir | FilesystemAccess::RemoveFile |
134 FilesystemAccess::MakeChar | FilesystemAccess::MakeDir | FilesystemAccess::MakeReg |
135 FilesystemAccess::MakeSock | FilesystemAccess::MakeFifo | FilesystemAccess::MakeBlock |
136 FilesystemAccess::MakeSym | FilesystemAccess::Truncate;
137 if ((access & mutations) && !writable(path)) {
138 return false;
139 }
140 if ((access & FilesystemAccess::Execute) && (mountFlags(path) & NoExec)) {
141 SYSCALL_ERROR(PermissionDenied);
142 return false;
143 }
144 if ((filesystemAccess(path, writer) & access) == access) {
145 return true;
146 }
147 SYSCALL_ERROR(PermissionDenied);
148 return false;
149}
150
151bool VfsMountView::authorizeRemove(const FilesystemPathRef& parent, File* node,
152 const VFS::NamespaceMutation& writer) {
153 return checkFilesystemAccess(
154 parent, node->isDirectory() ? FilesystemAccess::RemoveDir : FilesystemAccess::RemoveFile,
155 &writer);
156}
157
158bool VfsMountView::authorizeLink(const FilesystemPathRef& parent, const FilesystemPathRef& target,
159 const VFS::NamespaceMutation& writer) {
160#ifndef VFS_STANDALONE
161 if (currentPolicy()) {
162 auto* source = m_State->path(target);
163 Directory::ChildLease selected;
164 if (!source || !source->lookupParent || !source->lookupName.length() ||
165 Directory::fromFile(source->lookupParent->node())
166 ->lookupChild(HashedStringView(source->lookupName), selected) !=
167 Directory::LookupStatus::Found ||
168 selected.get() != source->node()) {
169 SYSCALL_ERROR(PermissionDenied);
170 return false;
171 }
172 }
173#endif
174 FilesystemPathRef oldParent;
175 return m_State->parent(target, oldParent) &&
176 authorizeRename(oldParent, target->node(), parent, nullptr, writer, false);
177}
178
179bool VfsMountView::authorizeRename(const FilesystemPathRef& oldParent, File* source,
180 const FilesystemPathRef& newParent, File* replaced,
181 const VFS::NamespaceMutation& writer, bool removeSource) {
182 if ((removeSource && !authorizeRemove(oldParent, source, writer)) ||
183 !checkFilesystemAccess(newParent, creationAccess(source), &writer) ||
184 (replaced && !authorizeRemove(newParent, replaced, writer))) {
185 return false;
186 }
187#ifndef VFS_STANDALONE
188 auto* policy = currentPolicy();
189 if (!policy || samePath(oldParent, newParent)) {
190 return true;
191 }
192 FilesystemPathRef selected;
193 if (!m_State->makePath(m_State->path(oldParent)->attachment, source, selected)) {
194 return false;
195 }
196 static_cast<VfsPath*>(selected.get())->lookupParent = oldParent;
198 if (!m_State->ancestors(selected, from) || !to.tryReserve(1)) {
199 SYSCALL_ERROR(OutOfMemory);
200 return false;
201 }
202 to.pushBack(selected);
203 if (!m_State->ancestors(newParent, to)) {
204 SYSCALL_ERROR(OutOfMemory);
205 return false;
206 }
207 if (!checkFilesystemAccess(oldParent, FilesystemAccess::Refer, &writer) ||
208 !checkFilesystemAccess(newParent, FilesystemAccess::Refer, &writer) ||
209 !policy->filesystemReparent(from.begin(), from.count(), to.begin(), to.count())) {
210 SYSCALL_ERROR(CrossDeviceLink);
211 return false;
212 }
213#endif
214 return true;
215}
static Directory * fromFile(File *pF)
Definition Directory.h:151
MUST_USE_RESULT LookupStatus lookupChild(const HashedStringView &s, ChildLease &child) const
Definition Directory.cc:355
Definition File.h:75
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
T * get() const
A vector / dynamic array.
Definition Vector.h:33
Iterator begin()
Definition Vector.h:162
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270