The Pedigree Project 0.1
mqueue-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/processor/Processor.h"
5#include "pedigree/kernel/processor/ProcessorInformation.h"
6#include "pedigree/kernel/syscallError.h"
7
8#include <fcntl.h>
9
10#include "modules/subsys/posix/FileDescriptor.h"
11#include "modules/subsys/posix/PosixProcess.h"
12#include "modules/subsys/posix/PosixSubsystem.h"
13#include "mqueue-state.h"
14
15Mutex g_MqueueRegistryLock;
17namespace {
18// Pooled nodes retain erased references and can destroy them under this lock
19// when reused; queue destruction acquires the registry lock itself.
21constexpr size_t MaximumQueues = 64;
22constexpr int AllowedFlags = O_ACCMODE | O_CREAT | O_EXCL | O_NONBLOCK | O_CLOEXEC;
23
24bool copyName(const char* userName, String& name) {
25 const auto result = PosixSubsystem::copyUserString(userName, name, 256);
26 if (result != PosixSubsystem::UserStringSuccess) {
27 syscallError(result == PosixSubsystem::UserStringTooLong ? Error::NameTooLong
28 : Error::BadAddress);
29 return false;
30 }
31 if (!name.length()) {
32 SYSCALL_ERROR(DoesNotExist);
33 return false;
34 }
35 for (size_t n = 0; n < name.length(); ++n) {
36 if (name[n] == '/') {
37 SYSCALL_ERROR(PermissionDenied);
38 return false;
39 }
40 }
41 if (name == "." || name == "..") {
42 SYSCALL_ERROR(PermissionDenied);
43 return false;
44 }
45 return true;
46}
47
48bool queueDescriptor(int fd, DescriptorLease& descriptor, SharedPointer<PosixMessageQueue>& queue,
49 int forbiddenAccess = -1) {
50 if (!acquireDescriptor(fd, descriptor) || !(queue = descriptor->getMqueueImpl()) ||
51 (descriptor->getStatusFlags() & O_ACCMODE) == forbiddenAccess) {
52 SYSCALL_ERROR(BadFileDescriptor);
53 return false;
54 }
55 return true;
56}
57} // namespace
58
59int posix_mq_open(const char* userName, int flags, unsigned mode, const LinuxMqAttr* userAttr) {
60 TerminationDeferral lifetime;
61 if ((flags & ~AllowedFlags) || (flags & O_ACCMODE) == O_ACCMODE) {
62 SYSCALL_ERROR(InvalidArgument);
63 return -1;
64 }
65 String name;
66 if (!copyName(userName, name)) {
67 return -1;
68 }
69 Process* process = Processor::information().getCurrentThread()->getParent();
71 {
72 LockGuard<Mutex> guard(g_MqueueRegistryLock);
73 for (auto it = namedQueues.begin(); it != namedQueues.end(); ++it) {
74 if ((*it)->namespaceId() == posix_ipc_namespace_id() && (*it)->name() == name) {
75 queue = *it;
76 break;
77 }
78 }
79 if (queue) {
80 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
81 SYSCALL_ERROR(FileExists);
82 return -1;
83 }
84 if (!queue->mayOpen(process, flags)) {
85 SYSCALL_ERROR(PermissionDenied);
86 return -1;
87 }
88 } else {
89 if (!(flags & O_CREAT)) {
90 SYSCALL_ERROR(DoesNotExist);
91 return -1;
92 }
93 LinuxMqAttr attr = {0, 10, 8192, 0, {}};
94 if (userAttr && !PosixSubsystem::copyFromUser(&attr, userAttr, sizeof(attr))) {
95 SYSCALL_ERROR(BadAddress);
96 return -1;
97 }
98 if (attr.maxMessages <= 0 || attr.maxMessages > 128 || attr.messageSize <= 0 ||
99 attr.messageSize > 8192) {
100 SYSCALL_ERROR(InvalidArgument);
101 return -1;
102 }
103 if (g_Mqueues.count() >= MaximumQueues) {
104 SYSCALL_ERROR(NoSpaceLeftOnDevice);
105 return -1;
106 }
107 mode &= 0777 & ~static_cast<PosixProcess*>(process)->getMask();
109 name, attr.maxMessages, attr.messageSize, process->getEffectiveUserId(),
110 process->getEffectiveGroupId(), mode));
111 g_Mqueues.pushBack(queue.get());
112 namedQueues.pushBack(queue);
113 }
114 }
115 // Linux applies close-on-exec to every mq_open descriptor, including opens
116 // which omit O_CLOEXEC. The status flags belong to the shared description.
117 FileDescriptor* descriptor =
118 new FileDescriptor(nullptr, 0, 0xFFFFFFFF, FD_CLOEXEC, flags & (O_ACCMODE | O_NONBLOCK));
119 descriptor->setMqueueImpl(queue);
120 DescriptorLease installed;
121 return static_cast<int>(installDescriptor(descriptor, installed));
122}
123
124int posix_mq_unlink(const char* userName) {
125 TerminationDeferral lifetime;
126 String name;
127 if (!copyName(userName, name)) {
128 return -1;
129 }
131 {
132 LockGuard<Mutex> guard(g_MqueueRegistryLock);
133 for (auto it = namedQueues.begin(); it != namedQueues.end(); ++it) {
134 if ((*it)->namespaceId() == posix_ipc_namespace_id() && (*it)->name() == name) {
135 if (!(*it)->mayUnlink(Processor::information().getCurrentThread()->getParent())) {
136 SYSCALL_ERROR(PermissionDenied);
137 return -1;
138 }
139 removed = *it;
140 namedQueues.erase(it);
141 return 0;
142 }
143 }
144 }
145 SYSCALL_ERROR(DoesNotExist);
146 return -1;
147}
148
149int posix_mq_timedsend(int fd, const char* data, size_t length, unsigned priority,
150 const LinuxMqTimespec* timeout) {
151 TerminationDeferral lifetime;
152 DescriptorLease descriptor;
154 if (!queueDescriptor(fd, descriptor, queue, O_RDONLY)) {
155 return -1;
156 }
157 return queue->send(data, length, priority, descriptor->getStatusFlags() & O_NONBLOCK, timeout);
158}
159
160int posix_mq_timedreceive(int fd, char* data, size_t length, unsigned* priority,
161 const LinuxMqTimespec* timeout) {
162 TerminationDeferral lifetime;
163 DescriptorLease descriptor;
165 if (!queueDescriptor(fd, descriptor, queue, O_WRONLY)) {
166 return -1;
167 }
168 return queue->receive(data, length, priority, descriptor->getStatusFlags() & O_NONBLOCK, timeout);
169}
170
171int posix_mq_notify(int fd, const LinuxMqSigevent* event) {
172 TerminationDeferral lifetime;
173 DescriptorLease descriptor;
175 if (!queueDescriptor(fd, descriptor, queue)) {
176 return -1;
177 }
178 return queue->notify(event);
179}
180
181int posix_mq_getsetattr(int fd, const LinuxMqAttr* requested, LinuxMqAttr* previous) {
182 TerminationDeferral lifetime;
183 DescriptorLease descriptor;
185 if (!queueDescriptor(fd, descriptor, queue)) {
186 return -1;
187 }
188 return queue->attributes(*descriptor, requested, previous);
189}
190
191void posix_mqueue_close(PosixMessageQueue* queue, size_t pid) {
192 if (queue) {
193 queue->cancelNotification(pid);
194 }
195}
196
197void posix_mqueue_process_exit(size_t pid) {
198 LockGuard<Mutex> guard(g_MqueueRegistryLock);
199 for (auto it = g_Mqueues.begin(); it != g_Mqueues.end(); ++it) {
200 (*it)->cancelNotification(pid);
201 }
202}
203
204void posix_mqueue_clock_changed() {
205 LockGuard<Mutex> guard(g_MqueueRegistryLock);
206 for (auto it = g_Mqueues.begin(); it != g_Mqueues.end(); ++it) {
207 (*it)->clockChanged();
208 }
209}
210
211void posix_mqueue_namespace_exit(uint64_t identity) {
212 while (true) {
214 {
215 LockGuard<Mutex> guard(g_MqueueRegistryLock);
216 for (auto it = namedQueues.begin(); it != namedQueues.end(); ++it) {
217 if ((*it)->namespaceId() == identity) {
218 removed = *it;
219 namedQueues.erase(it);
220 break;
221 }
222 }
223 }
224 if (!removed) {
225 return;
226 }
227 }
228}
int getStatusFlags() const
Get current status flags.
Definition List.h:61
Iterator begin()
Definition List.h:122
Iterator end()
Definition List.h:132
Definition Mutex.h:56
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static UserStringResult copyUserString(const char *userString, String &copy, size_t maxLength)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
T * get() const
Iterator erase(Iterator &Iter)
Definition List.h:352
void pushBack(const T &value)
Definition List.h:216