The Pedigree Project 0.1
ProcFs-usermaps.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Scheduler.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8#include "pedigree/kernel/utilities/Pointers.h"
9#include "pedigree/kernel/utilities/utility.h"
10
11#include "ProcFs.h"
12#include "modules/system/vfs/VFS.h"
13#include "user-namespace.h"
14
15namespace {
16enum class Kind { Uid, Gid, Setgroups };
17const char* name(Kind kind) {
18 return kind == Kind::Uid ? "uid_map" : kind == Kind::Gid ? "gid_map" : "setgroups";
19}
20
21class UserMapFile final : public File {
22 public:
23 UserMapFile(ProcFs& filesystem, File* parent, Kind kind, const UserNamespaceRef& space,
24 uint32_t uid, uint32_t gid)
25 : File(String(name(kind)), 0, 0, 0, filesystem.getNextInode(), &filesystem, 0, parent),
26 m_Kind(kind),
27 m_Space(space),
28 m_Viewer(posix_user_namespace(*Processor::information().getCurrentThread())) {
29 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_OR);
30 setUidOnly(uid);
31 setGidOnly(gid);
32 }
33 bool allowMapping(bool, bool, bool&) override {
34 SYSCALL_ERROR(NoSuchDevice);
35 return false;
36 }
37
38 protected:
39 bool isBytewise() const override {
40 return true;
41 }
42 bool allowResize(size_t, size_t) override {
43 SYSCALL_ERROR(InvalidArgument);
44 return false;
45 }
46 uint64_t readBytewise(uint64_t offset, uint64_t size, uintptr_t buffer, bool) override {
47 auto bytes = UniqueArray<char>::allocate(4096);
48 if (!bytes) {
49 SYSCALL_ERROR(OutOfMemory);
50 return 0;
51 }
52 size_t length;
53 if (m_Kind == Kind::Setgroups) {
54 const bool allow = !m_Space || m_Space->groupsAllowed();
55 length = allow ? 6 : 5;
56 MemoryCopy(bytes.get(), allow ? "allow\n" : "deny\n", length);
57 } else if (!m_Space) {
58 static const char initial[] = "0 0 4294967295\n";
59 length = sizeof(initial) - 1;
60 MemoryCopy(bytes.get(), initial, length);
61 } else {
62 length = m_Space->readMap(m_Kind == Kind::Gid, m_Viewer, bytes.get(), 4096);
63 }
64 if (offset >= length) {
65 return 0;
66 }
67 if (size > length - offset) {
68 size = length - offset;
69 }
70 MemoryCopy(reinterpret_cast<void*>(buffer), bytes.get() + offset, size);
71 return size;
72 }
73 uint64_t writeBytewise(uint64_t offset, uint64_t size, uintptr_t buffer, bool) override {
74 if (!m_Space) {
75 SYSCALL_ERROR(NotEnoughPermissions);
76 return 0;
77 }
78 if (offset || size >= 4096) {
79 SYSCALL_ERROR(InvalidArgument);
80 return 0;
81 }
82 const char* bytes = reinterpret_cast<const char*>(buffer);
83 const int result = m_Kind == Kind::Setgroups
84 ? m_Space->writeSetgroups(bytes, size)
85 : m_Space->writeMap(m_Kind == Kind::Gid, bytes, size);
86 return result < 0 ? 0 : result;
87 }
88
89 private:
90 const Kind m_Kind;
91 const UserNamespaceRef m_Space, m_Viewer;
92};
93
94class UserMapNode final : public File {
95 public:
96 UserMapNode(ProcFs& filesystem, File* parent, Process& process, Kind kind)
97 : File(String(name(kind)), 0, 0, 0, filesystem.getNextInode(), &filesystem, 0, parent),
98 m_Kind(kind),
99 m_ProcessId(process.getId()),
100 m_Identity(process.pidIdentity()) {
101 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_OR);
102 setUidOnly(process.getEffectiveUserId());
103 setGidOnly(process.getEffectiveGroupId());
104 }
105 File* openForDescriptor(RetainedFile& owner) override {
106 TerminationDeferral lifetime;
109 if (!Scheduler::instance().acquireProcessById(process, m_ProcessId) ||
110 process->pidIdentity() != m_Identity ||
111 !process->acquireThreadByUserspaceId(task, process->getUserspaceId(), nullptr)) {
112 SYSCALL_ERROR(NoSuchProcess);
113 return nullptr;
114 }
115 // Capture at open, so unshare changes subsequent opens while an existing
116 // descriptor continues to address the namespace it originally opened.
117 auto* file = new UserMapFile(*static_cast<ProcFs*>(getFilesystem()), getParent(), m_Kind,
118 posix_user_namespace(*task.get()), process->getEffectiveUserId(),
119 process->getEffectiveGroupId());
120 if (!file || !VFS::instance().tryTrackFile(file)) {
121 delete file;
122 SYSCALL_ERROR(OutOfMemory);
123 return nullptr;
124 }
125 owner.adopt(file);
126 return file;
127 }
128
129 private:
130 const Kind m_Kind;
131 const size_t m_ProcessId;
132 const SharedPointer<UserspacePid> m_Identity;
133};
134} // namespace
135
136bool procfsAddUserMaps(ProcFs& filesystem, ProcFsDirectory& directory, Process& process) {
137 constexpr Kind kinds[] = {Kind::Uid, Kind::Gid, Kind::Setgroups};
138 for (Kind kind : kinds) {
139 auto* file = new UserMapNode(filesystem, &directory, process, kind);
140 if (!file) {
141 return false;
142 }
143 directory.addEntry(file->getName(), file);
144 }
145 return true;
146}
Definition File.h:75
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1369
virtual bool isBytewise() const
Definition File.cc:1365
void setGidOnly(size_t gid)
Definition File.cc:1448
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
Definition File.cc:1177
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1376
virtual File * openForDescriptor(RetainedFile &owner)
Definition File.cc:1361
size_t getUserspaceId() const
Definition Process.h:504
static Scheduler & instance()
Definition Scheduler.h:96
static VFS & instance()
Definition VFS.cc:311