The Pedigree Project 0.1
file-syscalls.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Subsystem.h"
21#include "pedigree/kernel/compiler.h"
22#include "pedigree/kernel/machine/Disk.h"
23#include "pedigree/kernel/machine/Keyboard.h"
24#include "pedigree/kernel/machine/KeymapManager.h"
25#include "pedigree/kernel/machine/Machine.h"
26#include "pedigree/kernel/process/Process.h"
27#include "pedigree/kernel/process/TerminationDeferral.h"
28#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
29#include "pedigree/kernel/processor/Processor.h"
30#include "pedigree/kernel/processor/VirtualAddressSpace.h"
31#include "pedigree/kernel/processor/types.h"
32#include "pedigree/kernel/syscallError.h"
33#include "pedigree/kernel/utilities/Pointers.h"
34#include "pedigree/kernel/utilities/assert.h"
35#include "pedigree/kernel/utilities/utility.h"
36
37#include <FileDescriptor.h>
38#include <PosixProcess.h>
39#include <PosixSubsystem.h>
40#include <dirent.h>
41#include <fcntl.h>
42#include <limits.h>
43#include <stddef.h>
44#include <stdint.h>
45#include <termios.h>
46#include <utime.h>
47
48#include "DevFs-block.h"
49#include "TerminalControl.h"
50#include "advisory-lock-syscalls.h"
51#include "console-syscalls.h"
52#include "eventfd-syscalls.h"
53#include "fanotify-syscalls.h"
54#include "file-metadata.h"
55#include "file-syscalls.h"
56#include "inotify-syscalls.h"
57#include "landlock.h"
58#include "memfd-syscalls.h"
59#include "metadata-syscalls.h"
60#include "modules/subsys/posix/IoEvent.h"
61#include "modules/system/console/Console.h"
62#include "modules/system/users/User.h"
63#include "modules/system/vfs/Directory.h"
64#include "modules/system/vfs/File.h"
66#include "modules/system/vfs/MountView.h"
67#include "modules/system/vfs/Pipe.h"
68#include "modules/system/vfs/Symlink.h"
69#include "modules/system/vfs/VFS.h"
70#include "namespace-file.h"
71#include "net-syscalls.h"
72#include "network-namespace.h"
73#include "pipe-syscalls.h"
74#include "signalfd-syscalls.h"
75#include "timerfd-syscalls.h"
76#include "user-namespace.h"
77#include <sys/file.h>
78#include <sys/ioctl.h>
79#include <sys/mman.h>
80#include <sys/stat.h>
81#include <sys/statfs.h>
82#include <sys/statvfs.h>
83
84// Emits a lot of logs in normalisePath to help debug remaps.
85static constexpr bool VerboseNormalisation = false;
86
87extern int posix_getpid();
88
89// For getdents() (getdents64 uses a compatible struct dirent).
91 long d_ino;
92 off_t d_off;
93 unsigned short d_reclen;
94 char d_name[1];
95};
96
97extern DevFs* g_pDevFs;
98
99//
100// Syscalls pertaining to files.
101//
102
103#define CHECK_FLAG(a, b) (((a) & (b)) == (b))
104
105static PosixProcess* getPosixProcess() {
106 Process* pStockProcess = Processor::information().getCurrentThread()->getParent();
107 if (pStockProcess->getType() != Process::Posix) {
108 return 0;
109 }
110
111 PosixProcess* pProcess = static_cast<PosixProcess*>(pStockProcess);
112 return pProcess;
113}
114
115static bool copyUserString(const char* userString, String& copy) {
116 PosixSubsystem::UserStringResult result =
117 PosixSubsystem::copyUserString(userString, copy, PATH_MAX);
118 if (result == PosixSubsystem::UserStringBadAddress) {
119 SYSCALL_ERROR(BadAddress);
120 return false;
121 }
122 if (result == PosixSubsystem::UserStringTooLong) {
123 SYSCALL_ERROR(NameTooLong);
124 return false;
125 }
126 return true;
127}
128
129File* findFilePath(const String& name, ResolvedPath& result, const FilesystemPathRef& start,
130 bool followFinal) {
131 Process* process = Processor::information().getCurrentThread()->getParent();
132 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
133 return subsystem ? subsystem->findFileRetained(name, result, start, followFinal) : nullptr;
134}
135
136File* followFilePath(ResolvedPath& selected) {
137 Process* process = Processor::information().getCurrentThread()->getParent();
138 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
139 return subsystem ? subsystem->followFile(selected) : nullptr;
140}
141
142static File* traverseSymlink(File* file, ResolvedPath& selected) {
143 if (!file) {
144 if (!Processor::information().getCurrentThread()->getErrno())
145 SYSCALL_ERROR(DoesNotExist);
146 return nullptr;
147 }
148 if (!file->isSymlink())
149 return file;
150 if (selected.get() != file) {
151 SYSCALL_ERROR(DoesNotExist);
152 return nullptr;
153 }
154 return followFilePath(selected);
155}
156
157static bool doChdir(File* dir, ResolvedPath& selected) {
158 File* target = traverseSymlink(dir, selected);
159 if (!target)
160 return false;
161 if (!target->isDirectory() || selected.get() != target) {
162 SYSCALL_ERROR(NotADirectory);
163 return false;
164 }
165 auto* process = Processor::information().getCurrentThread()->getParent();
166 auto context = process->acquireFilesystemContext();
167 auto* view = VFS::instance().mountView();
168 return view && context && view->changeCwd(context, selected.path());
169}
170
171static bool doStat(const char* name, File* file, struct stat* output) {
172 return posix_stat_file(name, file, output);
173}
174
175// NON-special-case remappings.
176static struct Remapping {
177 // from must match either completely, or be followed by a "/"
178 const char* from;
179 const char* to;
180 const char* fsname; // certain remaps are to be reported as custom FS's to
181 // some ABIs
182 bool all_abis; // certain ABIs shouldn't normalise certain paths
183 bool on_devfs; // certain callers care about the result being on devfs
184} g_Remappings[] = {
185 {"/dev", "/dev", "dev", true, true},
186 {"/proc", "/proc", "proc", true, false},
187 {"/tmp", "/tmp", "tmpfs", true, false},
188 {"/run", "/run", "tmpfs", true, false},
189 {"/var/run", "/run", "tmpfs", true, false},
190 // Temporary compatibility for packages built against Pedigree's old
191 // non-FHS root. PUP extracts through these syscalls, so old package members
192 // are installed into the canonical namespace while packages are rebuilt.
193 {"/.profile", "/root/.profile", nullptr, true, false},
194 {"/.bashrc", "/root/.bashrc", nullptr, true, false},
195 {"/support/pup/db", "/var/cache/pup", nullptr, true, false},
196 {"/support/pup", "/etc/pup", nullptr, true, false},
197 {"/system/initscripts", "/etc/init.d", nullptr, true, false},
198 {"/system/modules", "/usr/lib/modules", nullptr, true, false},
199 {"/system/include", "/usr/include", nullptr, true, false},
200 {"/system/locale", "/usr/share/locale", nullptr, true, false},
201 {"/system/keymaps", "/usr/share/keymaps", nullptr, true, false},
202 {"/system/fonts", "/usr/share/fonts", nullptr, true, false},
203 {"/applications", "/usr/bin", nullptr, true, false},
204 {"/libraries", "/usr/lib", nullptr, true, false},
205 {"/initscripts", "/etc/init.d", nullptr, true, false},
206 {"/config", "/etc", nullptr, true, false},
207 {"/support", "/usr/lib/pedigree", nullptr, true, false},
208 {"/include", "/usr/include", nullptr, true, false},
209 {"/users", "/home", nullptr, true, false},
210 {"/fonts", "/usr/share/fonts", nullptr, true, false},
211 {"/docs", "/usr/share/doc", nullptr, true, false},
212 {"/doc", "/usr/share/doc", nullptr, true, false},
213 {nullptr, nullptr, nullptr, false, false},
214};
215
216bool normalisePath(String& nameToOpen, const char* name, bool* onDevFs) {
217 if (!name || !name[0]) {
218 nameToOpen.clear();
219 if (onDevFs) {
220 *onDevFs = false;
221 }
222 return true;
223 }
224 Process* pProcess = Processor::information().getCurrentThread()->getParent();
225 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
226 // Compatibility mappings apply consistently while old PUP packages are
227 // still in circulation.
228 bool fixFilesystemPaths = true;
229
230 // /dev/tty is special because it can refer to the controlling terminal.
231 // Each compatibility rule must match a complete path component, so a
232 // legacy name never rewrites a merely similar modern path.
233 if (!StringCompare(name, "/dev/tty")) {
234 // Get controlling console, unless we have none.
235 auto cttyContext = pProcess->acquireCttyContext();
236 if (!cttyContext || !cttyContext->file()) {
237 if (onDevFs)
238 *onDevFs = true;
239 }
240
241 nameToOpen.assign(name);
242 return true;
243 } else {
244 // try the remappings
245 struct Remapping* remap = g_Remappings;
246 EMIT_IF(VerboseNormalisation) {
247 F_NOTICE("performing remap for '" << name << "'...");
248 }
249 bool ok = false;
250 while (remap->from != nullptr) {
251 if (!(fixFilesystemPaths || remap->all_abis)) {
252 EMIT_IF(VerboseNormalisation) {
253 F_NOTICE(" -> ignoring " << remap->from << " as it is not for the current ABI");
254 }
255 ++remap;
256 continue;
257 }
258
259 EMIT_IF(VerboseNormalisation) {
260 F_NOTICE(" -> check against " << remap->from);
261 }
262 if (!StringCompare(name, remap->from)) {
263 EMIT_IF(VerboseNormalisation) {
264 F_NOTICE(" -> direct remap to " << remap->to);
265 }
266 nameToOpen.assign(remap->to);
267 ok = true;
268 break;
269 }
270
271 // does not match directly, so we need to check for a partial match
272 if (!StringCompareN(name, remap->from, StringLength(remap->from))) {
273 EMIT_IF(VerboseNormalisation) {
274 F_NOTICE(" -> possibly partial remap");
275 }
276
277 // we have a partial match, but this only OK if the following
278 // character is '/' to avoid incorrectly rewriting paths
279 if (*(name + StringLength(remap->from)) == '/') {
280 // good
281 nameToOpen.assign(remap->to);
282 nameToOpen += (name + StringLength(remap->from));
283 EMIT_IF(VerboseNormalisation) {
284 F_NOTICE(" -> indirect remap to create path '" << nameToOpen << "'...");
285 }
286 ok = true;
287 break;
288 }
289
290 // no good
291 EMIT_IF(VerboseNormalisation) {
292 NOTICE(
293 " -> cannot use this remap as it is not actually "
294 "matching a path segment");
295 }
296 }
297
298 ++remap;
299 }
300
301 if (onDevFs && remap) {
302 *onDevFs = remap->on_devfs;
303 }
304
305 if (!ok) {
306 nameToOpen.assign(name);
307 return false;
308 }
309
310 return true;
311 }
312}
313
314int posix_close(int fd) {
315#if VERBOSE_KERNEL
316 F_NOTICE("close(" << fd << ")");
317#endif
318 Process* pProcess = Processor::information().getCurrentThread()->getParent();
319 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
320 if (!pSubsystem) {
321 ERROR("No subsystem for this process!");
322 return -1;
323 }
324
325 DescriptorLease pFd;
326 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
327 // Error - no such file descriptor.
328 SYSCALL_ERROR(BadFileDescriptor);
329 return -1;
330 }
331
332#if !VERBOSE_KERNEL
333 F_NOTICE("close(" << fd << ")");
334#endif
335
336 // Remove only the generation we acquired. Another thread can close this
337 // descriptor and reuse the numeric fd while this path is still running;
338 // an unconditional free would then close the replacement by mistake.
339 if (!pSubsystem->closeFileDescriptor(fd, pFd)) {
340 return 0;
341 }
342
343 // If this was a master psuedoterminal, we should unlock it now.
344 if (ConsoleManager::instance().isConsole(pFd->getFile())) {
345 if (ConsoleManager::instance().isMasterConsole(pFd->getFile())) {
346 ConsoleManager::instance().unlockConsole(pFd->getFile());
347 }
348 }
349
350 return 0;
351}
352
353int posix_open(const char* name, int flags, int mode) {
354 return posix_openat(AT_FDCWD, name, flags, mode);
355}
356
357namespace {
358constexpr size_t ScalarIoBounceCapacity = PIPE_BUF_MAX + 1;
359constexpr size_t RegularReadBounceCapacity = 128 * 1024;
360constexpr size_t RegularWriteBounceCapacity = 64 * 1024;
361
362#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
363Process::BenchmarkVmCounter vmLengthCounter(bool unmap, size_t pages) {
364 const size_t bucket = pages <= 1 ? 0
365 : pages <= 3 ? 1
366 : pages <= 15 ? 2
367 : pages <= 63 ? 3
368 : pages <= 255 ? 4
369 : 5;
370 const auto first = unmap ? Process::VmMunmapLength1 : Process::VmMmapLength1;
371 return static_cast<Process::BenchmarkVmCounter>(static_cast<size_t>(first) + bucket);
372}
373#endif
374
375UniqueArray<uint8_t> allocateReadBounce(File* file, size_t length, size_t& capacity) {
376 const bool diskBackedRegular = file->supportsRegularFileOperations() && !file->isBlockDevice() &&
377 file->getFilesystem() && file->getFilesystem()->getDisk();
378 const size_t limit = diskBackedRegular ? RegularReadBounceCapacity : ScalarIoBounceCapacity;
379 capacity = length < limit ? length : limit;
381 if (!bounce && capacity > ScalarIoBounceCapacity) {
382 capacity = ScalarIoBounceCapacity;
383 bounce = UniqueArray<uint8_t>::allocate(capacity);
384 }
385 return bounce;
386}
387
388UniqueArray<uint8_t> allocateWriteBounce(bool regularFile, size_t length, size_t& capacity) {
389 const size_t limit = regularFile ? RegularWriteBounceCapacity : ScalarIoBounceCapacity;
390 capacity = length < limit ? length : limit;
392 if (!bounce && capacity > ScalarIoBounceCapacity) {
393 capacity = ScalarIoBounceCapacity;
394 bounce = UniqueArray<uint8_t>::allocate(capacity);
395 }
396 return bounce;
397}
398
399bool copyWriteSource(void* destination, const void* source, size_t& requested) {
400 if (PosixSubsystem::copyFromUser(destination, source, requested)) {
401 return true;
402 }
403 if (requested <= ScalarIoBounceCapacity) {
404 return false;
405 }
406
407 // Preserve partial progress when a later page rejects the larger user copy.
408 requested = ScalarIoBounceCapacity;
409 return PosixSubsystem::copyFromUser(destination, source, requested);
410}
411
412bool checkReadDestination(void* destination, size_t& requested) {
413 if (PosixSubsystem::checkUserBuffer(reinterpret_cast<uintptr_t>(destination), requested, 1,
414 PosixSubsystem::SafeWrite)) {
415 return true;
416 }
417 if (requested <= ScalarIoBounceCapacity) {
418 return false;
419 }
420
421 // A larger precheck must not reject the valid prefix that smaller reads delivered.
422 requested = ScalarIoBounceCapacity;
423 return PosixSubsystem::checkUserBuffer(reinterpret_cast<uintptr_t>(destination), requested, 1,
424 PosixSubsystem::SafeWrite);
425}
426
427bool scalarIoRangeDoesNotWrap(const void* buffer, size_t length) {
428 if (!length) {
429 return true;
430 }
431
432 const uintptr_t address = reinterpret_cast<uintptr_t>(buffer);
433 return address && length - 1 <= (~static_cast<uintptr_t>(0) - address);
434}
435
436bool positionalIoRangeIsValid(off_t offset, size_t length) {
437 if (offset < 0 || length > static_cast<size_t>(SSIZE_MAX)) {
438 return false;
439 }
440
441 const uint64_t location = static_cast<uint64_t>(offset);
442 return static_cast<uint64_t>(length) <= static_cast<uint64_t>(INT64_MAX) - location;
443}
444} // namespace
445
446int posix_read(int fd, char* ptr, int len) {
447 F_NOTICE("read(" << Dec << fd << Hex << ", " << reinterpret_cast<uintptr_t>(ptr) << ", " << len
448 << ")");
449 if (len < 0) {
450 SYSCALL_ERROR(InvalidArgument);
451 return -1;
452 }
453
454 // Lookup this process.
455 Thread* pThread = Processor::information().getCurrentThread();
456 Process* pProcess = pThread->getParent();
457 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
458 if (!pSubsystem) {
459 ERROR("No subsystem for this process!");
460 return -1;
461 }
462
463 DescriptorLease pFd;
464 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
465 // Error - no such file descriptor.
466 SYSCALL_ERROR(BadFileDescriptor);
467 return -1;
468 }
469 if (pFd->getFile() &&
470 ((pFd->getAccessFlags() & O_PATH) || (pFd->getAccessFlags() & O_ACCMODE) == O_WRONLY)) {
471 SYSCALL_ERROR(BadFileDescriptor);
472 return -1;
473 }
474
475 if (!pFd->getFile()) {
476 auto timerFd = pFd->getTimerFdImpl();
477 auto signalFd = pFd->getSignalFdImpl();
478 if (timerFd || signalFd) {
479 const bool canBlock = !(pFd->getStatusFlags() & O_NONBLOCK);
480 pFd.reset();
481 return timerFd ? timerFd->readToUser(ptr, len, canBlock)
482 : signalFd->readToUser(ptr, len, canBlock);
483 }
484
485 SharedPointer<EventFd> eventFd = pFd->getEventFdImpl();
486 if (eventFd) {
487 if (len < static_cast<int>(sizeof(uint64_t))) {
488 SYSCALL_ERROR(InvalidArgument);
489 return -1;
490 }
491 if (!PosixSubsystem::checkUserBuffer(reinterpret_cast<uintptr_t>(ptr), sizeof(uint64_t), 1,
492 PosixSubsystem::SafeWrite)) {
493 SYSCALL_ERROR(BadAddress);
494 return -1;
495 }
496
497 const bool canBlock = !(pFd->getStatusFlags() & O_NONBLOCK);
498 pFd.reset();
499 uint64_t value = 0;
500 const int result = eventFd->readValue(value, canBlock);
501 if (result < 0) {
502 return result;
503 }
504 if (!PosixSubsystem::copyToUser(ptr, &value, sizeof(value))) {
505 SYSCALL_ERROR(BadAddress);
506 return -1;
507 }
508 return result;
509 }
510
512 if (inotify) {
513 const size_t length = static_cast<size_t>(len);
514 const bool canBlock = !(pFd->getStatusFlags() & O_NONBLOCK);
515 pFd.reset();
516 return inotify->readEventsToUser(reinterpret_cast<uint8_t*>(ptr), length, canBlock);
517 }
518 auto fanotify = pFd->getFanotifyImpl();
519 if (fanotify) {
520 const bool canBlock = !(pFd->getStatusFlags() & O_NONBLOCK);
521 pFd.reset();
522 return fanotify->readToUser(ptr, static_cast<size_t>(len), canBlock);
523 }
524
525 if (pFd->networkImpl) {
526 // Need to redirect to socket implementation.
527 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(ptr), static_cast<size_t>(len),
528 PosixSubsystem::SafeWrite)) {
529 F_NOTICE(" -> invalid address");
530 SYSCALL_ERROR(BadAddress);
531 return -1;
532 }
533 return posix_recv_descriptor(pFd, ptr, len, 0);
534 }
535
536 SYSCALL_ERROR(InvalidArgument);
537 return -1;
538 }
539
540 if (pFd->getFile()->isDirectory()) {
541 SYSCALL_ERROR(IsADirectory);
542 return -1;
543 }
544
545 if (!len) {
546 return 0;
547 }
548
549 const size_t length = static_cast<size_t>(len);
550 if (!scalarIoRangeDoesNotWrap(ptr, length)) {
551 SYSCALL_ERROR(BadAddress);
552 return -1;
553 }
554 size_t bounceCapacity = 0;
556
557 auto readFile = [&](FileDescriptor::PositionGuard* position, int statusFlags) -> int {
558 const bool canBlock = !(statusFlags & O_NONBLOCK);
559 size_t totalRead = 0;
560
561 while (totalRead < length) {
562 if (pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
563 if (totalRead) {
564 break;
565 }
566 pThread->clearInterruption();
567 SYSCALL_ERROR(Interrupted);
568 return -1;
569 }
570
571 const size_t remaining = length - totalRead;
572 size_t requested =
573 remaining < RegularReadBounceCapacity ? remaining : RegularReadBounceCapacity;
574 char* userDestination = reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(ptr) + totalRead);
575
576 if (position) {
577 const size_t cached = PosixSubsystem::readCachedFile(*pFd->getFile(), position->offset(),
578 userDestination, requested);
579 if (cached) {
580 position->advanceOffset(cached);
581 totalRead += cached;
582 continue;
583 }
584 }
585 if (!bounce) {
586 bounce = allocateReadBounce(pFd->getFile(), remaining, bounceCapacity);
587 if (!bounce) {
588 pThread->clearInterruption();
589 if (totalRead) {
590 return static_cast<int>(totalRead);
591 }
592 SYSCALL_ERROR(OutOfMemory);
593 return -1;
594 }
595 }
596 if (requested > bounceCapacity) {
597 requested = bounceCapacity;
598 }
599
600 // Avoid consuming data for an address which is already known to be
601 // unusable. copyToUser repeats this check after a blocking operation.
602 if (!checkReadDestination(userDestination, requested)) {
603 if (totalRead) {
604 pThread->clearInterruption();
605 return static_cast<int>(totalRead);
606 }
607 pThread->clearInterruption();
608 SYSCALL_ERROR(BadAddress);
609 return -1;
610 }
611
612 // A nonseekable operation may block once, but must not block again
613 // after it has already made progress during this syscall.
614 const bool operationCanBlock = canBlock && (position || !totalRead);
615 if (!operationCanBlock) {
616 const ReadyMask ready = pFd->acquireOpenFileDescription()->queryFileReady(true, false);
617 if (!(ready & (ReadyRead | ReadyError | ReadyHangup))) {
618 if (totalRead) {
619 break;
620 }
621 pThread->clearInterruption();
622 SYSCALL_ERROR(NoMoreProcesses);
623 F_NOTICE(" -> async and nothing available to read");
624 return -1;
625 }
626 }
627
628 if (pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
629 if (totalRead) {
630 break;
631 }
632 pThread->clearInterruption();
633 SYSCALL_ERROR(Interrupted);
634 return -1;
635 }
636
637 pThread->setErrno(0);
638 uint64_t amount = 0;
639 if (position) {
640 amount = pFd->getFile()->read(position->offset(), requested,
641 reinterpret_cast<uintptr_t>(bounce.get()), operationCanBlock);
642 } else {
643 amount = pFd->readFile(0, requested, reinterpret_cast<uintptr_t>(bounce.get()),
644 operationCanBlock);
645 }
646 const bool signalInterrupted =
647 pThread->getInterruptionReason() == Thread::InterruptedBySignal;
648 const size_t backendError = pThread->getErrno();
649
650 if (!amount && pFd->terminalHungUp())
651 break;
652 if (!amount) {
653 if (!totalRead && signalInterrupted) {
654 pThread->clearInterruption();
655 SYSCALL_ERROR(Interrupted);
656 F_NOTICE(" -> interrupted");
657 return -1;
658 }
659 if (backendError) {
660 pThread->clearInterruption();
661 if (!totalRead) {
662 return -1;
663 }
664 pThread->setErrno(0);
665 }
666 break;
667 }
668
669 if (!PosixSubsystem::copyToUser(userDestination, bounce.get(), amount)) {
670 if (totalRead) {
671 pThread->clearInterruption();
672 return static_cast<int>(totalRead);
673 }
674 pThread->clearInterruption();
675 SYSCALL_ERROR(BadAddress);
676 return -1;
677 }
678
679 if (position) {
680 position->advanceOffset(amount);
681 }
682 totalRead += amount;
683 if (amount < requested || signalInterrupted ||
684 pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
685 break;
686 }
687 }
688
689 pThread->clearInterruption();
690 F_NOTICE(" -> " << Dec << totalRead << Hex);
691 return static_cast<int>(totalRead);
692 };
693
694 pThread->clearInterruption();
695 if (pFd->getFile()->isSeekable()) {
697 return readFile(&position, position.statusFlags());
698 }
699
700 // A blocking nonseekable read must not hold the OFD metadata mutex needed
701 // by a writer using the same O_RDWR description.
702 return readFile(nullptr, pFd->getStatusFlags());
703}
704
705int posix_write(int fd, char* ptr, int len, bool nocheck) {
706 F_NOTICE("write(" << fd << ", " << reinterpret_cast<uintptr_t>(ptr) << ", " << len << ")");
707 if (len < 0) {
708 SYSCALL_ERROR(InvalidArgument);
709 return -1;
710 }
711 F_NOTICE(" s = " << String(ptr, len));
712
713 // Lookup this process.
714 Thread* pThread = Processor::information().getCurrentThread();
715 Process* pProcess = pThread->getParent();
716 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
717 if (!pSubsystem) {
718 ERROR("No subsystem for this process!");
719 return -1;
720 }
721
722 DescriptorLease pFd;
723 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
724 // Error - no such file descriptor.
725 SYSCALL_ERROR(BadFileDescriptor);
726 return -1;
727 }
728 if (pFd->getFile() &&
729 ((pFd->getAccessFlags() & O_PATH) || (pFd->getAccessFlags() & O_ACCMODE) == O_RDONLY)) {
730 SYSCALL_ERROR(BadFileDescriptor);
731 return -1;
732 }
733
734 VfsMountView::WriteLease mountWrite;
735 if (pFd->openingPath() && pFd->getFile() && pFd->getFile()->supportsRegularFileOperations() &&
736 !pFd->getFile()->isBlockDevice() && !mountWrite.acquire(pFd->openingPath())) {
737 return -1;
738 }
739 if (!pFd->getFile()) {
740 if (pFd->getTimerFdImpl() || pFd->getSignalFdImpl() || pFd->getFanotifyImpl()) {
741 SYSCALL_ERROR(InvalidArgument);
742 return -1;
743 }
744
745 SharedPointer<EventFd> eventFd = pFd->getEventFdImpl();
746 if (eventFd) {
747 if (len != static_cast<int>(sizeof(uint64_t))) {
748 SYSCALL_ERROR(InvalidArgument);
749 return -1;
750 }
751
752 uint64_t value = 0;
753 if (nocheck) {
754 ForwardMemoryCopy(&value, ptr, sizeof(value));
755 } else if (!PosixSubsystem::copyFromUser(&value, ptr, sizeof(value))) {
756 SYSCALL_ERROR(BadAddress);
757 return -1;
758 }
759
760 const bool canBlock = !(pFd->getStatusFlags() & O_NONBLOCK);
761 pFd.reset();
762 return eventFd->writeValue(value, canBlock);
763 }
764
765 if (pFd->getInotifyImpl()) {
766 SYSCALL_ERROR(BadFileDescriptor);
767 return -1;
768 }
769
770 if (pFd->networkImpl) {
771 // Need to redirect to socket implementation.
772 if (!nocheck &&
773 !PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(ptr), static_cast<size_t>(len),
774 PosixSubsystem::SafeRead)) {
775 F_NOTICE(" -> invalid address");
776 SYSCALL_ERROR(BadAddress);
777 return -1;
778 }
779 return posix_send_descriptor(pFd, ptr, len, 0, nocheck);
780 }
781
782 SYSCALL_ERROR(InvalidArgument);
783 return -1;
784 }
785
786 if (!len) {
787 if (pFd->terminalHungUp()) {
788 SYSCALL_ERROR(IoError);
789 return -1;
790 }
791 return 0;
792 }
793
794 const bool pipeLike = pFd->getFile()->isPipe() || pFd->getFile()->isFifo();
795 const size_t length = static_cast<size_t>(len);
796 if (!scalarIoRangeDoesNotWrap(ptr, length)) {
797 SYSCALL_ERROR(BadAddress);
798 return -1;
799 }
800 const bool regularFile =
801 pFd->getFile()->supportsRegularFileOperations() && !pFd->getFile()->isBlockDevice();
802 size_t bounceCapacity = 0;
803 UniqueArray<uint8_t> bounce = allocateWriteBounce(regularFile, length, bounceCapacity);
804 if (!bounce) {
805 SYSCALL_ERROR(OutOfMemory);
806 return -1;
807 }
808 bool deliverPipeSignal = false;
809
810 auto writeFile = [&](FileDescriptor::PositionGuard* position, int statusFlags) -> int {
811 const bool canBlock = !(statusFlags & O_NONBLOCK);
812 File::WriteGuard writeGuard = pFd->getFile()->lockWrites();
813 size_t totalWritten = 0;
814
815 while (totalWritten < length) {
816 if (totalWritten && pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
817 break;
818 }
819
820 const size_t remaining = length - totalWritten;
821 size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
822 const char* userSource =
823 reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(ptr) + totalWritten);
824
825 if (nocheck) {
826 ForwardMemoryCopy(bounce.get(), userSource, requested);
827 } else if (!copyWriteSource(bounce.get(), userSource, requested)) {
828 if (totalWritten) {
829 pThread->clearInterruption();
830 return static_cast<int>(totalWritten);
831 }
832 pThread->clearInterruption();
833 SYSCALL_ERROR(BadAddress);
834 return -1;
835 }
836
837 if (pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
838 if (totalWritten) {
839 break;
840 }
841 pThread->clearInterruption();
842 SYSCALL_ERROR(Interrupted);
843 return -1;
844 }
845
846 pThread->setErrno(0);
847 uint64_t amount = 0;
848 if (position) {
849 uint64_t location = position->offset();
850 amount =
851 (statusFlags & O_APPEND)
852 ? writeGuard.append(requested, reinterpret_cast<uintptr_t>(bounce.get()), location,
853 canBlock, !regularFile)
854 : writeGuard.write(location, requested, reinterpret_cast<uintptr_t>(bounce.get()),
855 canBlock, !regularFile);
856 if (amount) {
857 position->setOffset(location + amount);
858 }
859 } else if (ConsoleManager::instance().isConsole(pFd->getFile())) {
860 amount = pFd->writeFile(0, requested, reinterpret_cast<uintptr_t>(bounce.get()), canBlock);
861 } else {
862 amount =
863 writeGuard.write(0, requested, reinterpret_cast<uintptr_t>(bounce.get()), canBlock);
864 }
865 const bool signalInterrupted =
866 pThread->getInterruptionReason() == Thread::InterruptedBySignal;
867 const size_t backendError = pThread->getErrno();
868
869 if (!amount) {
870 if (totalWritten) {
871 pThread->setErrno(0);
872 break;
873 }
874 if (signalInterrupted && !pFd->terminalHungUp()) {
875 pThread->clearInterruption();
876 SYSCALL_ERROR(Interrupted);
877 F_NOTICE(" -> interrupted");
878 return -1;
879 }
880 if (backendError) {
881 pThread->clearInterruption();
882 deliverPipeSignal = pipeLike && backendError == Error::BrokenPipe;
883 return -1;
884 }
885 if (pipeLike && !Pipe::fromFile(pFd->getFile())->getReaderCount()) {
886 pThread->clearInterruption();
887 F_NOTICE(" -> write to a broken pipe");
888 SYSCALL_ERROR(BrokenPipe);
889 deliverPipeSignal = true;
890 return -1;
891 }
892 if (!canBlock) {
893 pThread->clearInterruption();
894 SYSCALL_ERROR(NoMoreProcesses);
895 return -1;
896 }
897 if (pipeLike) {
898 pThread->clearInterruption();
899 F_NOTICE(" -> write to a broken pipe");
900 SYSCALL_ERROR(BrokenPipe);
901 deliverPipeSignal = true;
902 return -1;
903 }
904 break;
905 }
906
907 pThread->setErrno(0);
908 totalWritten += amount;
909 if (amount < requested || signalInterrupted ||
910 pThread->getInterruptionReason() == Thread::InterruptedBySignal) {
911 break;
912 }
913 }
914
915 pThread->clearInterruption();
916 F_NOTICE(" -> write returns " << totalWritten);
917 return static_cast<int>(totalWritten);
918 };
919
920 pThread->clearInterruption();
921 int result = 0;
922 if (pFd->getFile()->isSeekable()) {
923 {
925 result = writeFile(&position, position.statusFlags());
926 }
927 } else {
928 // See the matching read path: a blocking nonseekable write must not
929 // monopolize the OFD metadata mutex needed by its peer.
930 result = writeFile(nullptr, pFd->getStatusFlags());
931 }
932
933 if (deliverPipeSignal) {
934 pSubsystem->threadException(pThread, Subsystem::Pipe);
935 }
936 return result;
937}
938
939ssize_t posix_pread64(int fd, char* ptr, size_t len, off_t offset) {
940 F_NOTICE("pread64(" << Dec << fd << Hex << ", " << reinterpret_cast<uintptr_t>(ptr) << ", " << len
941 << ", " << offset << ")");
942 if (!positionalIoRangeIsValid(offset, len)) {
943 SYSCALL_ERROR(InvalidArgument);
944 return -1;
945 }
946
947 Thread* thread = Processor::information().getCurrentThread();
948 Process* process = thread->getParent();
949 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
950 if (!subsystem) {
951 ERROR("No subsystem for this process!");
952 return -1;
953 }
954
955 DescriptorLease descriptor;
956 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
957 SYSCALL_ERROR(BadFileDescriptor);
958 return -1;
959 }
960
961 if (!descriptor->getFile() || !descriptor->getFile()->isSeekable()) {
962 SYSCALL_ERROR(IllegalSeek);
963 return -1;
964 }
965 const int statusFlags = descriptor->getStatusFlags();
966 if ((statusFlags & O_PATH) || (statusFlags & O_ACCMODE) == O_WRONLY) {
967 SYSCALL_ERROR(BadFileDescriptor);
968 return -1;
969 }
970 if (descriptor->getFile()->isDirectory()) {
971 SYSCALL_ERROR(IsADirectory);
972 return -1;
973 }
974 if (!len) {
975 return 0;
976 }
977 if (!scalarIoRangeDoesNotWrap(ptr, len)) {
978 SYSCALL_ERROR(BadAddress);
979 return -1;
980 }
981
982 size_t bounceCapacity = 0;
984 const bool canBlock = !(statusFlags & O_NONBLOCK);
985 const uint64_t startingOffset = static_cast<uint64_t>(offset);
986 size_t totalRead = 0;
987
988 thread->clearInterruption();
989 while (totalRead < len) {
990 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
991 if (totalRead) {
992 break;
993 }
994 thread->clearInterruption();
995 SYSCALL_ERROR(Interrupted);
996 return -1;
997 }
998
999 const size_t remaining = len - totalRead;
1000 size_t requested =
1001 remaining < RegularReadBounceCapacity ? remaining : RegularReadBounceCapacity;
1002 char* userDestination = reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(ptr) + totalRead);
1003 const size_t cached = PosixSubsystem::readCachedFile(
1004 *descriptor->getFile(), startingOffset + totalRead, userDestination, requested);
1005 if (cached) {
1006 totalRead += cached;
1007 continue;
1008 }
1009 if (!bounce) {
1010 bounce = allocateReadBounce(descriptor->getFile(), remaining, bounceCapacity);
1011 if (!bounce) {
1012 thread->clearInterruption();
1013 if (totalRead) {
1014 return static_cast<ssize_t>(totalRead);
1015 }
1016 SYSCALL_ERROR(OutOfMemory);
1017 return -1;
1018 }
1019 }
1020 if (requested > bounceCapacity) {
1021 requested = bounceCapacity;
1022 }
1023 if (!checkReadDestination(userDestination, requested)) {
1024 if (totalRead) {
1025 thread->clearInterruption();
1026 return static_cast<ssize_t>(totalRead);
1027 }
1028 thread->clearInterruption();
1029 SYSCALL_ERROR(BadAddress);
1030 return -1;
1031 }
1032
1033 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1034 if (totalRead) {
1035 break;
1036 }
1037 thread->clearInterruption();
1038 SYSCALL_ERROR(Interrupted);
1039 return -1;
1040 }
1041
1042 thread->setErrno(0);
1043 const uint64_t amount = descriptor->getFile()->read(
1044 startingOffset + totalRead, requested, reinterpret_cast<uintptr_t>(bounce.get()), canBlock);
1045 const bool signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
1046 const size_t backendError = thread->getErrno();
1047 if (!amount) {
1048 if (!totalRead && signalInterrupted) {
1049 thread->clearInterruption();
1050 SYSCALL_ERROR(Interrupted);
1051 return -1;
1052 }
1053 if (backendError) {
1054 thread->clearInterruption();
1055 if (!totalRead) {
1056 return -1;
1057 }
1058 thread->setErrno(0);
1059 }
1060 break;
1061 }
1062
1063 if (!PosixSubsystem::copyToUser(userDestination, bounce.get(), amount)) {
1064 if (totalRead) {
1065 thread->clearInterruption();
1066 return static_cast<ssize_t>(totalRead);
1067 }
1068 thread->clearInterruption();
1069 SYSCALL_ERROR(BadAddress);
1070 return -1;
1071 }
1072
1073 totalRead += amount;
1074 if (amount < requested || signalInterrupted ||
1075 thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1076 break;
1077 }
1078 }
1079
1080 thread->clearInterruption();
1081 return static_cast<ssize_t>(totalRead);
1082}
1083
1084ssize_t posix_pwrite64(int fd, const char* ptr, size_t len, off_t offset) {
1085 F_NOTICE("pwrite64(" << Dec << fd << Hex << ", " << reinterpret_cast<uintptr_t>(ptr) << ", "
1086 << len << ", " << offset << ")");
1087 if (!positionalIoRangeIsValid(offset, len)) {
1088 SYSCALL_ERROR(InvalidArgument);
1089 return -1;
1090 }
1091
1092 Thread* thread = Processor::information().getCurrentThread();
1093 Process* process = thread->getParent();
1094 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
1095 if (!subsystem) {
1096 ERROR("No subsystem for this process!");
1097 return -1;
1098 }
1099
1100 DescriptorLease descriptor;
1101 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
1102 SYSCALL_ERROR(BadFileDescriptor);
1103 return -1;
1104 }
1105
1106 if (!descriptor->getFile() || !descriptor->getFile()->isSeekable()) {
1107 SYSCALL_ERROR(IllegalSeek);
1108 return -1;
1109 }
1110 VfsMountView::WriteLease mountWrite;
1111 if (descriptor->openingPath() && descriptor->getFile() &&
1112 descriptor->getFile()->supportsRegularFileOperations() &&
1113 !descriptor->getFile()->isBlockDevice() && !mountWrite.acquire(descriptor->openingPath())) {
1114 return -1;
1115 }
1116 const int statusFlags = descriptor->getStatusFlags();
1117 if ((statusFlags & O_PATH) || (statusFlags & O_ACCMODE) == O_RDONLY) {
1118 SYSCALL_ERROR(BadFileDescriptor);
1119 return -1;
1120 }
1121 if (descriptor->getFile()->isDirectory()) {
1122 SYSCALL_ERROR(IsADirectory);
1123 return -1;
1124 }
1125 if (!len) {
1126 return 0;
1127 }
1128 if (!scalarIoRangeDoesNotWrap(ptr, len)) {
1129 SYSCALL_ERROR(BadAddress);
1130 return -1;
1131 }
1132
1133 const bool regularFile = descriptor->getFile()->supportsRegularFileOperations() &&
1134 !descriptor->getFile()->isBlockDevice();
1135 size_t bounceCapacity = 0;
1136 UniqueArray<uint8_t> bounce = allocateWriteBounce(regularFile, len, bounceCapacity);
1137 if (!bounce) {
1138 SYSCALL_ERROR(OutOfMemory);
1139 return -1;
1140 }
1141 const bool canBlock = !(statusFlags & O_NONBLOCK);
1142 const uint64_t startingOffset = static_cast<uint64_t>(offset);
1143 File::WriteGuard writeGuard = descriptor->getFile()->lockWrites();
1144 size_t totalWritten = 0;
1145
1146 thread->clearInterruption();
1147 while (totalWritten < len) {
1148 if (totalWritten && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1149 break;
1150 }
1151
1152 const size_t remaining = len - totalWritten;
1153 size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
1154 const char* userSource =
1155 reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(ptr) + totalWritten);
1156 if (!copyWriteSource(bounce.get(), userSource, requested)) {
1157 if (totalWritten) {
1158 thread->clearInterruption();
1159 return static_cast<ssize_t>(totalWritten);
1160 }
1161 thread->clearInterruption();
1162 SYSCALL_ERROR(BadAddress);
1163 return -1;
1164 }
1165
1166 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1167 if (totalWritten) {
1168 break;
1169 }
1170 thread->clearInterruption();
1171 SYSCALL_ERROR(Interrupted);
1172 return -1;
1173 }
1174
1175 thread->setErrno(0);
1176 const uint64_t amount =
1177 writeGuard.write(startingOffset + totalWritten, requested,
1178 reinterpret_cast<uintptr_t>(bounce.get()), canBlock, !regularFile);
1179 const bool signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
1180 const size_t backendError = thread->getErrno();
1181 if (!amount) {
1182 if (!totalWritten && signalInterrupted) {
1183 thread->clearInterruption();
1184 SYSCALL_ERROR(Interrupted);
1185 return -1;
1186 }
1187 if (totalWritten) {
1188 thread->setErrno(0);
1189 break;
1190 }
1191 if (backendError) {
1192 thread->clearInterruption();
1193 return -1;
1194 }
1195 if (!canBlock) {
1196 thread->clearInterruption();
1197 SYSCALL_ERROR(NoMoreProcesses);
1198 return -1;
1199 }
1200 break;
1201 }
1202
1203 thread->setErrno(0);
1204 totalWritten += amount;
1205 if (amount < requested || signalInterrupted ||
1206 thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1207 break;
1208 }
1209 }
1210
1211 thread->clearInterruption();
1212 return static_cast<ssize_t>(totalWritten);
1213}
1214
1215enum class VectorPayloadValidation {
1216 Full,
1217 AddressRange,
1218 CopyTime,
1219 CommitTime,
1220};
1221
1223 static constexpr size_t InlineCount = 8;
1224 struct iovec inlineVectors[InlineCount];
1226 struct iovec* vectors = nullptr;
1227};
1228
1229// Keep a page-sized regular-file transfer off the heap without a large stack buffer.
1230constexpr size_t SmallVectorIoBounceCapacity = 4096;
1231// Changing chunk sizes would change how much I/O precedes a later user-copy fault.
1232static_assert(SmallVectorIoBounceCapacity <= ScalarIoBounceCapacity &&
1233 SmallVectorIoBounceCapacity <= RegularReadBounceCapacity);
1234
1235static bool snapshotIoVectors(
1236 const struct iovec* userVectors, int vectorCount, bool writeOperation,
1237 IoVectorSnapshot& vectorOwner, size_t& totalLength,
1238 VectorPayloadValidation payloadValidation = VectorPayloadValidation::Full) {
1239 constexpr int MaximumIoVectors = 1024;
1240 if (vectorCount < 0 || vectorCount > MaximumIoVectors) {
1241 SYSCALL_ERROR(InvalidArgument);
1242 return false;
1243 }
1244
1245 totalLength = 0;
1246 if (!vectorCount) {
1247 return true;
1248 }
1249
1250 if (static_cast<size_t>(vectorCount) <= IoVectorSnapshot::InlineCount) {
1251 vectorOwner.vectors = vectorOwner.inlineVectors;
1252 } else {
1253 vectorOwner.heap = UniqueArray<struct iovec>::allocate(static_cast<size_t>(vectorCount));
1254 vectorOwner.vectors = vectorOwner.heap.get();
1255 }
1256 struct iovec* vectors = vectorOwner.vectors;
1257 if (!vectors) {
1258 SYSCALL_ERROR(OutOfMemory);
1259 return false;
1260 }
1261 if (!PosixSubsystem::copyFromUser(vectors, userVectors, static_cast<size_t>(vectorCount),
1262 sizeof(struct iovec))) {
1263 SYSCALL_ERROR(BadAddress);
1264 return false;
1265 }
1266
1267 const bool validatePayload = payloadValidation != VectorPayloadValidation::CopyTime &&
1268 payloadValidation != VectorPayloadValidation::CommitTime;
1269 const bool rangeOnly = payloadValidation == VectorPayloadValidation::AddressRange;
1270 const size_t access = writeOperation ? PosixSubsystem::SafeRead : PosixSubsystem::SafeWrite;
1271 for (int i = 0; i < vectorCount; ++i) {
1272 if (vectors[i].iov_len > static_cast<size_t>(INT_MAX) - totalLength) {
1273 SYSCALL_ERROR(InvalidArgument);
1274 return false;
1275 }
1276 if (vectors[i].iov_len && validatePayload) {
1277 const uintptr_t base = reinterpret_cast<uintptr_t>(vectors[i].iov_base);
1278 const bool valid = rangeOnly
1279 ? PosixSubsystem::checkUserAddressRange(base, vectors[i].iov_len, 1)
1280 : PosixSubsystem::checkUserBuffer(base, vectors[i].iov_len, 1, access);
1281 if (!valid) {
1282 SYSCALL_ERROR(BadAddress);
1283 return false;
1284 }
1285 }
1286 totalLength += vectors[i].iov_len;
1287 }
1288 return true;
1289}
1290
1291static int writeFileVectorElement(Thread* thread, const DescriptorLease& descriptor,
1292 FileDescriptor::PositionGuard* position, int statusFlags,
1293 File::WriteGuard& writeGuard, const void* buffer, size_t length,
1294 bool publishMetadata, bool reportError, bool& signalInterrupted,
1295 bool& deliverPipeSignal) {
1296 File* file = descriptor->getFile();
1297 const bool canBlock = !(statusFlags & O_NONBLOCK);
1298
1299 thread->setErrno(0);
1300 uint64_t written = 0;
1301 if (file->isSeekable()) {
1302 assert(position);
1303 uint64_t location = position->offset();
1304 written = (statusFlags & O_APPEND)
1305 ? writeGuard.append(length, reinterpret_cast<uintptr_t>(buffer), location,
1306 canBlock, publishMetadata)
1307 : writeGuard.write(location, length, reinterpret_cast<uintptr_t>(buffer),
1308 canBlock, publishMetadata);
1309 if (written) {
1310 position->setOffset(location + written);
1311 }
1312 } else if (ConsoleManager::instance().isConsole(file)) {
1313 written = descriptor->writeFile(0, length, reinterpret_cast<uintptr_t>(buffer), canBlock);
1314 } else {
1315 written =
1316 writeGuard.write(0, length, reinterpret_cast<uintptr_t>(buffer), canBlock, publishMetadata);
1317 }
1318
1319 signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
1320 const size_t backendError = thread->getErrno();
1321 thread->clearInterruption();
1322 if (!written && signalInterrupted && !descriptor->terminalHungUp()) {
1323 if (reportError) {
1324 SYSCALL_ERROR(Interrupted);
1325 } else {
1326 thread->setErrno(0);
1327 }
1328 return -1;
1329 }
1330 if (!written && backendError) {
1331 deliverPipeSignal = (file->isPipe() || file->isFifo()) && backendError == Error::BrokenPipe;
1332 if (!reportError) {
1333 thread->setErrno(0);
1334 }
1335 return -1;
1336 }
1337 thread->setErrno(0);
1338
1339 const bool pipeLike = file->isPipe() || file->isFifo();
1340 if (!canBlock && !written && length && (!pipeLike || Pipe::fromFile(file)->getReaderCount())) {
1341 if (reportError) {
1342 SYSCALL_ERROR(NoMoreProcesses);
1343 }
1344 return -1;
1345 }
1346 if (pipeLike && !written && length) {
1347 if (reportError) {
1348 SYSCALL_ERROR(BrokenPipe);
1349 }
1350 deliverPipeSignal = true;
1351 return -1;
1352 }
1353 return static_cast<int>(written);
1354}
1355
1356static int readFileVectorElement(Thread* thread, const DescriptorLease& descriptor,
1357 FileDescriptor::PositionGuard* position, int statusFlags,
1358 void* buffer, size_t length, bool reportError,
1359 bool& signalInterrupted) {
1360 File* file = descriptor->getFile();
1361 const bool canBlock = !(statusFlags & O_NONBLOCK);
1362
1363 thread->setErrno(0);
1364 uint64_t amount = 0;
1365 if (file->isSeekable()) {
1366 assert(position);
1367 amount = file->read(position->offset(), length, reinterpret_cast<uintptr_t>(buffer), canBlock);
1368 } else {
1369 amount = descriptor->readFile(0, length, reinterpret_cast<uintptr_t>(buffer), canBlock);
1370 }
1371
1372 signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
1373 const size_t backendError = thread->getErrno();
1374 thread->clearInterruption();
1375 if (!amount && signalInterrupted && !descriptor->terminalHungUp()) {
1376 if (reportError) {
1377 SYSCALL_ERROR(Interrupted);
1378 }
1379 return -1;
1380 }
1381 if (!amount && backendError) {
1382 if (!reportError) {
1383 thread->setErrno(0);
1384 }
1385 return -1;
1386 }
1387 return static_cast<int>(amount);
1388}
1389
1390static bool scatterEventFdValue(const struct iovec* vectors, int vectorCount, uint64_t value) {
1391 size_t copied = 0;
1392 const uint8_t* source = reinterpret_cast<const uint8_t*>(&value);
1393 for (int i = 0; i < vectorCount && copied < sizeof(value); ++i) {
1394 size_t fragment = vectors[i].iov_len;
1395 if (fragment > sizeof(value) - copied) {
1396 fragment = sizeof(value) - copied;
1397 }
1398 if (fragment && !PosixSubsystem::copyToUser(vectors[i].iov_base, source + copied, fragment)) {
1399 SYSCALL_ERROR(BadAddress);
1400 return false;
1401 }
1402 copied += fragment;
1403 }
1404 return copied == sizeof(value);
1405}
1406
1407static int posixWritev(int fd, const struct iovec* iov, int iovcnt, bool suppressAppend) {
1408 F_NOTICE("writev(" << fd << ", <iov>, " << iovcnt << ")");
1409
1410 Thread* thread = Processor::information().getCurrentThread();
1411 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
1412 if (!subsystem) {
1413 return -1;
1414 }
1415
1416 DescriptorLease descriptor;
1417 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
1418 SYSCALL_ERROR(BadFileDescriptor);
1419 return -1;
1420 }
1421 if (descriptor->getFile()) {
1422 const int accessFlags = descriptor->getAccessFlags();
1423 if ((accessFlags & O_PATH) || (accessFlags & O_ACCMODE) == O_RDONLY) {
1424 SYSCALL_ERROR(BadFileDescriptor);
1425 return -1;
1426 }
1427 }
1428
1429 VfsMountView::WriteLease mountWrite;
1430 if (descriptor->openingPath() && descriptor->getFile() &&
1431 descriptor->getFile()->supportsRegularFileOperations() &&
1432 !descriptor->getFile()->isBlockDevice() && !mountWrite.acquire(descriptor->openingPath())) {
1433 return -1;
1434 }
1435 const bool regularFile = descriptor->getFile() &&
1436 descriptor->getFile()->supportsRegularFileOperations() &&
1437 !descriptor->getFile()->isBlockDevice();
1438 const VectorPayloadValidation payloadValidation =
1439 regularFile ? VectorPayloadValidation::CopyTime : VectorPayloadValidation::Full;
1440 IoVectorSnapshot vectorOwner;
1441 size_t totalLength = 0;
1442 if (!snapshotIoVectors(iov, iovcnt, true, vectorOwner, totalLength, payloadValidation)) {
1443 return -1;
1444 }
1445 struct iovec* vectors = vectorOwner.vectors;
1446
1447 if (!iovcnt) {
1448 return 0;
1449 }
1450 if (!totalLength) {
1451 return 0;
1452 }
1453
1454 if (!regularFile && (descriptor->getTimerFdImpl() || descriptor->getSignalFdImpl() ||
1455 descriptor->getFanotifyImpl())) {
1456 SYSCALL_ERROR(InvalidArgument);
1457 return -1;
1458 }
1459
1460 SharedPointer<EventFd> eventFd =
1461 regularFile ? SharedPointer<EventFd>() : descriptor->getEventFdImpl();
1462 if (eventFd) {
1463 const bool canBlock = !(descriptor->getStatusFlags() & O_NONBLOCK);
1464 descriptor.reset();
1465
1466 int totalWritten = 0;
1467 for (int i = 0; i < iovcnt; ++i) {
1468 if (!vectors[i].iov_len) {
1469 continue;
1470 }
1471 if (vectors[i].iov_len != sizeof(uint64_t)) {
1472 SYSCALL_ERROR(InvalidArgument);
1473 return totalWritten ? totalWritten : -1;
1474 }
1475
1476 uint64_t value = 0;
1477 if (!PosixSubsystem::copyFromUser(&value, vectors[i].iov_base, sizeof(value))) {
1478 SYSCALL_ERROR(BadAddress);
1479 return totalWritten ? totalWritten : -1;
1480 }
1481
1482 const int written = eventFd->writeValue(value, canBlock);
1483 if (written < 0) {
1484 return totalWritten ? totalWritten : written;
1485 }
1486 totalWritten += written;
1487 }
1488 return totalWritten;
1489 }
1490
1491 if (descriptor->networkImpl) {
1492 struct msghdr message = {};
1493 message.msg_iov = vectors;
1494 message.msg_iovlen = static_cast<size_t>(iovcnt);
1495 return static_cast<int>(posix_sendmsg_descriptor(descriptor, &message));
1496 }
1497 if (!descriptor->getFile()) {
1498 SYSCALL_ERROR(InvalidArgument);
1499 return -1;
1500 }
1501 bool deliverPipeSignal = false;
1502 auto writeVector = [&](FileDescriptor::PositionGuard* position, int statusFlags) -> int {
1503 const bool pipeLike = descriptor->getFile()->isPipe() || descriptor->getFile()->isFifo();
1504 if (pipeLike && totalLength <= PIPE_BUF_MAX) {
1506 size_t offset = 0;
1507 for (int i = 0; i < iovcnt; ++i) {
1508 if (vectors[i].iov_len &&
1509 !PosixSubsystem::copyFromUser(aggregate.get() + offset, vectors[i].iov_base,
1510 vectors[i].iov_len)) {
1511 SYSCALL_ERROR(BadAddress);
1512 return -1;
1513 }
1514 offset += vectors[i].iov_len;
1515 }
1516 File::WriteGuard writeGuard = descriptor->getFile()->lockWrites();
1517 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1518 thread->clearInterruption();
1519 SYSCALL_ERROR(Interrupted);
1520 return -1;
1521 }
1522 bool signalInterrupted = false;
1523 return totalLength ? writeFileVectorElement(thread, descriptor, position, statusFlags,
1524 writeGuard, aggregate.get(), totalLength, true,
1525 true, signalInterrupted, deliverPipeSignal)
1526 : 0;
1527 }
1528
1529 const size_t bounceLimit = regularFile ? RegularWriteBounceCapacity : ScalarIoBounceCapacity;
1530 const size_t bounceCapacity = totalLength < bounceLimit ? totalLength : bounceLimit;
1531 uint8_t smallBounce[SmallVectorIoBounceCapacity];
1532 UniqueArray<uint8_t> bounceOwner;
1533 uint8_t* bounce = smallBounce;
1534 if (!regularFile || bounceCapacity > sizeof(smallBounce)) {
1535 bounceOwner = UniqueArray<uint8_t>::allocate(bounceCapacity);
1536 bounce = bounceOwner.get();
1537 }
1538 if (!bounce) {
1539 SYSCALL_ERROR(OutOfMemory);
1540 return -1;
1541 }
1542 File::WriteGuard writeGuard = descriptor->getFile()->lockWrites();
1543 for (int i = 0; i < iovcnt; ++i) {
1544 F_NOTICE("writev: iov[" << i << "] is @ " << vectors[i].iov_base << ", " << vectors[i].iov_len
1545 << " bytes.");
1546 }
1547
1548 int totalWritten = 0;
1549 int vectorIndex = 0;
1550 size_t vectorOffset = 0;
1551 while (static_cast<size_t>(totalWritten) < totalLength) {
1552 if (totalWritten && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1553 thread->clearInterruption();
1554 return totalWritten;
1555 }
1556
1557 const size_t remaining = totalLength - static_cast<size_t>(totalWritten);
1558 const size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
1559 size_t gathered = 0;
1560 while (gathered < requested) {
1561 while (vectorIndex < iovcnt && vectorOffset == vectors[vectorIndex].iov_len) {
1562 ++vectorIndex;
1563 vectorOffset = 0;
1564 }
1565 assert(vectorIndex < iovcnt);
1566
1567 const size_t vectorRemaining = vectors[vectorIndex].iov_len - vectorOffset;
1568 const size_t fragment =
1569 vectorRemaining < requested - gathered ? vectorRemaining : requested - gathered;
1570 const char* userSource = reinterpret_cast<const char*>(
1571 reinterpret_cast<uintptr_t>(vectors[vectorIndex].iov_base) + vectorOffset);
1572 if (!PosixSubsystem::copyFromUser(bounce + gathered, userSource, fragment)) {
1573 thread->clearInterruption();
1574 if (totalWritten) {
1575 return totalWritten;
1576 }
1577 SYSCALL_ERROR(BadAddress);
1578 return -1;
1579 }
1580 gathered += fragment;
1581 vectorOffset += fragment;
1582 }
1583
1584 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1585 thread->clearInterruption();
1586 if (totalWritten) {
1587 return totalWritten;
1588 }
1589 SYSCALL_ERROR(Interrupted);
1590 return -1;
1591 }
1592
1593 bool signalInterrupted = false;
1594 const int r = writeFileVectorElement(thread, descriptor, position, statusFlags, writeGuard,
1595 bounce, requested, !regularFile, totalWritten == 0,
1596 signalInterrupted, deliverPipeSignal);
1597 if (r < 0) {
1598 return totalWritten ? totalWritten : r;
1599 }
1600
1601 totalWritten += r;
1602 if (static_cast<size_t>(r) < requested || signalInterrupted) {
1603 return totalWritten;
1604 }
1605 }
1606
1607 return totalWritten;
1608 };
1609
1610 thread->clearInterruption();
1611 int result = 0;
1612 if (descriptor->getFile()->isSeekable()) {
1613 {
1614 FileDescriptor::PositionGuard position = descriptor->lockPosition();
1615 int statusFlags = position.statusFlags();
1616 if (suppressAppend) {
1617 statusFlags &= ~O_APPEND;
1618 }
1619 result = writeVector(&position, statusFlags);
1620 }
1621 } else {
1622 // A blocking nonseekable write must not hold the OFD metadata mutex needed
1623 // by a reader using the same O_RDWR description.
1624 result = writeVector(nullptr, descriptor->getStatusFlags());
1625 }
1626
1627 if (deliverPipeSignal) {
1628 subsystem->threadException(thread, Subsystem::Pipe);
1629 }
1630 return result;
1631}
1632
1633int posix_writev(int fd, const struct iovec* iov, int iovcnt) {
1634 return posixWritev(fd, iov, iovcnt, false);
1635}
1636
1637int posix_readv(int fd, const struct iovec* iov, int iovcnt) {
1638 F_NOTICE("readv(" << fd << ", <iov>, " << iovcnt << ")");
1639
1640 Thread* thread = Processor::information().getCurrentThread();
1641 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
1642 if (!subsystem) {
1643 return -1;
1644 }
1645
1646 DescriptorLease descriptor;
1647 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
1648 SYSCALL_ERROR(BadFileDescriptor);
1649 return -1;
1650 }
1651 if (descriptor->getFile()) {
1652 const int accessFlags = descriptor->getAccessFlags();
1653 if ((accessFlags & O_PATH) || (accessFlags & O_ACCMODE) == O_WRONLY) {
1654 SYSCALL_ERROR(BadFileDescriptor);
1655 return -1;
1656 }
1657 }
1658 const bool regularFile = descriptor->getFile() &&
1659 descriptor->getFile()->supportsRegularFileOperations() &&
1660 !descriptor->getFile()->isBlockDevice();
1661 auto timerFd = regularFile ? SharedPointer<TimerFd>() : descriptor->getTimerFdImpl();
1662 auto signalFd = regularFile ? SharedPointer<SignalFd>() : descriptor->getSignalFdImpl();
1663 auto fanotify = regularFile ? SharedPointer<FanotifyInstance>() : descriptor->getFanotifyImpl();
1664 VectorPayloadValidation payloadValidation =
1665 regularFile ? VectorPayloadValidation::CopyTime : VectorPayloadValidation::Full;
1666 if (timerFd || signalFd || fanotify) {
1667 payloadValidation = VectorPayloadValidation::CommitTime;
1668 }
1669 IoVectorSnapshot vectorOwner;
1670 size_t totalLength = 0;
1671 // Record readers validate each destination at commit time. A later fault
1672 // must not suppress complete records copied before it.
1673 if (!snapshotIoVectors(iov, iovcnt, false, vectorOwner, totalLength, payloadValidation)) {
1674 return -1;
1675 }
1676 struct iovec* vectors = vectorOwner.vectors;
1677 if (!iovcnt) {
1678 return 0;
1679 }
1680 if (!totalLength) {
1681 return 0;
1682 }
1683
1684 if (timerFd || signalFd || fanotify) {
1685 const bool canBlock = !(descriptor->getStatusFlags() & O_NONBLOCK);
1686 descriptor.reset();
1687 struct Scatter {
1688 struct iovec* vectors;
1689 int count;
1690 int index = 0;
1691 size_t offset = 0;
1692 } scatter{vectors, iovcnt};
1693 auto copy = [](void* context, const void* data, size_t size) -> bool {
1694 auto& cursor = *static_cast<Scatter*>(context);
1695 auto* bytes = static_cast<const uint8_t*>(data);
1696 while (size) {
1697 while (cursor.index < cursor.count &&
1698 cursor.offset == cursor.vectors[cursor.index].iov_len) {
1699 ++cursor.index;
1700 cursor.offset = 0;
1701 }
1702 if (cursor.index == cursor.count) {
1703 SYSCALL_ERROR(BadAddress);
1704 return false;
1705 }
1706 const struct iovec& vector = cursor.vectors[cursor.index];
1707 const size_t available = vector.iov_len - cursor.offset;
1708 const size_t amount = size < available ? size : available;
1709 const uintptr_t base = reinterpret_cast<uintptr_t>(vector.iov_base);
1710 if (cursor.offset > ~uintptr_t(0) - base) {
1711 SYSCALL_ERROR(BadAddress);
1712 return false;
1713 }
1714 void* destination = reinterpret_cast<void*>(base + cursor.offset);
1715 if (!PosixSubsystem::copyToUser(destination, bytes, amount)) {
1716 SYSCALL_ERROR(BadAddress);
1717 return false;
1718 }
1719 cursor.offset += amount;
1720 bytes += amount;
1721 size -= amount;
1722 }
1723 return true;
1724 };
1725 return timerFd ? timerFd->readWithCopy(totalLength, canBlock, copy, &scatter)
1726 : signalFd ? signalFd->readWithCopy(totalLength, canBlock, copy, &scatter)
1727 : fanotify->readWithCopy(totalLength, canBlock, copy, &scatter);
1728 }
1729
1730 SharedPointer<EventFd> eventFd =
1731 regularFile ? SharedPointer<EventFd>() : descriptor->getEventFdImpl();
1732 if (eventFd) {
1733 if (totalLength < sizeof(uint64_t)) {
1734 SYSCALL_ERROR(InvalidArgument);
1735 return -1;
1736 }
1737
1738 const bool canBlock = !(descriptor->getStatusFlags() & O_NONBLOCK);
1739 descriptor.reset();
1740 uint64_t value = 0;
1741 const int result = eventFd->readValue(value, canBlock);
1742 if (result < 0) {
1743 return result;
1744 }
1745 return scatterEventFdValue(vectors, iovcnt, value) ? result : -1;
1746 }
1747
1748 if (descriptor->networkImpl) {
1749 struct msghdr message = {};
1750 message.msg_iov = vectors;
1751 message.msg_iovlen = static_cast<size_t>(iovcnt);
1752 return static_cast<int>(posix_recvmsg_descriptor(descriptor, &message));
1753 }
1754 if (!descriptor->getFile()) {
1755 SYSCALL_ERROR(InvalidArgument);
1756 return -1;
1757 }
1758 if (descriptor->getFile()->isDirectory()) {
1759 SYSCALL_ERROR(IsADirectory);
1760 return -1;
1761 }
1762
1763 auto readVector = [&](FileDescriptor::PositionGuard* position, int statusFlags) -> int {
1764 const bool pipeLike = descriptor->getFile()->isPipe() || descriptor->getFile()->isFifo();
1765 if (pipeLike && totalLength) {
1766 const size_t readCapacity = totalLength < PIPE_BUF_MAX ? totalLength : PIPE_BUF_MAX;
1767 UniqueArray<uint8_t> aggregate = UniqueArray<uint8_t>::allocate(readCapacity);
1768 if (statusFlags & O_NONBLOCK) {
1769 const ReadyMask ready =
1770 descriptor->acquireOpenFileDescription()->queryFileReady(true, false);
1771 if (!(ready & (ReadyRead | ReadyError | ReadyHangup))) {
1772 SYSCALL_ERROR(NoMoreProcesses);
1773 return -1;
1774 }
1775 }
1776 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1777 thread->clearInterruption();
1778 SYSCALL_ERROR(Interrupted);
1779 return -1;
1780 }
1781 bool signalInterrupted = false;
1782 const int amount =
1783 readFileVectorElement(thread, descriptor, position, statusFlags, aggregate.get(),
1784 readCapacity, true, signalInterrupted);
1785 if (amount <= 0) {
1786 return amount;
1787 }
1788
1789 size_t copied = 0;
1790 for (int i = 0; i < iovcnt && copied < static_cast<size_t>(amount); ++i) {
1791 size_t fragment = vectors[i].iov_len;
1792 if (fragment > static_cast<size_t>(amount) - copied) {
1793 fragment = static_cast<size_t>(amount) - copied;
1794 }
1795 if (fragment &&
1796 !PosixSubsystem::copyToUser(vectors[i].iov_base, aggregate.get() + copied, fragment)) {
1797 if (copied) {
1798 return static_cast<int>(copied);
1799 }
1800 SYSCALL_ERROR(BadAddress);
1801 return -1;
1802 }
1803 copied += fragment;
1804 }
1805 return static_cast<int>(copied);
1806 }
1807
1808 if (regularFile && position) {
1809 uint8_t smallBounce[SmallVectorIoBounceCapacity];
1810 size_t bounceCapacity = totalLength;
1811 UniqueArray<uint8_t> bounceOwner;
1812 uint8_t* bounce = smallBounce;
1813 if (totalLength > sizeof(smallBounce)) {
1814 bounceOwner = allocateReadBounce(descriptor->getFile(), totalLength, bounceCapacity);
1815 bounce = bounceOwner.get();
1816 }
1817 if (!bounce) {
1818 SYSCALL_ERROR(OutOfMemory);
1819 return -1;
1820 }
1821
1822 size_t totalRead = 0;
1823 int vectorIndex = 0;
1824 size_t vectorOffset = 0;
1825 while (totalRead < totalLength) {
1826 if (totalRead && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1827 thread->clearInterruption();
1828 return static_cast<int>(totalRead);
1829 }
1830
1831 const size_t remaining = totalLength - totalRead;
1832 const size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
1833 bool signalInterrupted = false;
1834 const int amount = readFileVectorElement(thread, descriptor, position, statusFlags, bounce,
1835 requested, totalRead == 0, signalInterrupted);
1836 if (amount < 0) {
1837 return totalRead ? static_cast<int>(totalRead) : amount;
1838 }
1839 if (!amount) {
1840 return static_cast<int>(totalRead);
1841 }
1842
1843 size_t copied = 0;
1844 while (copied < static_cast<size_t>(amount)) {
1845 while (vectorIndex < iovcnt && vectorOffset == vectors[vectorIndex].iov_len) {
1846 ++vectorIndex;
1847 vectorOffset = 0;
1848 }
1849 assert(vectorIndex < iovcnt);
1850
1851 const size_t vectorRemaining = vectors[vectorIndex].iov_len - vectorOffset;
1852 const size_t fragment = vectorRemaining < static_cast<size_t>(amount) - copied
1853 ? vectorRemaining
1854 : static_cast<size_t>(amount) - copied;
1855 void* userDestination = reinterpret_cast<void*>(
1856 reinterpret_cast<uintptr_t>(vectors[vectorIndex].iov_base) + vectorOffset);
1857 if (!PosixSubsystem::copyToUser(userDestination, bounce + copied, fragment)) {
1858 thread->clearInterruption();
1859 if (totalRead) {
1860 return static_cast<int>(totalRead);
1861 }
1862 SYSCALL_ERROR(BadAddress);
1863 return -1;
1864 }
1865
1866 copied += fragment;
1867 vectorOffset += fragment;
1868 position->advanceOffset(fragment);
1869 totalRead += fragment;
1870 }
1871
1872 if (static_cast<size_t>(amount) < requested || signalInterrupted) {
1873 return static_cast<int>(totalRead);
1874 }
1875 }
1876
1877 return static_cast<int>(totalRead);
1878 }
1879
1880 size_t bounceCapacity = 0;
1881 UniqueArray<uint8_t> bounce =
1882 allocateReadBounce(descriptor->getFile(), totalLength, bounceCapacity);
1883 if (!bounce) {
1884 SYSCALL_ERROR(OutOfMemory);
1885 return -1;
1886 }
1887 int totalRead = 0;
1888 for (int i = 0; i < iovcnt; ++i) {
1889 F_NOTICE("readv: iov[" << i << "] is @ " << vectors[i].iov_base << ", " << vectors[i].iov_len
1890 << " bytes.");
1891
1892 if (!vectors[i].iov_len) {
1893 continue;
1894 }
1895
1896 size_t vectorOffset = 0;
1897 while (vectorOffset < vectors[i].iov_len) {
1898 if (totalRead && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1899 thread->clearInterruption();
1900 return totalRead;
1901 }
1902
1903 const size_t remaining = vectors[i].iov_len - vectorOffset;
1904 size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
1905 void* userDestination = reinterpret_cast<void*>(
1906 reinterpret_cast<uintptr_t>(vectors[i].iov_base) + vectorOffset);
1907 // Regular-file reads do not consume the OFD offset until the copy has
1908 // succeeded. Let copyToUser perform the only authoritative mapping
1909 // check immediately before publishing bytes to userspace.
1910 if (!regularFile && !checkReadDestination(userDestination, requested)) {
1911 thread->clearInterruption();
1912 if (totalRead) {
1913 return totalRead;
1914 }
1915 SYSCALL_ERROR(BadAddress);
1916 return -1;
1917 }
1918
1919 // Once a nonseekable source has produced data, do not turn another
1920 // bounce chunk into a second blocking operation.
1921 const bool operationCanBlock = !(statusFlags & O_NONBLOCK) && (position || !totalRead);
1922 if (!operationCanBlock) {
1923 const ReadyMask ready =
1924 descriptor->acquireOpenFileDescription()->queryFileReady(true, false);
1925 if (!(ready & (ReadyRead | ReadyError | ReadyHangup))) {
1926 if (totalRead) {
1927 return totalRead;
1928 }
1929 thread->clearInterruption();
1930 SYSCALL_ERROR(NoMoreProcesses);
1931 return -1;
1932 }
1933 }
1934
1935 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
1936 thread->clearInterruption();
1937 if (totalRead) {
1938 return totalRead;
1939 }
1940 SYSCALL_ERROR(Interrupted);
1941 return -1;
1942 }
1943
1944 const int elementFlags = operationCanBlock ? statusFlags : statusFlags | O_NONBLOCK;
1945 bool signalInterrupted = false;
1946 const int r =
1947 readFileVectorElement(thread, descriptor, position, elementFlags, bounce.get(),
1948 requested, totalRead == 0, signalInterrupted);
1949 if (r < 0) {
1950 return totalRead ? totalRead : r;
1951 }
1952 if (!r) {
1953 return totalRead;
1954 }
1955
1956 if (!PosixSubsystem::copyToUser(userDestination, bounce.get(), static_cast<size_t>(r))) {
1957 thread->clearInterruption();
1958 if (totalRead) {
1959 return totalRead;
1960 }
1961 SYSCALL_ERROR(BadAddress);
1962 return -1;
1963 }
1964
1965 if (position) {
1966 position->advanceOffset(static_cast<size_t>(r));
1967 }
1968 totalRead += r;
1969 vectorOffset += static_cast<size_t>(r);
1970 if (static_cast<size_t>(r) < requested || signalInterrupted) {
1971 return totalRead;
1972 }
1973 }
1974 }
1975
1976 return totalRead;
1977 };
1978
1979 thread->clearInterruption();
1980 if (descriptor->getFile()->isSeekable()) {
1981 FileDescriptor::PositionGuard position = descriptor->lockPosition();
1982 return readVector(&position, position.statusFlags());
1983 }
1984
1985 // See the matching writev path: a blocking nonseekable read must not
1986 // monopolize the OFD metadata mutex needed by its peer.
1987 return readVector(nullptr, descriptor->getStatusFlags());
1988}
1989
1990namespace {
1991constexpr int LinuxRwfNoAppend = 0x20;
1992
1993ssize_t positionalReadVector(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
1994 IoVectorSnapshot vectorOwner;
1995 size_t totalLength = 0;
1996 if (!snapshotIoVectors(iov, iovcnt, false, vectorOwner, totalLength,
1997 VectorPayloadValidation::AddressRange)) {
1998 return -1;
1999 }
2000 if (!positionalIoRangeIsValid(offset, totalLength)) {
2001 SYSCALL_ERROR(InvalidArgument);
2002 return -1;
2003 }
2004
2005 Thread* thread = Processor::information().getCurrentThread();
2006 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
2007 if (!subsystem) {
2008 return -1;
2009 }
2010
2011 DescriptorLease descriptor;
2012 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
2013 SYSCALL_ERROR(BadFileDescriptor);
2014 return -1;
2015 }
2016 if (!descriptor->getFile() || !descriptor->getFile()->isSeekable()) {
2017 SYSCALL_ERROR(IllegalSeek);
2018 return -1;
2019 }
2020
2021 const int statusFlags = descriptor->getStatusFlags();
2022 if ((statusFlags & O_PATH) || (statusFlags & O_ACCMODE) == O_WRONLY) {
2023 SYSCALL_ERROR(BadFileDescriptor);
2024 return -1;
2025 }
2026 if (descriptor->getFile()->isDirectory()) {
2027 SYSCALL_ERROR(IsADirectory);
2028 return -1;
2029 }
2030 if (!iovcnt || !totalLength) {
2031 return 0;
2032 }
2033
2034 struct iovec* vectors = vectorOwner.vectors;
2035 size_t bounceCapacity = 0;
2036 UniqueArray<uint8_t> bounce =
2037 allocateReadBounce(descriptor->getFile(), totalLength, bounceCapacity);
2038 if (!bounce) {
2039 SYSCALL_ERROR(OutOfMemory);
2040 return -1;
2041 }
2042 const bool canBlock = !(statusFlags & O_NONBLOCK);
2043 const uint64_t startingOffset = static_cast<uint64_t>(offset);
2044 size_t totalRead = 0;
2045
2046 thread->clearInterruption();
2047 for (int i = 0; i < iovcnt; ++i) {
2048 size_t vectorOffset = 0;
2049 while (vectorOffset < vectors[i].iov_len) {
2050 if (totalRead && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2051 thread->clearInterruption();
2052 return static_cast<ssize_t>(totalRead);
2053 }
2054
2055 const size_t remaining = vectors[i].iov_len - vectorOffset;
2056 size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
2057 void* userDestination =
2058 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(vectors[i].iov_base) + vectorOffset);
2059 if (!checkReadDestination(userDestination, requested)) {
2060 thread->clearInterruption();
2061 if (totalRead) {
2062 return static_cast<ssize_t>(totalRead);
2063 }
2064 SYSCALL_ERROR(BadAddress);
2065 return -1;
2066 }
2067
2068 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2069 thread->clearInterruption();
2070 if (totalRead) {
2071 return static_cast<ssize_t>(totalRead);
2072 }
2073 SYSCALL_ERROR(Interrupted);
2074 return -1;
2075 }
2076
2077 thread->setErrno(0);
2078 const uint64_t amount =
2079 descriptor->getFile()->read(startingOffset + totalRead, requested,
2080 reinterpret_cast<uintptr_t>(bounce.get()), canBlock);
2081 const bool signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
2082 const size_t backendError = thread->getErrno();
2083 if (!amount) {
2084 thread->clearInterruption();
2085 if (!totalRead && signalInterrupted) {
2086 SYSCALL_ERROR(Interrupted);
2087 return -1;
2088 }
2089 if (backendError) {
2090 if (!totalRead) {
2091 return -1;
2092 }
2093 thread->setErrno(0);
2094 }
2095 return static_cast<ssize_t>(totalRead);
2096 }
2097
2098 if (!PosixSubsystem::copyToUser(userDestination, bounce.get(), amount)) {
2099 thread->clearInterruption();
2100 if (totalRead) {
2101 return static_cast<ssize_t>(totalRead);
2102 }
2103 SYSCALL_ERROR(BadAddress);
2104 return -1;
2105 }
2106
2107 totalRead += static_cast<size_t>(amount);
2108 vectorOffset += static_cast<size_t>(amount);
2109 if (amount < requested || signalInterrupted ||
2110 thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2111 thread->clearInterruption();
2112 return static_cast<ssize_t>(totalRead);
2113 }
2114 }
2115 }
2116
2117 thread->clearInterruption();
2118 return static_cast<ssize_t>(totalRead);
2119}
2120
2121ssize_t positionalWriteVector(int fd, const struct iovec* iov, int iovcnt, off_t offset,
2122 bool honorAppend) {
2123 IoVectorSnapshot vectorOwner;
2124 size_t totalLength = 0;
2125 if (!snapshotIoVectors(iov, iovcnt, true, vectorOwner, totalLength,
2126 VectorPayloadValidation::AddressRange)) {
2127 return -1;
2128 }
2129 if (!positionalIoRangeIsValid(offset, totalLength)) {
2130 SYSCALL_ERROR(InvalidArgument);
2131 return -1;
2132 }
2133
2134 Thread* thread = Processor::information().getCurrentThread();
2135 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
2136 if (!subsystem) {
2137 return -1;
2138 }
2139
2140 DescriptorLease descriptor;
2141 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
2142 SYSCALL_ERROR(BadFileDescriptor);
2143 return -1;
2144 }
2145 if (!descriptor->getFile() || !descriptor->getFile()->isSeekable()) {
2146 SYSCALL_ERROR(IllegalSeek);
2147 return -1;
2148 }
2149
2150 VfsMountView::WriteLease mountWrite;
2151 if (descriptor->openingPath() && descriptor->getFile() &&
2152 descriptor->getFile()->supportsRegularFileOperations() &&
2153 !descriptor->getFile()->isBlockDevice() && !mountWrite.acquire(descriptor->openingPath())) {
2154 return -1;
2155 }
2156 const int statusFlags = descriptor->getStatusFlags();
2157 if ((statusFlags & O_PATH) || (statusFlags & O_ACCMODE) == O_RDONLY) {
2158 SYSCALL_ERROR(BadFileDescriptor);
2159 return -1;
2160 }
2161 if (descriptor->getFile()->isDirectory()) {
2162 SYSCALL_ERROR(IsADirectory);
2163 return -1;
2164 }
2165 if (!iovcnt || !totalLength) {
2166 return 0;
2167 }
2168
2169 struct iovec* vectors = vectorOwner.vectors;
2170 const size_t bounceCapacity =
2171 totalLength < ScalarIoBounceCapacity ? totalLength : ScalarIoBounceCapacity;
2173 const bool canBlock = !(statusFlags & O_NONBLOCK);
2174 const uint64_t startingOffset = static_cast<uint64_t>(offset);
2175 const bool append = honorAppend && (statusFlags & O_APPEND);
2176 File::WriteGuard writeGuard = descriptor->getFile()->lockWrites();
2177 size_t totalWritten = 0;
2178 int vectorIndex = 0;
2179 size_t vectorOffset = 0;
2180
2181 thread->clearInterruption();
2182 while (totalWritten < totalLength) {
2183 if (totalWritten && thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2184 thread->clearInterruption();
2185 return static_cast<ssize_t>(totalWritten);
2186 }
2187
2188 const size_t remaining = totalLength - totalWritten;
2189 const size_t requested = remaining < bounceCapacity ? remaining : bounceCapacity;
2190 size_t gathered = 0;
2191 while (gathered < requested) {
2192 while (vectorIndex < iovcnt && vectorOffset == vectors[vectorIndex].iov_len) {
2193 ++vectorIndex;
2194 vectorOffset = 0;
2195 }
2196 assert(vectorIndex < iovcnt);
2197
2198 const size_t vectorRemaining = vectors[vectorIndex].iov_len - vectorOffset;
2199 const size_t fragment =
2200 vectorRemaining < requested - gathered ? vectorRemaining : requested - gathered;
2201 const void* userSource = reinterpret_cast<const void*>(
2202 reinterpret_cast<uintptr_t>(vectors[vectorIndex].iov_base) + vectorOffset);
2203 if (!PosixSubsystem::copyFromUser(bounce.get() + gathered, userSource, fragment)) {
2204 thread->clearInterruption();
2205 if (totalWritten) {
2206 return static_cast<ssize_t>(totalWritten);
2207 }
2208 SYSCALL_ERROR(BadAddress);
2209 return -1;
2210 }
2211 gathered += fragment;
2212 vectorOffset += fragment;
2213 }
2214
2215 if (thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2216 thread->clearInterruption();
2217 if (totalWritten) {
2218 return static_cast<ssize_t>(totalWritten);
2219 }
2220 SYSCALL_ERROR(Interrupted);
2221 return -1;
2222 }
2223
2224 thread->setErrno(0);
2225 uint64_t location = startingOffset + totalWritten;
2226 const uint64_t amount =
2227 append ? writeGuard.append(requested, reinterpret_cast<uintptr_t>(bounce.get()), location,
2228 canBlock)
2229 : writeGuard.write(location, requested, reinterpret_cast<uintptr_t>(bounce.get()),
2230 canBlock);
2231 const bool signalInterrupted = thread->getInterruptionReason() == Thread::InterruptedBySignal;
2232 const size_t backendError = thread->getErrno();
2233 if (!amount) {
2234 thread->clearInterruption();
2235 if (!totalWritten && signalInterrupted) {
2236 SYSCALL_ERROR(Interrupted);
2237 return -1;
2238 }
2239 if (totalWritten) {
2240 thread->setErrno(0);
2241 return static_cast<ssize_t>(totalWritten);
2242 }
2243 if (backendError) {
2244 return -1;
2245 }
2246 if (!canBlock) {
2247 SYSCALL_ERROR(NoMoreProcesses);
2248 return -1;
2249 }
2250 return static_cast<ssize_t>(totalWritten);
2251 }
2252
2253 thread->setErrno(0);
2254 totalWritten += static_cast<size_t>(amount);
2255 if (amount < requested || signalInterrupted ||
2256 thread->getInterruptionReason() == Thread::InterruptedBySignal) {
2257 thread->clearInterruption();
2258 return static_cast<ssize_t>(totalWritten);
2259 }
2260 }
2261
2262 thread->clearInterruption();
2263 return static_cast<ssize_t>(totalWritten);
2264}
2265} // namespace
2266
2267ssize_t posix_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
2268 F_NOTICE("preadv(" << fd << ", <iov>, " << iovcnt << ", " << offset << ")");
2269 return positionalReadVector(fd, iov, iovcnt, offset);
2270}
2271
2272ssize_t posix_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
2273 F_NOTICE("pwritev(" << fd << ", <iov>, " << iovcnt << ", " << offset << ")");
2274 return positionalWriteVector(fd, iov, iovcnt, offset, false);
2275}
2276
2277ssize_t posix_preadv2(int fd, const struct iovec* iov, int iovcnt, off_t offset, int flags) {
2278 F_NOTICE("preadv2(" << fd << ", <iov>, " << iovcnt << ", " << offset << ", " << flags << ")");
2279 if (flags) {
2280 SYSCALL_ERROR(OperationNotSupported);
2281 return -1;
2282 }
2283 if (offset == -1) {
2284 return posix_readv(fd, iov, iovcnt);
2285 }
2286 return positionalReadVector(fd, iov, iovcnt, offset);
2287}
2288
2289ssize_t posix_pwritev2(int fd, const struct iovec* iov, int iovcnt, off_t offset, int flags) {
2290 F_NOTICE("pwritev2(" << fd << ", <iov>, " << iovcnt << ", " << offset << ", " << flags << ")");
2291 if (flags & ~LinuxRwfNoAppend) {
2292 SYSCALL_ERROR(OperationNotSupported);
2293 return -1;
2294 }
2295 if (offset == -1) {
2296 return posixWritev(fd, iov, iovcnt, flags & LinuxRwfNoAppend);
2297 }
2298 return positionalWriteVector(fd, iov, iovcnt, offset, !(flags & LinuxRwfNoAppend));
2299}
2300
2301off_t posix_lseek(int file, off_t ptr, int dir) {
2302 F_NOTICE("lseek(" << file << ", " << ptr << ", " << dir << ")");
2303
2304 // Lookup this process.
2305 Process* pProcess = Processor::information().getCurrentThread()->getParent();
2306 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
2307 if (!pSubsystem) {
2308 ERROR("No subsystem for this process!");
2309 return -1;
2310 }
2311
2312 DescriptorLease pFd;
2313 if (!pSubsystem->acquireFileDescriptor(file, pFd)) {
2314 // Error - no such file descriptor.
2315 SYSCALL_ERROR(BadFileDescriptor);
2316 return -1;
2317 }
2318
2320 if (position.statusFlags() & O_PATH) {
2321 SYSCALL_ERROR(BadFileDescriptor);
2322 return -1;
2323 }
2324
2325 if (position.isNoopSeekEndpoint()) {
2326 if (dir < SEEK_SET || dir > 4) {
2327 SYSCALL_ERROR(InvalidArgument);
2328 return -1;
2329 }
2330 // Linux assigns these anonymous objects noop_llseek: offsets stay zero.
2331 return 0;
2332 }
2333
2334 File* fileObject = position.file();
2335 if (fileObject && fileObject->isSeekable()) {
2336 switch (dir) {
2337 case SEEK_SET:
2338 position.setOffset(ptr);
2339 break;
2340 case SEEK_CUR:
2341 position.advanceOffset(ptr);
2342 break;
2343 case SEEK_END:
2344 position.setOffset(fileObject->getSize() + ptr);
2345 break;
2346 }
2347
2348 return static_cast<off_t>(position.offset());
2349 }
2350
2351 if (!fileObject) {
2352 SYSCALL_ERROR(IllegalSeek);
2353 return -1;
2354 }
2355
2356 if (!fileObject->isSeekable()) {
2357 SYSCALL_ERROR(IllegalSeek);
2358 return -1;
2359 }
2360
2361 return -1;
2362}
2363
2364int posix_link(char* target, char* link) {
2365 return posix_linkat(AT_FDCWD, target, AT_FDCWD, link, AT_SYMLINK_FOLLOW);
2366}
2367
2368int posix_readlink(const char* path, char* buf, unsigned int bufsize) {
2369 return posix_readlinkat(AT_FDCWD, path, buf, bufsize);
2370}
2371
2372int posix_realpath(const char* path, char* buf, size_t bufsize) {
2373 F_NOTICE("realpath");
2374
2375 String pathCopy;
2376 if (!copyUserString(path, pathCopy)) {
2377 F_NOTICE("realpath -> invalid address");
2378 return -1;
2379 }
2380 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(buf), bufsize,
2381 PosixSubsystem::SafeWrite)) {
2382 F_NOTICE("realpath -> invalid address");
2383 SYSCALL_ERROR(BadAddress);
2384 return -1;
2385 }
2386
2387 String realPath;
2388 normalisePath(realPath, pathCopy.cstr());
2389 F_NOTICE(" -> traversing " << realPath);
2390 Process* process = Processor::information().getCurrentThread()->getParent();
2391 ResolvedPath fileLease;
2392 File* f = findFilePath(realPath, fileLease);
2393 if (!f) {
2394 SYSCALL_ERROR(DoesNotExist);
2395 return -1;
2396 }
2397
2398 f = traverseSymlink(f, fileLease);
2399 if (!f) {
2400 SYSCALL_ERROR(DoesNotExist);
2401 return -1;
2402 }
2403
2404 if (!f->isDirectory()) {
2405 SYSCALL_ERROR(NotADirectory);
2406 return -1;
2407 }
2408
2409 String actualPath;
2410 auto context = process->acquireFilesystemContext();
2412 auto* view = VFS::instance().mountView();
2413 if (!context || !context->snapshot(snapshot) || !view ||
2414 !view->formatPath(snapshot, fileLease.path(), actualPath))
2415 return -1;
2416 if (actualPath.length() >= bufsize) {
2417 SYSCALL_ERROR(NameTooLong);
2418 return -1;
2419 }
2420
2421 // File is good, copy it now.
2422 F_NOTICE(" -> returning " << actualPath);
2423 if (!PosixSubsystem::copyToUser(buf, actualPath.cstr(), actualPath.length() + 1)) {
2424 SYSCALL_ERROR(BadAddress);
2425 return -1;
2426 }
2427
2428 return 0;
2429}
2430
2431int posix_unlink(char* name) {
2432 return posix_unlinkat(AT_FDCWD, name, 0);
2433}
2434
2435int posix_symlink(char* target, char* link) {
2436 return posix_symlinkat(target, AT_FDCWD, link);
2437}
2438
2439int posix_rename(const char* source, const char* dst) {
2440 return posix_renameat(AT_FDCWD, source, AT_FDCWD, dst);
2441}
2442
2443int posix_getcwd(char* buf, size_t maxlen) {
2444 F_NOTICE("getcwd(" << maxlen << ")");
2445
2446 TerminationDeferral lifetime;
2447 Process* process = Processor::information().getCurrentThread()->getParent();
2448 auto context = process->acquireFilesystemContext();
2450 if (!context || !context->snapshot(snapshot) || !snapshot.cwd) {
2451 SYSCALL_ERROR(DoesNotExist);
2452 return -1;
2453 }
2454 File* cwd = snapshot.cwd->node();
2455 if (!cwd || !cwd->isDirectory() || Directory::fromFile(cwd)->isDetached()) {
2456 SYSCALL_ERROR(DoesNotExist);
2457 return -1;
2458 }
2459 String str;
2460 auto* view = VFS::instance().mountView();
2461 if (!view || !view->formatPath(snapshot, snapshot.cwd, str))
2462 return -1;
2463
2464 size_t maxLength = str.length();
2465 if (maxLength >= maxlen) {
2466 // Too long.
2467 SYSCALL_ERROR(BadRange);
2468 return -1;
2469 }
2470 if (!PosixSubsystem::copyToUser(buf, str.cstr(), maxLength + 1)) {
2471 SYSCALL_ERROR(BadAddress);
2472 return -1;
2473 }
2474
2475 F_NOTICE(" -> " << str);
2476
2477 return maxLength + 1; // include null terminator
2478}
2479
2480int posix_stat(const char* name, struct stat* st) {
2481 F_NOTICE("stat(" << name << ") => fstatat");
2482 return posix_fstatat(AT_FDCWD, name, st, 0);
2483}
2484
2485int posix_fstat(int fd, struct stat* st) {
2486 F_NOTICE("fstat(" << fd << ") => fstatat");
2487 return posix_fstatat(fd, 0, st, AT_EMPTY_PATH);
2488}
2489
2490int posix_lstat(char* name, struct stat* st) {
2491 F_NOTICE("lstat(" << name << ") => fstatat");
2492 return posix_fstatat(AT_FDCWD, name, st, AT_SYMLINK_NOFOLLOW);
2493}
2494
2495static int getdents_common(int fd,
2496 size_t (*set_dent)(const Directory::DirectoryEntryView&, void*, size_t),
2497 void* buffer, int count) {
2498 // Lookup this process.
2499 Process* pProcess = Processor::information().getCurrentThread()->getParent();
2500 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
2501 if (!pSubsystem) {
2502 ERROR("No subsystem for this process!");
2503 return -1;
2504 }
2505
2506 DescriptorLease pFd;
2507 if (!pSubsystem->acquireFileDescriptor(fd, pFd) || !pFd->getFile()) {
2508 // Error - no such file descriptor.
2509 F_NOTICE(" -> bad file");
2510 SYSCALL_ERROR(BadFileDescriptor);
2511 return -1;
2512 }
2513
2514 if (pFd->getStatusFlags() & O_PATH) {
2515 SYSCALL_ERROR(BadFileDescriptor);
2516 return -1;
2517 }
2518
2519 if (!pFd->getFile()->isDirectory()) {
2520 F_NOTICE(" -> not a directory");
2521 SYSCALL_ERROR(NotADirectory);
2522 return -1;
2523 }
2524
2525 if (!count) {
2526 F_NOTICE(" -> count is zero");
2527 return 0;
2528 }
2529
2530 const size_t capacity = static_cast<size_t>(count) < 65536 ? count : 65536;
2532 if (!entries) {
2533 SYSCALL_ERROR(OutOfMemory);
2534 return -1;
2535 }
2536
2537 // Navigate the directory tree.
2538 Directory* pDirectory = Directory::fromFile(pFd->getFile());
2539 struct Context {
2540 size_t (*setDent)(const Directory::DirectoryEntryView&, void*, size_t);
2541 void* buffer;
2542 size_t available;
2543 size_t written;
2544 bool rejected;
2545 } context = {set_dent, entries.get(), capacity, 0, false};
2546
2547 auto emitter = [](void* opaque, const Directory::DirectoryEntryView& entry) -> bool {
2548 Context* context = reinterpret_cast<Context*>(opaque);
2549 F_NOTICE(" -> " << entry.name.toString());
2550 size_t reclen = context->setDent(entry, context->buffer, context->available - context->written);
2551 if (!reclen) {
2552 context->rejected = true;
2553 return false;
2554 }
2555 context->buffer = adjust_pointer(context->buffer, reclen);
2556 context->written += reclen;
2557 return true;
2558 };
2559
2561 uint64_t cookie = position.offset();
2562 Directory::ReadStatus status = pDirectory->enumerate(cookie, emitter, &context);
2563
2564 if (status == Directory::ReadStatus::IoError && !context.written) {
2565 SYSCALL_ERROR(IoError);
2566 return -1;
2567 }
2568 if (status == Directory::ReadStatus::Stopped && context.rejected && !context.written) {
2569 SYSCALL_ERROR(InvalidArgument);
2570 return -1;
2571 }
2572 if (!PosixSubsystem::copyToUser(buffer, entries.get(), context.written)) {
2573 SYSCALL_ERROR(BadAddress);
2574 return -1;
2575 }
2576 position.setOffset(cookie);
2577
2578 F_NOTICE(" -> " << context.written);
2579 return context.written;
2580}
2581
2582static char getdentsType(Directory::EntryType type) {
2583 switch (type) {
2584 case Directory::EntryType::Directory:
2585 return DT_DIR;
2586 case Directory::EntryType::Symlink:
2587 return DT_LNK;
2588 case Directory::EntryType::Fifo:
2589 return DT_FIFO;
2590 case Directory::EntryType::Socket:
2591 return DT_SOCK;
2592 case Directory::EntryType::CharacterDevice:
2593 return DT_CHR;
2594 case Directory::EntryType::BlockDevice:
2595 return DT_BLK;
2596 case Directory::EntryType::Regular:
2597 return DT_REG;
2598 case Directory::EntryType::Unknown:
2599 default:
2600 return DT_UNKNOWN;
2601 }
2602}
2603
2604static size_t getdents_helper(const Directory::DirectoryEntryView& file, void* buffer,
2605 size_t avail) {
2606 struct linux_dirent* entry = reinterpret_cast<struct linux_dirent*>(buffer);
2607 char* char_buffer = reinterpret_cast<char*>(buffer);
2608
2609 size_t filenameLength = file.name.length();
2610 // dirent struct, filename, null terminator, and d_type
2611 size_t reclen = (offsetof(struct linux_dirent, d_name) + filenameLength + 2 + sizeof(long) - 1) &
2612 ~(sizeof(long) - 1);
2613 // do we have room for this record?
2614 if (avail < reclen) {
2615 // need to call again with more space available
2616 return 0;
2617 }
2618
2619 ByteSet(entry, 0, reclen);
2620 entry->d_reclen = reclen;
2621 entry->d_off = file.nextCookie;
2622
2623 entry->d_ino = file.inode;
2624 if (!entry->d_ino) {
2625 entry->d_ino = ~0U;
2626 }
2627
2628 MemoryCopy(entry->d_name, file.name.str(), filenameLength);
2629 entry->d_name[filenameLength] = 0;
2630 char_buffer[reclen - 2] = 0;
2631 char_buffer[reclen - 1] = getdentsType(file.type);
2632
2633 return reclen;
2634}
2635
2636static size_t getdents64_helper(const Directory::DirectoryEntryView& file, void* buffer,
2637 size_t avail) {
2638 struct dirent* entry = reinterpret_cast<struct dirent*>(buffer);
2639
2640 size_t filenameLength = file.name.length();
2641 size_t reclen = (offsetof(struct dirent, d_name) + filenameLength + 1 + sizeof(uint64_t) - 1) &
2642 ~(sizeof(uint64_t) - 1);
2643 // do we have room for this record?
2644 if (avail < reclen) {
2645 // need to call again with more space available
2646 return 0;
2647 }
2648
2649 ByteSet(entry, 0, reclen);
2650 entry->d_reclen = reclen;
2651 entry->d_off = file.nextCookie;
2652
2653 entry->d_ino = file.inode;
2654 if (!entry->d_ino) {
2655 entry->d_ino = ~0U;
2656 }
2657
2658 MemoryCopy(entry->d_name, file.name.str(), filenameLength);
2659 entry->d_name[filenameLength] = 0;
2660 entry->d_type = getdentsType(file.type);
2661
2662 return reclen;
2663}
2664
2665int posix_getdents(int fd, struct linux_dirent* ents, int count) {
2666 F_NOTICE("getdents(" << fd << ")");
2667 if (count < 0) {
2668 SYSCALL_ERROR(InvalidArgument);
2669 return -1;
2670 }
2671 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(ents), count,
2672 PosixSubsystem::SafeWrite)) {
2673 F_NOTICE("getdents -> invalid address");
2674 SYSCALL_ERROR(BadAddress);
2675 return -1;
2676 }
2677
2678 return getdents_common(fd, getdents_helper, ents, count);
2679}
2680
2681int posix_getdents64(int fd, struct dirent* ents, int count) {
2682 F_NOTICE("getdents64(" << fd << ")");
2683 if (count < 0) {
2684 SYSCALL_ERROR(InvalidArgument);
2685 return -1;
2686 }
2687 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(ents), count,
2688 PosixSubsystem::SafeWrite)) {
2689 F_NOTICE("getdents64 -> invalid address");
2690 SYSCALL_ERROR(BadAddress);
2691 return -1;
2692 }
2693
2694 return getdents_common(fd, getdents64_helper, ents, count);
2695}
2696
2697template <typename T>
2698static bool copyIoctlInput(const void* buffer, T& value) {
2699 if (!PosixSubsystem::copyFromUser(&value, buffer, sizeof(value))) {
2700 SYSCALL_ERROR(BadAddress);
2701 return false;
2702 }
2703 return true;
2704}
2705
2706template <typename T>
2707static int copyIoctlResult(void* buffer, const T& value) {
2708 if (!PosixSubsystem::copyToUser(buffer, &value, sizeof(value))) {
2709 SYSCALL_ERROR(BadAddress);
2710 return -1;
2711 }
2712 return 0;
2713}
2714
2715int posix_ioctl(int fd, size_t command, void* buf) {
2716 F_NOTICE("ioctl(" << Dec << fd << ", " << Hex << command << ", "
2717 << reinterpret_cast<uintptr_t>(buf) << ")");
2718
2719 Process* pProcess = Processor::information().getCurrentThread()->getParent();
2720 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
2721 if (!pSubsystem) {
2722 ERROR("No subsystem for this process!");
2723 return -1;
2724 }
2725
2727 if (!pSubsystem->acquireFileDescriptor(fd, f)) {
2728 // Error - no such FD.
2729 F_NOTICE(" -> ioctl for a file that doesn't exist");
2730 SYSCALL_ERROR(BadFileDescriptor);
2731 return -1;
2732 }
2733
2734 if (f->getStatusFlags() & O_PATH) {
2735 SYSCALL_ERROR(BadFileDescriptor);
2736 return -1;
2737 }
2738
2739 if (command == FIOCLEX || command == FIONCLEX) {
2740 f->fdflags = command == FIOCLEX ? f->fdflags | FD_CLOEXEC : f->fdflags & ~FD_CLOEXEC;
2741 return 0;
2742 }
2743
2744 FileDescriptor::TerminalOperation terminalOperation;
2745 const bool terminalPolicyCommand =
2746 command == TIOCSCTTY || command == TIOCGPGRP || command == TIOCSPGRP || command == TIOCGSID;
2747 if (!terminalPolicyCommand && !f->acquireTerminalOperation(terminalOperation)) {
2748 SYSCALL_ERROR(IoError);
2749 return -1;
2750 }
2751
2752 if (command == FIONBIO) {
2753 int enabled = 0;
2754 if (!copyIoctlInput(buf, enabled)) {
2755 return -1;
2756 }
2757 if (enabled) {
2758 f->addStatusFlag(O_NONBLOCK);
2759 } else {
2760 f->removeStatusFlag(O_NONBLOCK);
2761 }
2762 return 0;
2763 }
2764 auto fanotify = f->getFanotifyImpl();
2765 if (fanotify && command == FIONREAD)
2766 return copyIoctlResult(buf, fanotify->queuedMetadataBytes());
2767 if (f->getTimerFdImpl() || f->getSignalFdImpl() || fanotify) {
2768 SYSCALL_ERROR(NotAConsole);
2769 return -1;
2770 }
2771
2772 if (f->networkImpl) {
2773 return posix_network_ioctl(*f->networkImpl, command, reinterpret_cast<uintptr_t>(buf));
2774 }
2775 if (!f->getFile()) {
2776 F_NOTICE(" -> fd " << fd << " is not supposed to be ioctl'd");
2777 SYSCALL_ERROR(InvalidArgument);
2778 return -1;
2779 }
2780
2781 size_t terminalNumber = ~size_t(0);
2782 if ((command & 0xff00) == 0x5600 || command == 0x4b3a || command == 0x4b3b) {
2783 if (ConsoleManager::instance().isConsole(f->getFile())) {
2784 terminalNumber = static_cast<ConsoleFile*>(f->getFile())->getPhysicalConsoleNumber();
2785 }
2786 if (!g_pDevFs->getTerminalManager().isTerminal(terminalNumber)) {
2787 SYSCALL_ERROR(NotAConsole);
2788 return -1;
2789 }
2790 }
2791
2792 if (f->getFile()->supports(command)) {
2793 return f->getFile()->command(command, buf);
2794 }
2795
2796 switch (command) {
2797 case FIONREAD: {
2798 int available = 0;
2799 if (f->getFile()->isPipe() || f->getFile()->isFifo()) {
2800 available = static_cast<int>(Pipe::fromFile(f->getFile())->readableBytes());
2801 } else if (ConsoleManager::instance().isConsole(f->getFile())) {
2802 auto* console = static_cast<ConsoleFile*>(f->getFile());
2803 available = static_cast<int>(console->readableBytes(*f->terminalEpoch()));
2804 } else {
2805 SYSCALL_ERROR(NotAConsole);
2806 return -1;
2807 }
2808 F_NOTICE(" -> FIONREAD: " << Dec << available << " bytes");
2809 return copyIoctlResult(buf, available);
2810 }
2811
2812 // KDGETLED
2813 case 0x4B31: {
2814 F_NOTICE(" -> KDGETLED, arg=" << buf);
2815 const char state = Machine::instance().getKeyboard()->getLedState();
2816 return copyIoctlResult(buf, state);
2817 }
2818 return 0;
2819
2820 // KDSETLED
2821 case 0x4B32: {
2822 F_NOTICE(" -> KDSETLED, arg=" << buf);
2823 uintptr_t leds = reinterpret_cast<uintptr_t>(buf);
2824 Machine::instance().getKeyboard()->setLedState(leds);
2825 }
2826 return 0;
2827
2828 case 0x4B33: // KDGKBTYPE
2829 {
2830 F_NOTICE(" -> KDGKBTYPE");
2831 if (ConsoleManager::instance().isConsole(f->getFile())) {
2832 // US 101
2833 return copyIoctlResult(buf, static_cast<unsigned char>(0x02));
2834 } else {
2835 SYSCALL_ERROR(NotAConsole);
2836 return -1;
2837 }
2838 }
2839
2840 // KDSETMODE
2841 case 0x4b3a: {
2842 const uintptr_t mode = reinterpret_cast<uintptr_t>(buf);
2843 if (mode > 1) {
2844 SYSCALL_ERROR(InvalidArgument);
2845 return -1;
2846 }
2847 g_pDevFs->getTerminalManager().setSystemMode(
2848 terminalNumber, mode ? VirtualTerminalManager::Graphics : VirtualTerminalManager::Text);
2849 return 0;
2850 }
2851
2852 // KDGETMODE
2853 case 0x4b3b: {
2854 const int mode = g_pDevFs->getTerminalManager().getSystemMode(terminalNumber) ==
2855 VirtualTerminalManager::Graphics
2856 ? 1
2857 : 0;
2858 return copyIoctlResult(buf, mode);
2859 }
2860
2861 // KDGKBMODE
2862 case 0x4b44:
2863 F_NOTICE(" -> KDGKBMODE (stubbed), arg=" << buf);
2864 return 0;
2865
2866 // KDSKBMODE
2867 case 0x4B45: {
2868 F_NOTICE(" -> KDSKBMODE, arg=" << buf);
2869
2870 size_t consoleNumber = 0;
2871 if (ConsoleManager::instance().isConsole(f->getFile())) {
2872 ConsoleFile* pConsole = static_cast<ConsoleFile*>(f->getFile());
2873 consoleNumber = pConsole->getPhysicalConsoleNumber();
2874 if (consoleNumber == ~0U) {
2875 ERROR("KDSKBMODE used on something that is not a VT");
2876 return -1;
2877 }
2878 } else {
2879 SYSCALL_ERROR(NotAConsole);
2880 return -1;
2881 }
2882
2883 long mode = reinterpret_cast<long>(buf);
2884 if (mode == 0) {
2885 g_pDevFs->getTerminalManager().setInputMode(consoleNumber, TextIO::Raw);
2886 } else {
2887 g_pDevFs->getTerminalManager().setInputMode(consoleNumber, TextIO::Standard);
2888 }
2889 }
2890 return 0;
2891
2892 // KDGKBENT
2893 case 0x4B46: {
2894 F_NOTICE(" -> KDGKBENT, arg=" << buf);
2895 POSIX_VERBOSE_LOG("io", " -> KDGKBENT, arg=" << buf);
2896
2897 struct kbentry entryCopy = {};
2898 if (!copyIoctlInput(buf, entryCopy)) {
2899 return -1;
2900 }
2901 struct kbentry* kbent = &entryCopy;
2902 bool shift = kbent->kb_table & 0x1;
2903 bool altgr = kbent->kb_table & 0x2;
2904 bool ctrl = kbent->kb_table & 0x4;
2905 bool alt = kbent->kb_table & 0x8;
2906
2907 // convert to HID so we can look in the keymap
2908 KeymapManager::EscapeState escape = KeymapManager::EscapeNone;
2909 uint8_t keyCode =
2912 KeymapManager::instance().getKeymapEntry(ctrl, shift, alt, altgr, 0, keyCode);
2913 if (entry) {
2914 F_NOTICE(" -> no keymap entry for table #" << Dec << kbent->kb_table << " index #" << Hex
2915 << kbent->kb_index);
2916 kbent->kb_value = 0xF000 | static_cast<uint16_t>(entry->value & 0xFFFF);
2917 } else {
2918 kbent->kb_value = 0;
2919 }
2920
2921 POSIX_VERBOSE_LOG("io", " -> val for table #" << Dec << kbent->kb_table << " #" << Hex
2922 << kbent->kb_index << " is now "
2923 << kbent->kb_value << "!");
2924 return copyIoctlResult(buf, entryCopy);
2925 }
2926 return 0;
2927
2928 // KDSKBENT
2929 case 0x4B47:
2930 F_NOTICE(" -> KDSKBENT (stubbed), arg=" << buf);
2931 POSIX_VERBOSE_LOG("io", " -> KDSKBENT (stubbed), arg=" << buf);
2932 return -1;
2933
2934 // KDKBDREP
2935 case 0x4B52:
2936 F_NOTICE(" -> KDKBDREP (stubbed), arg=" << buf);
2937 return 0;
2938
2939 case TCGETS: {
2940 if (ConsoleManager::instance().isConsole(f->getFile())) {
2941 return console_tcgetattr(f, reinterpret_cast<struct termios*>(buf));
2942 } else {
2943 SYSCALL_ERROR(NotAConsole);
2944 return -1;
2945 }
2946 }
2947
2948 case TCSETS: {
2949 if (ConsoleManager::instance().isConsole(f->getFile())) {
2950 return console_tcsetattr(f, TCSANOW, reinterpret_cast<struct termios*>(buf));
2951 } else {
2952 SYSCALL_ERROR(NotAConsole);
2953 return -1;
2954 }
2955 }
2956
2957 case TCSETSW: {
2958 if (ConsoleManager::instance().isConsole(f->getFile())) {
2959 return console_tcsetattr(f, TCSADRAIN, reinterpret_cast<struct termios*>(buf));
2960 } else {
2961 SYSCALL_ERROR(NotAConsole);
2962 return -1;
2963 }
2964 }
2965
2966 case TCSETSF: {
2967 if (ConsoleManager::instance().isConsole(f->getFile())) {
2968 return console_tcsetattr(f, TCSAFLUSH, reinterpret_cast<struct termios*>(buf));
2969 } else {
2970 SYSCALL_ERROR(NotAConsole);
2971 return -1;
2972 }
2973 }
2974
2975 case TIOCGPGRP: {
2976 if (ConsoleManager::instance().isConsole(f->getFile())) {
2977 pid_t pgrp = TerminalControl::foreground(*static_cast<ConsoleFile*>(f->getFile()),
2978 f->terminalEpoch());
2979 return pgrp < 0 ? -1 : copyIoctlResult(buf, pgrp);
2980 } else {
2981 SYSCALL_ERROR(NotAConsole);
2982 return -1;
2983 }
2984 }
2985
2986 case TIOCSPGRP: {
2987 if (ConsoleManager::instance().isConsole(f->getFile())) {
2988 pid_t pgrp = 0;
2989 return copyIoctlInput(buf, pgrp)
2990 ? TerminalControl::setForeground(*static_cast<ConsoleFile*>(f->getFile()), pgrp,
2991 f->terminalEpoch())
2992 : -1;
2993 } else {
2994 SYSCALL_ERROR(NotAConsole);
2995 return -1;
2996 }
2997 }
2998
2999 case TIOCGSID: {
3000 if (ConsoleManager::instance().isConsole(f->getFile())) {
3001 const pid_t session =
3002 TerminalControl::session(*static_cast<ConsoleFile*>(f->getFile()), f->terminalEpoch());
3003 return session < 0 ? -1 : copyIoctlResult(buf, session);
3004 } else {
3005 SYSCALL_ERROR(NotAConsole);
3006 return -1;
3007 }
3008 }
3009
3010 case TCFLSH: {
3011 if (ConsoleManager::instance().isConsole(f->getFile())) {
3012 return console_flush(f->getFile(), static_cast<int>(reinterpret_cast<uintptr_t>(buf)));
3013 } else {
3014 SYSCALL_ERROR(NotAConsole);
3015 return -1;
3016 }
3017 }
3018
3019 case TIOCGWINSZ: {
3020 if (ConsoleManager::instance().isConsole(f->getFile())) {
3021 F_NOTICE(" -> TIOCGWINSZ");
3022 struct winsize value = {};
3023 const int result = console_getwinsize(f->getFile(), &value);
3024 return result < 0 ? result : copyIoctlResult(buf, value);
3025 } else {
3026 SYSCALL_ERROR(NotAConsole);
3027 return -1;
3028 }
3029 }
3030
3031 case TIOCSWINSZ: {
3032 if (ConsoleManager::instance().isConsole(f->getFile())) {
3033 struct winsize value = {};
3034 if (!copyIoctlInput(buf, value)) {
3035 return -1;
3036 }
3037 const struct winsize* ws = &value;
3038 F_NOTICE(" -> TIOCSWINSZ " << Dec << ws->ws_col << "x" << ws->ws_row << Hex);
3039 return console_setwinsize(f->getFile(), ws);
3040 } else {
3041 SYSCALL_ERROR(NotAConsole);
3042 return -1;
3043 }
3044 }
3045
3046 case TIOCSCTTY: {
3047 if (ConsoleManager::instance().isConsole(f->getFile())) {
3048 F_NOTICE(" -> TIOCSCTTY");
3049 return TerminalControl::attach(*static_cast<ConsoleFile*>(f->getFile()),
3050 reinterpret_cast<uintptr_t>(buf) == 1, false,
3051 f->terminalEpoch());
3052 } else {
3053 SYSCALL_ERROR(NotAConsole);
3054 return -1;
3055 }
3056 }
3057
3058 case TIOCGPTN: {
3059 F_NOTICE(" -> TIOCGPTN");
3060 unsigned int result = console_getptn(fd);
3061 if (result < ~0U) {
3062 F_NOTICE(" -> ok, returning " << result);
3063 if (!PosixSubsystem::copyToUser(buf, &result, sizeof(result))) {
3064 SYSCALL_ERROR(BadAddress);
3065 return -1;
3066 }
3067 return 0;
3068 } else {
3069 // console_getptn will set the syscall error
3070 F_NOTICE(" -> failed!");
3071 return -1;
3072 }
3073 }
3074
3075 case TIOCSPTLCK: {
3076 if (ConsoleManager::instance().isConsole(f->getFile()) &&
3077 ConsoleManager::instance().isMasterConsole(f->getFile())) {
3078 int locked = 0;
3079 if (!copyIoctlInput(buf, locked)) {
3080 return -1;
3081 }
3082 if (locked != 0 && locked != 1) {
3083 SYSCALL_ERROR(InvalidArgument);
3084 return -1;
3085 }
3086 F_NOTICE(" -> TIOCSPTLCK " << locked);
3087 return ConsoleManager::instance().setPtyLock(f->getFile(), locked != 0) ? 0 : -1;
3088 }
3089 SYSCALL_ERROR(NotAConsole);
3090 return -1;
3091 }
3092
3093 // VT_OPENQRY
3094 case 0x5600: {
3095 F_NOTICE(" -> VT_OPENQRY (stubbed)");
3096
3097 size_t newTty = g_pDevFs->getTerminalManager().openInactive();
3098 const int result = newTty != ~size_t(0) ? static_cast<int>(newTty + 1) : -1;
3099 return copyIoctlResult(buf, result);
3100 }
3101
3102 // VT_GETMODE
3103 case 0x5601:
3104 return copyIoctlResult(buf, g_pDevFs->getTerminalManager().getTerminalMode(terminalNumber));
3105
3106 // VT_SETMODE
3107 case 0x5602: {
3108 struct vt_mode mode = {};
3109 if (!copyIoctlInput(buf, mode)) {
3110 return -1;
3111 }
3112 if (!g_pDevFs->getTerminalManager().setTerminalMode(terminalNumber, mode)) {
3113 SYSCALL_ERROR(InvalidArgument);
3114 return -1;
3115 }
3116 return 0;
3117 }
3118
3119 // VT_GETSTATE
3120 case 0x5603: {
3121 F_NOTICE(" -> VT_GETSTATE (stubbed)");
3122
3123 return copyIoctlResult(buf, g_pDevFs->getTerminalManager().getState());
3124 }
3125 return 0;
3126
3127 // VT_RELDISP
3128 case 0x5605: {
3129 const uintptr_t permission = reinterpret_cast<uintptr_t>(buf);
3130 auto& terminals = g_pDevFs->getTerminalManager();
3131 const bool accepted =
3132 permission == VT_ACKACQ
3133 ? terminals.acknowledgeAcquire(terminalNumber)
3134 : permission <= 1 &&
3135 terminals.reportPermission(terminalNumber,
3136 permission ? VirtualTerminalManager::Allowed
3137 : VirtualTerminalManager::Disallowed);
3138 if (!accepted) {
3139 SYSCALL_ERROR(InvalidArgument);
3140 return -1;
3141 }
3142 return 0;
3143 }
3144
3145 // VT_ACTIVATE
3146 case 0x5606: {
3147 const uintptr_t ttyNum = reinterpret_cast<uintptr_t>(buf);
3148 if (!ttyNum || !g_pDevFs->getTerminalManager().activate(ttyNum - 1)) {
3149 SYSCALL_ERROR(InvalidArgument);
3150 return -1;
3151 }
3152 return 0;
3153 }
3154
3155 // VT_WAITACTIVE
3156 case 0x5607:
3157 // no-op on Pedigree so far
3158 return 0;
3159 }
3160
3161 F_NOTICE(" -> invalid combination of fd " << fd << " and ioctl " << Hex << command);
3162 SYSCALL_ERROR(InvalidArgument);
3163 return -1;
3164}
3165
3166int posix_chdir(const char* path) {
3167 F_NOTICE("chdir");
3168
3169 String pathCopy;
3170 if (!copyUserString(path, pathCopy)) {
3171 F_NOTICE("chdir -> invalid address");
3172 return -1;
3173 }
3174
3175 F_NOTICE("chdir(" << pathCopy << ")");
3176
3177 String realPath;
3178 normalisePath(realPath, pathCopy.cstr());
3179
3180 ResolvedPath dirLease;
3181 File* dir = findFilePath(realPath, dirLease);
3182 if (!dir) {
3183 F_NOTICE("Path lookup failed.");
3184 if (!Processor::information().getCurrentThread()->getErrno())
3185 SYSCALL_ERROR(DoesNotExist);
3186 return -1;
3187 }
3188
3189 return doChdir(dir, dirLease) ? 0 : -1;
3190}
3191
3192int posix_dup(int fd) {
3193 F_NOTICE("dup(" << fd << ")");
3194
3195 // grab the file descriptor pointer for the passed descriptor
3196 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3197 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3198 if (!pSubsystem) {
3199 ERROR("No subsystem for this process!");
3200 return -1;
3201 }
3202
3204 if (!pSubsystem->acquireFileDescriptor(fd, f)) {
3205 SYSCALL_ERROR(BadFileDescriptor);
3206 return -1;
3207 }
3208
3209 size_t newFd = pSubsystem->getFd();
3210
3211 // Copy the descriptor
3212 FileDescriptor* f2 = new FileDescriptor(*f);
3213 if ((f->networkImpl && !f2->networkPublished()) ||
3214 (f->getEventFdImpl() && !f2->eventFdPublished()) ||
3215 (f->getTimerFdImpl() && !f2->timerFdPublished()) ||
3216 (f->getSignalFdImpl() && !f2->signalFdPublished())) {
3217 delete f2;
3218 pSubsystem->freeFd(newFd);
3219 SYSCALL_ERROR(BadFileDescriptor);
3220 return -1;
3221 }
3222 // According to the spec, CLOEXEC is cleared on DUP.
3223 f2->fdflags &= ~FD_CLOEXEC;
3224 f2->fd = newFd;
3225 pSubsystem->addFileDescriptor(newFd, f2);
3226
3227 return static_cast<int>(newFd);
3228}
3229
3230int posix_dup2(int fd1, int fd2) {
3231 F_NOTICE("dup2(" << fd1 << ", " << fd2 << ")");
3232
3233 constexpr int MaximumFileDescriptors = 16384;
3234 if (fd2 < 0 || fd2 >= MaximumFileDescriptors) {
3235 SYSCALL_ERROR(BadFileDescriptor);
3236 return -1; // EBADF
3237 }
3238
3239 // grab the file descriptor pointer for the passed descriptor
3240 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3241 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3242 if (!pSubsystem) {
3243 ERROR("No subsystem for this process!");
3244 return -1;
3245 }
3246
3248 if (!pSubsystem->acquireFileDescriptor(fd1, f)) {
3249 SYSCALL_ERROR(BadFileDescriptor);
3250 return -1;
3251 }
3252
3253 // POSIX still requires the source to be valid when both numbers match.
3254 if (fd1 == fd2)
3255 return fd2;
3256
3257 // Copy the descriptor.
3258 //
3259 // This will also increase the refcount *before* we close the original, else
3260 // we might accidentally trigger an EOF condition on a pipe! (if the write
3261 // refcount drops to zero)...
3262 FileDescriptor* f2 = new FileDescriptor(*f);
3263 if ((f->networkImpl && !f2->networkPublished()) ||
3264 (f->getEventFdImpl() && !f2->eventFdPublished()) ||
3265 (f->getTimerFdImpl() && !f2->timerFdPublished()) ||
3266 (f->getSignalFdImpl() && !f2->signalFdPublished())) {
3267 delete f2;
3268 SYSCALL_ERROR(BadFileDescriptor);
3269 return -1;
3270 }
3271 // According to the spec, CLOEXEC is cleared on DUP.
3272 f2->fdflags &= ~FD_CLOEXEC;
3273 f2->fd = fd2;
3274 pSubsystem->addFileDescriptor(fd2, f2);
3275
3276 return fd2;
3277}
3278
3279int posix_dup3(int oldfd, int newfd, int flags) {
3280 F_NOTICE("dup3(" << oldfd << ", " << newfd << ", " << flags << ")");
3281
3282 if (flags & ~O_CLOEXEC || oldfd == newfd) {
3283 SYSCALL_ERROR(InvalidArgument);
3284 return -1;
3285 }
3286
3287 constexpr int MaximumFileDescriptors = 16384;
3288 if (newfd < 0 || newfd >= MaximumFileDescriptors) {
3289 SYSCALL_ERROR(BadFileDescriptor);
3290 return -1;
3291 }
3292
3293 Process* process = Processor::information().getCurrentThread()->getParent();
3294 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
3295 if (!subsystem) {
3296 ERROR("No subsystem for this process!");
3297 return -1;
3298 }
3299
3300 const PosixSubsystem::DescriptorDuplicationResult result = subsystem->duplicateFileDescriptor(
3301 static_cast<size_t>(oldfd), static_cast<size_t>(newfd), flags & O_CLOEXEC);
3302 if (result == PosixSubsystem::DescriptorDuplicationResult::TargetBusy) {
3303 SYSCALL_ERROR(DeviceBusy);
3304 return -1;
3305 }
3306 if (result != PosixSubsystem::DescriptorDuplicationResult::Success) {
3307 SYSCALL_ERROR(BadFileDescriptor);
3308 return -1;
3309 }
3310
3311 return newfd;
3312}
3313
3314int posix_mkdir(const char* name, int mode) {
3315 return posix_mkdirat(AT_FDCWD, name, mode);
3316}
3317
3318int posix_rmdir(const char* path) {
3319 return posix_unlinkat(AT_FDCWD, path, AT_REMOVEDIR);
3320}
3321
3322int posix_isatty(int fd) {
3323 // Lookup this process.
3324 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3325 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3326 if (!pSubsystem) {
3327 ERROR("No subsystem for this process!");
3328 return -1;
3329 }
3330
3331 DescriptorLease pFd;
3332 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
3333 // Error - no such file descriptor.
3334 ERROR("isatty: no such file descriptor (" << Dec << fd << Hex << ")");
3335 return 0;
3336 }
3337
3338 int result =
3339 ConsoleManager::instance().isConsole(pFd->getFile()) && !pFd->terminalHungUp() ? 1 : 0;
3340 NOTICE("isatty(" << fd << ") -> " << result);
3341 return result;
3342}
3343
3344int posix_fcntl(int fd, int cmd, void* arg) {
3346 F_NOTICE("fcntl(" << fd << ", " << cmd << ", " << arg << ")");
3347
3348 // grab the file descriptor pointer for the passed descriptor
3349 Thread* pThread = Processor::information().getCurrentThread();
3350 Process* pProcess = pThread->getParent();
3351 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3352 if (!pSubsystem) {
3353 ERROR("No subsystem for this process!");
3354 return -1;
3355 }
3356
3358 if (!pSubsystem->acquireFileDescriptor(fd, f)) {
3359 SYSCALL_ERROR(BadFileDescriptor);
3360 return -1;
3361 }
3362
3363 if ((f->getStatusFlags() & O_PATH) && cmd != F_GETFL && cmd != F_GETFD && cmd != F_SETFD &&
3364 cmd != F_DUPFD && cmd != F_DUPFD_CLOEXEC) {
3365 SYSCALL_ERROR(BadFileDescriptor);
3366 return -1;
3367 }
3368
3369 switch (cmd) {
3370#ifdef F_DUPFD_CLOEXEC
3371 case F_DUPFD_CLOEXEC:
3372#endif
3373 case F_DUPFD: {
3374 constexpr intptr_t MaximumFileDescriptors = 16384;
3375 const intptr_t minimum = reinterpret_cast<intptr_t>(arg);
3376 if (minimum < 0 || minimum >= MaximumFileDescriptors) {
3377 SYSCALL_ERROR(InvalidArgument);
3378 return -1;
3379 }
3380
3381 const size_t fd2 = pSubsystem->getFd(static_cast<size_t>(minimum));
3382 FileDescriptor* f2 = new FileDescriptor(*f);
3383 if ((f->networkImpl && !f2->networkPublished()) ||
3384 (f->getEventFdImpl() && !f2->eventFdPublished()) ||
3385 (f->getTimerFdImpl() && !f2->timerFdPublished()) ||
3386 (f->getSignalFdImpl() && !f2->signalFdPublished())) {
3387 delete f2;
3388 pSubsystem->freeFd(fd2);
3389 SYSCALL_ERROR(BadFileDescriptor);
3390 return -1;
3391 }
3392#ifdef F_DUPFD_CLOEXEC
3393 if (cmd == F_DUPFD_CLOEXEC) {
3394 f2->fdflags |= FD_CLOEXEC;
3395 } else
3396#endif
3397 {
3398 f2->fdflags &= ~FD_CLOEXEC;
3399 }
3400 f2->fd = fd2;
3401 pSubsystem->addFileDescriptor(fd2, f2);
3402
3403 return static_cast<int>(fd2);
3404 }
3405
3406 case F_GETFD:
3407 F_NOTICE(" -> get fd flags");
3408 return f->fdflags;
3409 case F_SETFD:
3410 F_NOTICE(" -> set fd flags: " << arg);
3411 f->fdflags = reinterpret_cast<size_t>(arg);
3412 return 0;
3413 case F_GETFL:
3414 F_NOTICE(" -> get flags " << f->getStatusFlags());
3415 return f->getStatusFlags();
3416 case F_SETFL:
3417 F_NOTICE(" -> set flags " << arg);
3418 f->setStatusFlags(reinterpret_cast<size_t>(arg));
3419 F_NOTICE(" -> new flags " << f->getStatusFlags());
3420 return 0;
3421 case F_GET_SEALS:
3422 case F_ADD_SEALS:
3423 return posix_memfd_fcntl(f, cmd, reinterpret_cast<uintptr_t>(arg));
3424 case F_GETLK:
3425 case F_SETLK:
3426 case F_SETLKW:
3427 case F_OFD_GETLK:
3428 case F_OFD_SETLK:
3429 case F_OFD_SETLKW:
3430 return posix_advisory_fcntl(*pSubsystem, fd, f, cmd, arg);
3431 case F_GETOWN:
3432 F_NOTICE(" -> F_GETOWN (stubbed)");
3433 return 0;
3434 case F_SETOWN:
3436 F_NOTICE(" -> F_SETOWN");
3437
3438 if (!f->ioevent) {
3439 if (f->getFile()) {
3440 NOTICE("Adding ioevent to thread");
3441 f->ioevent = new IoEvent(pSubsystem, f->getFile());
3442 f->getFile()->monitor(pThread, f->ioevent);
3443 } else {
3445 ERROR("F_SETOWN on something that can't raise events [fd="
3446 << fd << " file=" << f->getFile() << " impl=" << f->networkImpl.get() << "!");
3447 return -1;
3448 }
3449 }
3450 return 0;
3451
3452 default:
3453 WARNING("fcntl: unknown control " << cmd << " on fd " << fd);
3454 }
3455
3456 SYSCALL_ERROR(Unimplemented);
3457 return -1;
3458}
3459
3460void* posix_mmap(void* addr, size_t len, int prot, int flags, int fd, off_t off) {
3461 TerminationDeferral lifetime;
3462 F_NOTICE("mmap");
3463 F_NOTICE(" -> addr=" << addr << ", len=" << len << ", prot=" << prot << ", flags=" << flags
3464 << ", fildes=" << fd << ", off=" << off << ".");
3465
3466 // Get the File object to map
3467 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3468 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3469 if (!pSubsystem) {
3470 ERROR("No subsystem for this process!");
3471 return MAP_FAILED;
3472 }
3473
3474 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
3475 const size_t pageSz = PhysicalMemoryManager::getPageSize();
3476 const uintptr_t pageMask = pageSz - 1;
3477 const bool fixedNoReplace = flags & MAP_FIXED_NOREPLACE;
3478 const bool fixed = fixedNoReplace || (flags & MAP_FIXED);
3479 const MemoryMapManager::Placement placement =
3480 fixedNoReplace
3481 ? MemoryMapManager::Placement::FixedNoReplace
3482 : (fixed ? MemoryMapManager::Placement::FixedReplace : MemoryMapManager::Placement::Hint);
3483
3484 // Verify the passed length and file offset before rounding either input.
3485 const int mappingType = flags & (MAP_PRIVATE | MAP_SHARED);
3486 if ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) ||
3487 (mappingType != MAP_PRIVATE && mappingType != MAP_SHARED) || !len ||
3488 len > ~static_cast<size_t>(0) - pageMask ||
3489 (!(flags & MAP_ANON) && (off < 0 || (static_cast<uint64_t>(off) & pageMask)))) {
3490 SYSCALL_ERROR(InvalidArgument);
3491 return MAP_FAILED;
3492 }
3493 const size_t roundedLength = (len + pageMask) & ~pageMask;
3494 if (!(flags & MAP_ANON) && static_cast<uint64_t>(off) > ~static_cast<size_t>(0) - roundedLength) {
3495 SYSCALL_ERROR(InvalidArgument);
3496 return MAP_FAILED;
3497 }
3498#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
3499 pProcess->recordBenchmarkVmCounter(Process::VmMmapCalls);
3500 pProcess->recordBenchmarkVmCounter(flags & MAP_ANON ? Process::VmMmapAnonymousCalls
3501 : Process::VmMmapFileCalls);
3502 const size_t mappingPages = roundedLength / pageSz;
3503 pProcess->recordBenchmarkVmCounter(Process::VmMmapPages, mappingPages);
3504 pProcess->recordBenchmarkVmCounter(vmLengthCounter(false, mappingPages));
3505#endif
3506
3507 // Sanitise input.
3508 uintptr_t sanityAddress = reinterpret_cast<uintptr_t>(addr);
3509 if (fixed) {
3510 if ((sanityAddress & pageMask) || sanityAddress < va.getUserStart() ||
3511 sanityAddress >= va.getKernelStart() ||
3512 roundedLength > va.getKernelStart() - sanityAddress) {
3513 SYSCALL_ERROR(InvalidArgument);
3514 F_NOTICE(" -> mmap given invalid fixed address");
3515 return MAP_FAILED;
3516 }
3517 } else if (sanityAddress) {
3518 sanityAddress &= ~pageMask;
3519 if (sanityAddress < va.getUserStart() || sanityAddress >= va.getKernelStart() ||
3520 roundedLength > va.getKernelStart() - sanityAddress) {
3521 // Invalid non-fixed hints do not make the mapping fail.
3522 sanityAddress = 0;
3523 }
3524 }
3525
3526 MemoryMapManager::MapStatus mapStatus = MemoryMapManager::MapStatus::NoMemory;
3527 const MemoryLockMode requestedLock =
3528 flags & MAP_LOCKED ? MemoryLockMode::Eager : MemoryLockMode::None;
3529 if (requestedLock != MemoryLockMode::None) {
3531 if (!posix_global_capable(14) && !pSubsystem->memoryLockAccount().limit().current) {
3532 SYSCALL_ERROR(NotEnoughPermissions);
3533 return MAP_FAILED;
3534 }
3535 }
3536
3537 // Create permission set.
3539 if (prot == PROT_NONE) {
3540 perms = MemoryMappedObject::None;
3541 } else {
3542 // Everything implies a readable memory region.
3543 perms = MemoryMappedObject::Read;
3544 if (prot & PROT_WRITE)
3545 perms |= MemoryMappedObject::Write;
3546 if (prot & PROT_EXEC)
3547 perms |= MemoryMappedObject::Exec;
3548 }
3549
3550 if (flags & MAP_ANON) {
3551 if (flags & MAP_SHARED) {
3552 F_NOTICE(" -> failed (MAP_SHARED cannot be used with MAP_ANONYMOUS)");
3553 SYSCALL_ERROR(InvalidArgument);
3554 return MAP_FAILED;
3555 }
3556
3558 sanityAddress, len, perms, placement, &mapStatus, requestedLock);
3559 if (!pObject) {
3560 if (mapStatus == MemoryMapManager::MapStatus::AddressInUse) {
3561 SYSCALL_ERROR(FileExists);
3562 } else if (mapStatus == MemoryMapManager::MapStatus::LockLimit) {
3563 SYSCALL_ERROR(NoMoreProcesses);
3564 } else {
3565 SYSCALL_ERROR(OutOfMemory);
3566 }
3567 F_NOTICE(" -> failed (mapAnon)!");
3568 return MAP_FAILED;
3569 }
3570
3571 F_NOTICE(" -> " << sanityAddress);
3572
3573 if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) {
3574 MemoryMapManager::instance().populateMemory(va, sanityAddress, roundedLength);
3575 }
3576 Processor::information().getCurrentThread()->setErrno(0);
3577 return reinterpret_cast<void*>(sanityAddress);
3578 } else {
3579 // Valid file passed?
3581 if (!pSubsystem->acquireFileDescriptor(fd, f)) {
3582 SYSCALL_ERROR(BadFileDescriptor);
3583 return MAP_FAILED;
3584 }
3585
3586 // Grab the file to map in
3587 File* fileToMap = f->getFile();
3588 UtsRef namespaceBacking;
3589 if (!fileToMap || fileToMap->isDirectory() ||
3590 posix_uts_file_namespace(fileToMap, namespaceBacking)) {
3591 SYSCALL_ERROR(NoSuchDevice);
3592 return MAP_FAILED;
3593 }
3594
3595 const int descriptorFlags = f->getStatusFlags();
3596 const int accessMode = descriptorFlags & O_ACCMODE;
3597 if (descriptorFlags & O_PATH) {
3598 SYSCALL_ERROR(BadFileDescriptor);
3599 return MAP_FAILED;
3600 }
3601 if (accessMode == O_WRONLY) {
3602 SYSCALL_ERROR(PermissionDenied);
3603 return MAP_FAILED;
3604 }
3605 MemoryMappedObject::Permissions maximumPerms =
3606 MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec;
3607 if ((flags & MAP_SHARED) && accessMode != O_RDWR) {
3608 maximumPerms &= ~MemoryMappedObject::Write;
3609 if (prot & PROT_WRITE) {
3610 SYSCALL_ERROR(PermissionDenied);
3611 return MAP_FAILED;
3612 }
3613 }
3614
3615 auto* view = VfsMountView::fromPath(f->openingPath());
3616 const auto mountFlags = view ? view->mountFlags(f->openingPath()) : 0;
3617 if (mountFlags & VfsMountView::NoExec) {
3618 maximumPerms &= ~MemoryMappedObject::Exec;
3619 if (prot & PROT_EXEC) {
3620 SYSCALL_ERROR(NotEnoughPermissions);
3621 return MAP_FAILED;
3622 }
3623 }
3624 if ((flags & MAP_SHARED) && (mountFlags & VfsMountView::ReadOnly) &&
3625 fileToMap->supportsRegularFileOperations() && !fileToMap->isBlockDevice()) {
3626 maximumPerms &= ~MemoryMappedObject::Write;
3627 if (prot & PROT_WRITE) {
3628 SYSCALL_ERROR(PermissionDenied);
3629 return MAP_FAILED;
3630 }
3631 }
3632
3633 F_NOTICE("mmap: file name is " << fileToMap->getFullPath());
3634
3635 bool bCopyOnWrite = (flags & MAP_SHARED) == 0;
3636 const FileMappingOrigin origin{f->acquireOpenFileDescription()->identity(),
3637 accessMode == O_RDWR, f->openingPath()};
3639 fileToMap, sanityAddress, roundedLength, perms, off, bCopyOnWrite, placement, &mapStatus,
3640 maximumPerms, SharedPointer<MappingAttachment>(), requestedLock, origin);
3641 if (!pFile) {
3642 if (mapStatus == MemoryMapManager::MapStatus::AddressInUse) {
3643 SYSCALL_ERROR(FileExists);
3644 } else if (mapStatus == MemoryMapManager::MapStatus::PolicyDenied) {
3645 SYSCALL_ERROR(NotEnoughPermissions);
3646 } else if (mapStatus == MemoryMapManager::MapStatus::TextBusy) {
3647 SYSCALL_ERROR(TextFileBusy);
3648 } else if (mapStatus == MemoryMapManager::MapStatus::LockLimit) {
3649 SYSCALL_ERROR(NoMoreProcesses);
3650 } else {
3651 SYSCALL_ERROR(OutOfMemory);
3652 }
3653 F_NOTICE(" -> failed (mapFile)!");
3654 return MAP_FAILED;
3655 }
3656
3657 F_NOTICE(" -> " << sanityAddress);
3658
3659 if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) {
3660 MemoryMapManager::instance().populateMemory(va, sanityAddress, roundedLength);
3661 }
3662 Processor::information().getCurrentThread()->setErrno(0);
3663 return reinterpret_cast<void*>(sanityAddress);
3664 }
3665}
3666
3667int posix_msync(void* p, size_t len, int flags) {
3668 const uintptr_t address = reinterpret_cast<uintptr_t>(p);
3669 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
3670 if ((address & pageMask) || (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC)) ||
3671 ((flags & MS_ASYNC) && (flags & MS_SYNC))) {
3672 SYSCALL_ERROR(InvalidArgument);
3673 return -1;
3674 }
3675 if (!len) {
3676 return 0;
3677 }
3678 if (len > ~static_cast<size_t>(0) - pageMask ||
3679 ((len + pageMask) & ~pageMask) > ~static_cast<uintptr_t>(0) - address) {
3680 SYSCALL_ERROR(OutOfMemory);
3681 return -1;
3682 }
3683 int error = 0;
3685 if ((flags & MS_INVALIDATE) && MemoryMapManager::instance().hasLockedMemory(
3686 Processor::information().getVirtualAddressSpace(), address,
3687 (len + pageMask) & ~pageMask)) {
3688 SYSCALL_ERROR(DeviceBusy);
3689 return -1;
3690 }
3691 if (!MemoryMapManager::instance().sync(address, len, flags & MS_ASYNC, &error)) {
3692 syscallError(error);
3693 return -1;
3694 }
3695 return 0;
3696}
3697
3698int posix_mprotect(void* p, size_t len, int prot) {
3699 const uintptr_t address = reinterpret_cast<uintptr_t>(p);
3700 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
3701 if ((address & pageMask) || (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))) {
3702 SYSCALL_ERROR(InvalidArgument);
3703 return -1;
3704 }
3705 if (!len) {
3706 return 0;
3707 }
3708 MemoryMappedObject::Permissions permissions = MemoryMappedObject::None;
3709 if (prot != PROT_NONE) {
3710 permissions = MemoryMappedObject::Read;
3711 if (prot & PROT_WRITE) {
3712 permissions |= MemoryMappedObject::Write;
3713 }
3714 if (prot & PROT_EXEC) {
3715 permissions |= MemoryMappedObject::Exec;
3716 }
3717 }
3719 if (!MemoryMapManager::instance().setPermissions(address, len, permissions, &status)) {
3720 if (status == MemoryMapManager::ProtectStatus::AccessDenied) {
3721 SYSCALL_ERROR(PermissionDenied);
3722 } else if (status == MemoryMapManager::ProtectStatus::Unsupported) {
3723 SYSCALL_ERROR(OperationNotSupported);
3724 } else if (status == MemoryMapManager::ProtectStatus::TextBusy) {
3725 SYSCALL_ERROR(TextFileBusy);
3726 } else {
3727 SYSCALL_ERROR(OutOfMemory);
3728 }
3729 return -1;
3730 }
3731 return 0;
3732}
3733
3734int posix_munmap(void* addr, size_t len) {
3735 F_NOTICE("munmap(" << reinterpret_cast<uintptr_t>(addr) << ", " << len << ")");
3736
3737 const uintptr_t address = reinterpret_cast<uintptr_t>(addr);
3738 const size_t pageSz = PhysicalMemoryManager::getPageSize();
3739 const size_t pageMask = pageSz - 1;
3740 if (!len || (address & pageMask) || len > ~static_cast<size_t>(0) - pageMask) {
3741 SYSCALL_ERROR(InvalidArgument);
3742 return -1;
3743 }
3744 const size_t roundedLength = (len + pageMask) & ~pageMask;
3745 if (address > ~static_cast<uintptr_t>(0) - roundedLength) {
3746 SYSCALL_ERROR(InvalidArgument);
3747 return -1;
3748 }
3749
3750#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
3751 Process* process = Processor::information().getCurrentThread()->getParent();
3752 const size_t mappingPages = roundedLength / pageSz;
3753 process->recordBenchmarkVmCounter(Process::VmMunmapCalls);
3754 process->recordBenchmarkVmCounter(Process::VmMunmapPages, mappingPages);
3755 process->recordBenchmarkVmCounter(vmLengthCounter(true, mappingPages));
3756#endif
3757
3758 MemoryMapManager::VmStatus status;
3759 MemoryMapManager::instance().removeAndRelease(address, roundedLength, &status);
3760 if (status != MemoryMapManager::VmStatus::Success) {
3761 if (status == MemoryMapManager::VmStatus::Unsupported)
3762 SYSCALL_ERROR(OperationNotSupported);
3763 else
3764 SYSCALL_ERROR(OutOfMemory);
3765 return -1;
3766 }
3767
3768 return 0;
3769}
3770
3771int posix_access(const char* name, int amode) {
3772 return posix_faccessat(AT_FDCWD, name, amode, 0);
3773}
3774
3775int posix_ftruncate(int a, off_t b) {
3776 F_NOTICE("ftruncate(" << a << ", " << b << ")");
3777
3778 if (b < 0) {
3779 SYSCALL_ERROR(InvalidArgument);
3780 return -1;
3781 }
3782
3783 // Grab the File pointer for this file
3784 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3785 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3786 if (!pSubsystem) {
3787 ERROR("No subsystem for this process!");
3788 return -1;
3789 }
3790
3791 DescriptorLease pFd;
3792 if (!pSubsystem->acquireFileDescriptor(a, pFd)) {
3793 // Error - no such file descriptor.
3794 SYSCALL_ERROR(BadFileDescriptor);
3795 return -1;
3796 }
3797 if (pFd->getStatusFlags() & O_PATH) {
3798 SYSCALL_ERROR(BadFileDescriptor);
3799 return -1;
3800 }
3801 VfsMountView::WriteLease mountWrite;
3802 if (pFd->openingPath() && pFd->getFile() && pFd->getFile()->supportsRegularFileOperations() &&
3803 !pFd->getFile()->isBlockDevice() && !mountWrite.acquire(pFd->openingPath())) {
3804 return -1;
3805 }
3806 File* pFile = pFd->getFile();
3807 if (!pFile) {
3808 SYSCALL_ERROR(InvalidArgument);
3809 return -1;
3810 }
3811 if ((pFd->getStatusFlags() & O_ACCMODE) == O_RDONLY) {
3812 SYSCALL_ERROR(InvalidArgument);
3813 return -1;
3814 }
3815 if (!pFd->truncateAllowed()) {
3816 SYSCALL_ERROR(PermissionDenied);
3817 return -1;
3818 }
3819 return pFile->resize(static_cast<size_t>(b)) ? 0 : -1;
3820}
3821
3822int posix_fsync(int fd) {
3823 F_NOTICE("fsync(" << fd << ")");
3824
3825 // Grab the File pointer for this file
3826 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3827 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3828 if (!pSubsystem) {
3829 ERROR("No subsystem for this process!");
3830 return -1;
3831 }
3832
3833 DescriptorLease pFd;
3834 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
3835 // Error - no such file descriptor.
3836 SYSCALL_ERROR(BadFileDescriptor);
3837 return -1;
3838 }
3839 if (pFd->getStatusFlags() & O_PATH) {
3840 SYSCALL_ERROR(BadFileDescriptor);
3841 return -1;
3842 }
3843
3844 File* pFile = pFd->getFile();
3845 if (!pFile) {
3846 SYSCALL_ERROR(InvalidArgument);
3847 return -1;
3848 }
3849 if (!pFile->sync()) {
3850 SYSCALL_ERROR(IoError);
3851 return -1;
3852 }
3853
3854 return 0;
3855}
3856
3857EXPORTED_PUBLIC int pedigree_get_mount(char* mount_buf, char* info_buf, size_t n) {
3858 if (!(PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(mount_buf), PATH_MAX,
3859 PosixSubsystem::SafeWrite) &&
3860 PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(info_buf), PATH_MAX,
3861 PosixSubsystem::SafeWrite))) {
3862 F_NOTICE("pedigree_get_mount -> invalid address");
3863 SYSCALL_ERROR(BadAddress);
3864 return -1;
3865 }
3866
3867 NOTICE("pedigree_get_mount(" << Dec << n << Hex << ")");
3868
3870 VFS::instance().getMounts(mounts);
3871
3872 size_t i = 0;
3873 for (const auto& mount : mounts) {
3874 if (i == n) {
3875 String info;
3876 if (mount.hasDisk) {
3877 info = mount.diskParentName;
3878 if (info.length() && mount.diskName.length()) {
3879 info += " // ";
3880 }
3881 info += mount.diskName;
3882 } else {
3883 info.assign("no disk", 8);
3884 }
3885
3886 if (!PosixSubsystem::copyToUser(mount_buf, mount.path.cstr(), mount.path.length() + 1) ||
3887 !PosixSubsystem::copyToUser(info_buf, info.cstr(), info.length() + 1)) {
3888 SYSCALL_ERROR(BadAddress);
3889 return -1;
3890 }
3891
3892 return 0;
3893 }
3894 ++i;
3895 }
3896
3897 return -1;
3898}
3899
3900int posix_chmod(const char* path, mode_t mode) {
3901 return posix_fchmodat(AT_FDCWD, path, mode, 0);
3902}
3903
3904int posix_chown(const char* path, uid_t owner, gid_t group) {
3905 return posix_fchownat(AT_FDCWD, path, owner, group, 0);
3906}
3907
3908int posix_fchmod(int fd, mode_t mode) {
3909 DescriptorLease descriptor;
3910 if (!acquireDescriptor(fd, descriptor) || !descriptor->getFile() ||
3911 (descriptor->getStatusFlags() & O_PATH)) {
3912 SYSCALL_ERROR(BadFileDescriptor);
3913 return -1;
3914 }
3915 if (mode == static_cast<mode_t>(-1)) {
3916 SYSCALL_ERROR(InvalidArgument);
3917 return -1;
3918 }
3919 VfsMountView::WriteLease mountWrite;
3920 if (descriptor->openingPath() && !mountWrite.acquire(descriptor->openingPath())) {
3921 return -1;
3922 }
3923 File* file = descriptor->getFile();
3924 if (file->getFilesystem() && file->getFilesystem()->isReadOnly()) {
3925 SYSCALL_ERROR(ReadOnlyFilesystem);
3926 return -1;
3927 }
3928 return posix_chmod_file(file, mode) ? 0 : -1;
3929}
3930
3931int posix_fchown(int fd, uid_t owner, gid_t group) {
3932 DescriptorLease descriptor;
3933 if (!acquireDescriptor(fd, descriptor) || !descriptor->getFile() ||
3934 (descriptor->getStatusFlags() & O_PATH)) {
3935 SYSCALL_ERROR(BadFileDescriptor);
3936 return -1;
3937 }
3938 VfsMountView::WriteLease mountWrite;
3939 if (descriptor->openingPath() && !mountWrite.acquire(descriptor->openingPath())) {
3940 return -1;
3941 }
3942 File* file = descriptor->getFile();
3943 if (file->getFilesystem() && file->getFilesystem()->isReadOnly()) {
3944 SYSCALL_ERROR(ReadOnlyFilesystem);
3945 return -1;
3946 }
3947 return posix_chown_file(file, owner, group) ? 0 : -1;
3948}
3949
3950int posix_fchdir(int fd) {
3951 F_NOTICE("fchdir(" << fd << ")");
3952
3953 // Lookup this process.
3954 Process* pProcess = Processor::information().getCurrentThread()->getParent();
3955 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
3956 if (!pSubsystem) {
3957 ERROR("No subsystem for this process!");
3958 return -1;
3959 }
3960
3961 ResolvedPath targetLease;
3962 DescriptorLease pFd;
3963 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
3964 // Error - no such file descriptor.
3965 SYSCALL_ERROR(BadFileDescriptor);
3966 return -1;
3967 }
3968
3969 File* file = pFd->getFile();
3970 targetLease.retain(pFd->openingPath());
3971 return doChdir(file, targetLease) ? 0 : -1;
3972}
3973
3974static int statvfs_doer(Filesystem* pFs, struct statvfs* userBuffer,
3975 const FilesystemPathRef& path) {
3976 if (!pFs) {
3977 SYSCALL_ERROR(DoesNotExist);
3978 return -1;
3979 }
3980
3981 struct statvfs value = {};
3982 struct statvfs* buf = &value;
3984 buf->f_bsize = 4096;
3985 buf->f_frsize = 512;
3986 buf->f_blocks = static_cast<fsblkcnt_t>(-1);
3987 buf->f_bfree = static_cast<fsblkcnt_t>(-1);
3988 buf->f_bavail = static_cast<fsblkcnt_t>(-1);
3989 buf->f_files = 0;
3990 buf->f_ffree = static_cast<fsfilcnt_t>(-1);
3991 buf->f_favail = static_cast<fsfilcnt_t>(-1);
3992 buf->f_fsid = 0;
3993 buf->f_flag = (pFs->isReadOnly() ? ST_RDONLY : 0) | ST_NOSUID; // No suid in pedigree yet.
3994 if (auto* view = VfsMountView::fromPath(path)) {
3995 if (view->mountFlags(path) & VfsMountView::ReadOnly) {
3996 buf->f_flag |= ST_RDONLY;
3997 }
3998 }
3999 buf->f_namemax = 0;
4000
4001 if (!PosixSubsystem::copyToUser(userBuffer, &value, sizeof(value))) {
4002 SYSCALL_ERROR(BadAddress);
4003 return -1;
4004 }
4005 return 0;
4006}
4007
4008int posix_fstatvfs(int fd, struct statvfs* buf) {
4009 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(buf), sizeof(struct statvfs),
4010 PosixSubsystem::SafeWrite)) {
4011 F_NOTICE("fstatvfs -> invalid address");
4012 SYSCALL_ERROR(BadAddress);
4013 return -1;
4014 }
4015
4016 F_NOTICE("fstatvfs(" << fd << ")");
4017
4018 // Lookup this process.
4019 Process* pProcess = Processor::information().getCurrentThread()->getParent();
4020 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
4021 if (!pSubsystem) {
4022 ERROR("No subsystem for this process!");
4023 return -1;
4024 }
4025
4026 DescriptorLease pFd;
4027 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
4028 // Error - no such file descriptor.
4029 SYSCALL_ERROR(BadFileDescriptor);
4030 return -1;
4031 }
4032
4033 File* file = pFd->getFile();
4034
4035 if (!file) {
4036 SYSCALL_ERROR(InvalidArgument);
4037 return -1;
4038 }
4039
4040 return statvfs_doer(file->getFilesystem(), buf, pFd->openingPath());
4041}
4042
4043int posix_statvfs(const char* path, struct statvfs* buf) {
4044 String pathCopy;
4045 if (!copyUserString(path, pathCopy)) {
4046 F_NOTICE("statvfs -> invalid address");
4047 return -1;
4048 }
4049 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(buf), sizeof(struct statvfs),
4050 PosixSubsystem::SafeWrite)) {
4051 F_NOTICE("statvfs -> invalid address");
4052 SYSCALL_ERROR(BadAddress);
4053 return -1;
4054 }
4055
4056 F_NOTICE("statvfs(" << pathCopy << ")");
4057
4058 String realPath;
4059 normalisePath(realPath, pathCopy.cstr());
4060
4061 ResolvedPath fileLease;
4062 File* file = findFilePath(realPath, fileLease);
4063 if (!file) {
4064 SYSCALL_ERROR(DoesNotExist);
4065 return -1;
4066 }
4067
4068 // Symlink traversal
4069 file = traverseSymlink(file, fileLease);
4070 if (!file)
4071 return -1;
4072
4073 return statvfs_doer(file->getFilesystem(), buf, fileLease.path());
4074}
4075
4076int posix_utime(const char* path, const struct utimbuf* times) {
4077 String pathCopy;
4078 if (!copyUserString(path, pathCopy)) {
4079 F_NOTICE("utimes -> invalid address");
4080 return -1;
4081 }
4082 struct utimbuf snapshot = {};
4083 if (times && !PosixSubsystem::copyFromUser(&snapshot, times, sizeof(snapshot))) {
4084 F_NOTICE("utimes -> invalid address");
4085 SYSCALL_ERROR(BadAddress);
4086 return -1;
4087 }
4088 if (times && (snapshot.actime < 0 || snapshot.modtime < 0 ||
4089 static_cast<uint64_t>(snapshot.actime) > UINT32_MAX ||
4090 static_cast<uint64_t>(snapshot.modtime) > UINT32_MAX)) {
4091 SYSCALL_ERROR(InvalidArgument);
4092 return -1;
4093 }
4094
4095 F_NOTICE("utime(" << pathCopy << ")");
4096
4097 String realPath;
4098 normalisePath(realPath, pathCopy.cstr());
4099
4100 ResolvedPath fileLease;
4101 File* file = findFilePath(realPath, fileLease);
4102 if (!file) {
4103 SYSCALL_ERROR(DoesNotExist);
4104 return -1;
4105 }
4106
4107 // Symlink traversal
4108 file = traverseSymlink(file, fileLease);
4109 if (!file)
4110 return -1;
4111
4112 if (!VFS::checkAccess(file, false, true, false)) {
4113 // checkAccess does a SYSCALL_ERROR for us.
4114 return -1;
4115 }
4116
4117 VfsMountView::WriteLease mountWrite;
4118 if (fileLease.path() && !mountWrite.acquire(fileLease.path())) {
4119 return -1;
4120 }
4121
4122 Time::Timestamp accessTime;
4123 Time::Timestamp modifyTime;
4124 if (times) {
4125 accessTime = snapshot.actime;
4126 modifyTime = snapshot.modtime;
4127 } else {
4128 accessTime = modifyTime = Time::getTime();
4129 }
4130
4131 file->setAccessedTime(accessTime);
4132 file->setModifiedTime(modifyTime);
4133
4134 return 0;
4135}
4136
4137int posix_utimes(const char* path, const struct timeval* times) {
4138 return posix_futimesat(AT_FDCWD, path, times);
4139}
4140
4141int posix_chroot(const char* path) {
4142 auto* process = Processor::information().getCurrentThread()->getParent();
4143 if (!posix_capable(PosixCapabilities::SysChroot)) {
4144 SYSCALL_ERROR(NotEnoughPermissions);
4145 return -1;
4146 }
4147 String copied;
4148 if (!copyUserString(path, copied))
4149 return -1;
4150 String normalised;
4151 normalisePath(normalised, copied.cstr());
4152 ResolvedPath target;
4153 if (!findFilePath(normalised, target, FilesystemPathRef(), true))
4154 return -1;
4155 auto context = process->acquireFilesystemContext();
4156 auto* view = VFS::instance().mountView();
4157 return view && context && view->changeRoot(context, target.path()) ? 0 : -1;
4158}
4159
4160int posix_flock(int fd, int operation) {
4161 F_NOTICE("flock(" << fd << ", " << operation << ")");
4162
4163 const int lockType = operation & ~LOCK_NB;
4164 if (lockType != LOCK_SH && lockType != LOCK_EX && lockType != LOCK_UN) {
4165 SYSCALL_ERROR(InvalidArgument);
4166 return -1;
4167 }
4168
4169 Process* process = Processor::information().getCurrentThread()->getParent();
4170 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
4171 if (!subsystem) {
4172 ERROR("No subsystem for this process!");
4173 return -1;
4174 }
4175
4176 DescriptorLease descriptor;
4177 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
4178 SYSCALL_ERROR(BadFileDescriptor);
4179 return -1;
4180 }
4181
4182 return posix_advisory_flock(descriptor, operation);
4183}
4184
4185static File* check_dirfd(int dirfd, DescriptorLease& descriptor, ResolvedPath& directory,
4186 int flags = 0) {
4187 auto* process = Processor::information().getCurrentThread()->getParent();
4188 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
4189 auto* view = VFS::instance().mountView();
4190 if (!subsystem || !view) {
4191 SYSCALL_ERROR(DoesNotExist);
4192 return nullptr;
4193 }
4194 if (dirfd == AT_FDCWD) {
4195 auto context = process->acquireFilesystemContext();
4197 if (!context || !context->snapshot(snapshot) || !snapshot.cwd) {
4198 SYSCALL_ERROR(DoesNotExist);
4199 return nullptr;
4200 }
4201 directory.retain(snapshot.cwd);
4202 return directory.get();
4203 }
4204 if (!subsystem->acquireFileDescriptor(dirfd, descriptor) || !descriptor->getFile()) {
4205 SYSCALL_ERROR(BadFileDescriptor);
4206 return nullptr;
4207 }
4208 File* file = descriptor->getFile();
4209 auto path = descriptor->openingPath();
4210 auto* openingView = VfsMountView::fromPath(path);
4211 if (!(flags & AT_EMPTY_PATH) &&
4212 (!file->isDirectory() || !openingView || !openingView->attachmentId(path))) {
4213 SYSCALL_ERROR(NotADirectory);
4214 return nullptr;
4215 }
4216 if (!path && !view->anonymousPath(file, path))
4217 return nullptr;
4218 directory.retain(path);
4219 return file;
4220}
4221
4222static bool findParentPath(const String& pathname, const FilesystemPathRef& start,
4223 ResolvedPath& result, String& basename) {
4224 auto context =
4225 Processor::information().getCurrentThread()->getParent()->acquireFilesystemContext();
4226 auto* view = VFS::instance().mountView();
4227 FilesystemPathRef parent;
4228 if (!context || !view) {
4229 SYSCALL_ERROR(DoesNotExist);
4230 return false;
4231 }
4232 if (!view->resolveParent(context, start, pathname, parent, basename))
4233 return false;
4234 result.retain(parent);
4235 return true;
4236}
4237
4238static File* findAtPath(int dirfd, const String& pathname, int flags, ResolvedPath& result,
4239 DescriptorLease& descriptor) {
4240 ResolvedPath start;
4241 File* anchor = check_dirfd(pathname.length() && pathname[0] == '/' ? AT_FDCWD : dirfd, descriptor,
4242 start, pathname.length() ? 0 : flags);
4243 if (!anchor)
4244 return nullptr;
4245 if (!pathname.length()) {
4246 if (!(flags & AT_EMPTY_PATH)) {
4247 SYSCALL_ERROR(DoesNotExist);
4248 return nullptr;
4249 }
4250 result.retain(start.path());
4251 return anchor;
4252 }
4253 String normalised;
4254 normalisePath(normalised, pathname.cstr());
4255 return findFilePath(normalised, result, start.path(), !(flags & AT_SYMLINK_NOFOLLOW));
4256}
4257
4258int posix_openat(int dirfd, const char* pathname, int flags, mode_t mode) {
4259 F_NOTICE("openat");
4260
4261 String pathnameCopy;
4262 if (!copyUserString(pathname, pathnameCopy)) {
4263 F_NOTICE("open -> invalid address");
4264 return -1;
4265 }
4266
4267 ResolvedPath cwdLease;
4268 DescriptorLease dirDescriptor;
4269 File* cwd = check_dirfd(pathnameCopy.length() && pathnameCopy[0] == '/' ? AT_FDCWD : dirfd,
4270 dirDescriptor, cwdLease);
4271 if (!cwd)
4272 return -1;
4273
4274 const bool pathOnly = flags & O_PATH;
4275 if (pathOnly)
4276 flags &= O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC;
4277
4278 F_NOTICE("openat(" << dirfd << ", " << pathnameCopy << ", " << flags << ", " << Oct << mode
4279 << ")");
4280
4281 // Lookup this process.
4282 Process* pProcess = Processor::information().getCurrentThread()->getParent();
4283 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
4284 if (!pSubsystem) {
4285 F_NOTICE(" -> No subsystem for this process!");
4286 return -1;
4287 }
4288
4289 // One of these three must be specified.
4290 if (!(CHECK_FLAG(flags, O_RDONLY) || CHECK_FLAG(flags, O_RDWR) || CHECK_FLAG(flags, O_WRONLY))) {
4291 F_NOTICE("One of O_RDONLY, O_WRONLY, or O_RDWR must be passed.");
4292 SYSCALL_ERROR(InvalidArgument);
4293 return -1;
4294 }
4295
4296 // verify the filename - don't try to open a dud file
4297 if (!pathnameCopy.length()) {
4298 F_NOTICE(" -> File does not exist (empty path).");
4299 SYSCALL_ERROR(DoesNotExist);
4300 return -1;
4301 }
4302
4303 PosixProcess* pPosixProcess = getPosixProcess();
4304 if (pPosixProcess) {
4305 mode &= ~pPosixProcess->getMask();
4306 }
4307
4308 size_t fd = pSubsystem->getFd();
4309
4310 ResolvedPath fileLease;
4311 File* file = 0;
4312
4313 bool openingCtty = false;
4314 Process::FileContextLease cttyLease;
4315 String nameToOpen;
4316 normalisePath(nameToOpen, pathnameCopy.cstr());
4317 file = findFilePath(nameToOpen, fileLease, cwdLease.path());
4318
4319 bool bCreated = false;
4320 if (!file) {
4321 const size_t lookupError = Processor::information().getCurrentThread()->getErrno();
4322 if (lookupError && lookupError != Error::DoesNotExist) {
4323 pSubsystem->freeFd(fd);
4324 return -1;
4325 }
4326 if (flags & O_CREAT) {
4327 F_NOTICE(" {O_CREAT}");
4328 ResolvedPath parent;
4329 String basename;
4330 bool worked = findParentPath(nameToOpen, cwdLease.path(), parent, basename) &&
4331 VFS::instance().mountView()->createFile(parent.path(), basename, mode);
4332 if (!worked) {
4333 // createFile should set the error if it fails.
4334 F_NOTICE(" -> File does not exist (createFile failed)");
4335 pSubsystem->freeFd(fd);
4336 return -1;
4337 }
4338
4339 file = findFilePath(basename, fileLease, parent.path());
4340 if (!file) {
4341 F_NOTICE(" -> File does not exist (O_CREAT failed)");
4342 if (!Processor::information().getCurrentThread()->getErrno())
4343 SYSCALL_ERROR(DoesNotExist);
4344 pSubsystem->freeFd(fd);
4345 return -1;
4346 }
4347
4348 bCreated = true;
4349 } else {
4350 F_NOTICE(" -> Does not exist.");
4351 // Error - not found.
4352 SYSCALL_ERROR(DoesNotExist);
4353 pSubsystem->freeFd(fd);
4354 return -1;
4355 }
4356 }
4357
4358 if (!file) {
4359 F_NOTICE(" -> File does not exist.");
4360 SYSCALL_ERROR(DoesNotExist);
4361 pSubsystem->freeFd(fd);
4362 return -1;
4363 }
4364
4365 if (file->isSymlink() && (flags & O_NOFOLLOW)) {
4366 if (!pathOnly) {
4367 SYSCALL_ERROR(LoopExists);
4368 pSubsystem->freeFd(fd);
4369 return -1;
4370 }
4371 } else {
4372 file = traverseSymlink(file, fileLease);
4373 }
4374
4375 if (!file) {
4376 if (!Processor::information().getCurrentThread()->getErrno())
4377 SYSCALL_ERROR(DoesNotExist);
4378 pSubsystem->freeFd(fd);
4379 return -1;
4380 }
4381
4382 if ((flags & O_DIRECTORY) && !file->isDirectory()) {
4383 SYSCALL_ERROR(NotADirectory);
4384 pSubsystem->freeFd(fd);
4385 return -1;
4386 }
4387 if (pathOnly) {
4388 auto* descriptor = new FileDescriptor(fileLease.path(), 0, fd, 0, flags);
4389 if (!descriptor) {
4390 pSubsystem->freeFd(fd);
4391 SYSCALL_ERROR(OutOfMemory);
4392 return -1;
4393 }
4394 pSubsystem->addFileDescriptor(fd, descriptor);
4395 return static_cast<int>(fd);
4396 }
4397
4398 if (file->isDirectory() && (flags & (O_WRONLY | O_RDWR))) {
4399 // Error - is directory.
4400 F_NOTICE(" -> Is a directory, and O_WRONLY or O_RDWR was specified.");
4401 SYSCALL_ERROR(IsADirectory);
4402 pSubsystem->freeFd(fd);
4403 return -1;
4404 }
4405
4406 if ((flags & O_CREAT) && (flags & O_EXCL) && !bCreated) {
4407 // file exists with O_CREAT and O_EXCL
4408 F_NOTICE(" -> File exists");
4409 SYSCALL_ERROR(FileExists);
4410 pSubsystem->freeFd(fd);
4411 return -1;
4412 }
4413
4414 auto* openingView = VfsMountView::fromPath(fileLease.path());
4415 const auto mountFlags = openingView ? openingView->mountFlags(fileLease.path()) : 0;
4416 if ((mountFlags & VfsMountView::NoDev) &&
4417 (file->isBlockDevice() || file->isCharacterDevice() ||
4418 (file->getFilesystem() == g_pDevFs && !file->isDirectory() && !file->isSymlink()) ||
4419 file->isDirectPhysicalMapping())) {
4420 SYSCALL_ERROR(PermissionDenied);
4421 pSubsystem->freeFd(fd);
4422 return -1;
4423 }
4425 if ((flags & (O_WRONLY | O_RDWR | O_TRUNC)) && file->supportsRegularFileOperations() &&
4426 !file->isBlockDevice() && fileLease.path() &&
4427 !VfsMountView::retainWrite(fileLease.path(), mountWrite)) {
4428 pSubsystem->freeFd(fd);
4429 return -1;
4430 }
4431
4432 bool allowTruncate = false;
4433 if (!posix_landlock_open(fileLease.path(), flags, allowTruncate)) {
4434 pSubsystem->freeFd(fd);
4435 return -1;
4436 }
4437
4438 if (g_pDevFs && g_pDevFs->isControllingTerminalSelector(file)) {
4439 openingCtty = true;
4440 file = pProcess->acquireCtty(cttyLease);
4441 if (!file) {
4442 pSubsystem->freeFd(fd);
4443 SYSCALL_ERROR(NoSuchDevice);
4444 return -1;
4445 }
4446 if (ConsoleManager::instance().isMasterConsole(file))
4447 file = ConsoleManager::instance().getOther(file);
4448 }
4449
4450 const bool checkRead = (flags & O_ACCMODE) != O_WRONLY;
4451
4452 UtsRef namespaceBacking;
4453 if (posix_uts_file_namespace(file, namespaceBacking) &&
4454 ((flags & O_ACCMODE) != O_RDONLY || (flags & O_TRUNC))) {
4455 SYSCALL_ERROR(PermissionDenied);
4456 pSubsystem->freeFd(fd);
4457 return -1;
4458 }
4459
4460 // Handle side effects.
4461 if (ConsoleManager::instance().isPtySlaveLocked(file)) {
4462 F_NOTICE(" -> PTY slave is locked");
4463 SYSCALL_ERROR(IoError);
4464 pSubsystem->freeFd(fd);
4465 return -1;
4466 }
4467
4468 RetainedFile openedFile;
4469 File* newFile = file->openForDescriptor(openedFile);
4470 if (!newFile) {
4471 pSubsystem->freeFd(fd);
4472 return -1;
4473 }
4474
4475 // Check for the desired permissions.
4476 // Note: we are permitted to create a file that we cannot open for writing
4477 // again. It will be open for the original mode requested if it was
4478 // created.
4479 if (!bCreated) {
4480 if (!VFS::checkAccess(file, checkRead, flags & (O_WRONLY | O_RDWR | O_TRUNC), false)) {
4481 // checkAccess does a SYSCALL_ERROR for us.
4482 F_NOTICE(" -> file access denied.");
4483 pSubsystem->freeFd(fd);
4484 return -1;
4485 }
4486 // Check for the desired permissions.
4487 if ((newFile != file) &&
4488 (!VFS::checkAccess(newFile, checkRead, flags & (O_WRONLY | O_RDWR | O_TRUNC), false))) {
4489 // checkAccess does a SYSCALL_ERROR for us.
4490 F_NOTICE(" -> file access denied.");
4491 pSubsystem->freeFd(fd);
4492 return -1;
4493 }
4494 }
4495
4496 // ensure we tweak the correct file now
4497 file = newFile;
4498
4499 // Check for console (as we have special handling needed here)
4500 if (ConsoleManager::instance().isConsole(file)) {
4501 // If a master console, attempt to lock.
4502 if (ConsoleManager::instance().isMasterConsole(file)) {
4503 // Lock the master, we now own it.
4504 // Or, we don't - if someone else has it open for example.
4505 if (!ConsoleManager::instance().lockConsole(file)) {
4506 F_NOTICE("Couldn't lock pseudoterminal master");
4507 SYSCALL_ERROR(DeviceBusy);
4508 pSubsystem->freeFd(fd);
4509 return -1;
4510 }
4511 } else {
4512 // Slave - set as controlling unless noctty is set.
4513 if ((flags & O_NOCTTY) == 0 && !openingCtty) {
4514 F_NOTICE(" -> setting opened terminal '" << file->getName() << "' to be controlling");
4515 if (TerminalControl::attach(*static_cast<ConsoleFile*>(file), false, true) < 0) {
4516 pSubsystem->freeFd(fd);
4517 return -1;
4518 }
4519 }
4520 }
4521 }
4522
4523 // Permissions were OK.
4524 if ((flags & O_TRUNC) && !file->isDirectory() && !file->isBlockDevice() && !file->isPipe() &&
4525 !file->isFifo() && file->getFilesystem() != g_pDevFs &&
4526 !ConsoleManager::instance().isConsole(file)) {
4527 F_NOTICE(" -> {O_TRUNC}");
4528 if (!file->resize(0)) {
4529 pSubsystem->freeFd(fd);
4530 return -1;
4531 }
4532 }
4533
4534 // Final checks.
4535 if (file->isFifo()) {
4536 F_NOTICE("FIFO => checking if we have any readers");
4537
4540 Pipe* pipe = Pipe::fromFile(file);
4541 if (flags & O_WRONLY) {
4542 const bool bCanBlock = !(flags & O_NONBLOCK);
4543 if (!pipe->waitForReader(bCanBlock)) {
4544 if (!bCanBlock) {
4545 // FIFO + no readers yet = ENXIO
4546 F_NOTICE(" -> ENXIO");
4547 SYSCALL_ERROR(NoSuchDevice);
4548 } else {
4549 F_NOTICE(" -> EINTR (fifo)");
4550 SYSCALL_ERROR(Interrupted);
4551 }
4552 pSubsystem->freeFd(fd);
4553 return -1;
4554 }
4555
4556 F_NOTICE("FIFO => successfully observed a reader");
4557 }
4558 }
4559
4560 FilesystemPathRef openedPath = fileLease.get() == file ? fileLease.path() : FilesystemPathRef();
4561 if (!openedPath && openingView && file->isCharacterDevice() &&
4562 file->getFilesystem() == fileLease.get()->getFilesystem() &&
4563 !openingView->pathForNode(fileLease.path(), file, openedPath)) {
4564 pSubsystem->freeFd(fd);
4565 return -1;
4566 }
4567 FileDescriptor* f = openedPath ? new FileDescriptor(openedPath, 0, fd, 0, flags)
4568 : new FileDescriptor(file, 0, fd, 0, flags);
4569 if (!f || !f->terminalAvailable()) {
4570 delete f;
4571 pSubsystem->freeFd(fd);
4572 SYSCALL_ERROR(OutOfMemory);
4573 return -1;
4574 }
4575 if (f) {
4576 if ((flags & O_ACCMODE) != O_RDONLY) {
4577 f->acquireOpenFileDescription()->mountWrite = mountWrite;
4578 }
4579 if (openingCtty) {
4580 f->acquireOpenFileDescription()->terminalContext = cttyLease.context();
4581 }
4582 f->setTruncateAllowed(allowTruncate);
4583 pSubsystem->addFileDescriptor(fd, f);
4584 file->publishEvent(FileEvents::Open);
4585 }
4586
4587 F_NOTICE(" -> " << fd);
4588
4589 return static_cast<int>(fd);
4590}
4591
4592int posix_mkdirat(int dirfd, const char* pathname, mode_t mode) {
4593 String copied;
4594 if (!copyUserString(pathname, copied))
4595 return -1;
4596 ResolvedPath start, parent;
4597 DescriptorLease descriptor;
4598 if (!check_dirfd(copied.length() && copied[0] == '/' ? AT_FDCWD : dirfd, descriptor, start))
4599 return -1;
4600 String normalised, basename;
4601 normalisePath(normalised, copied.cstr());
4602 if (!findParentPath(normalised, start.path(), parent, basename))
4603 return -1;
4604 if (!basename.length() || basename == "." || basename == "..") {
4605 SYSCALL_ERROR(FileExists);
4606 return -1;
4607 }
4608 if (auto* process = getPosixProcess())
4609 mode &= ~process->getMask();
4610 return VFS::instance().mountView()->createDirectory(parent.path(), basename, mode) ? 0 : -1;
4611}
4612
4613int posix_fchownat(int dirfd, const char* pathname, uid_t owner, gid_t group, int flags) {
4614 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) {
4615 SYSCALL_ERROR(InvalidArgument);
4616 return -1;
4617 }
4618 String copied;
4619 if ((!pathname && !(flags & AT_EMPTY_PATH)) || (pathname && !copyUserString(pathname, copied))) {
4620 if (!pathname)
4621 SYSCALL_ERROR(BadAddress);
4622 return -1;
4623 }
4624 ResolvedPath selected;
4625 DescriptorLease descriptor;
4626 File* file = findAtPath(dirfd, copied, flags, selected, descriptor);
4627 VfsMountView::WriteLease mountWrite;
4628 const auto path = selected.path() ? selected.path()
4629 : descriptor ? descriptor->openingPath()
4631 if (file && path && !mountWrite.acquire(path)) {
4632 return -1;
4633 }
4634 return file && posix_chown_file(file, owner, group) ? 0 : -1;
4635}
4636
4637int posix_futimesat(int dirfd, const char* pathname, const struct timeval* times) {
4638 F_NOTICE("futimesat");
4639
4640 String pathnameCopy;
4641 if (!copyUserString(pathname, pathnameCopy)) {
4642 F_NOTICE("utimes -> invalid address");
4643 return -1;
4644 }
4645 struct timeval snapshot[2] = {};
4646 if (times && !PosixSubsystem::copyFromUser(snapshot, times, sizeof(snapshot))) {
4647 F_NOTICE("utimes -> invalid address");
4648 SYSCALL_ERROR(BadAddress);
4649 return -1;
4650 }
4651 for (const struct timeval& value : snapshot) {
4652 if (times && (value.tv_sec < 0 || static_cast<uint64_t>(value.tv_sec) > UINT32_MAX ||
4653 value.tv_usec < 0 || value.tv_usec >= 1000000)) {
4654 SYSCALL_ERROR(InvalidArgument);
4655 return -1;
4656 }
4657 }
4658
4659 F_NOTICE("futimesat(" << dirfd << ", " << pathnameCopy << ", " << times << ")");
4660
4661 String realPath;
4662 normalisePath(realPath, pathnameCopy.cstr());
4663
4664 ResolvedPath fileLease;
4665 DescriptorLease descriptor;
4666 File* file = findAtPath(dirfd, realPath, 0, fileLease, descriptor);
4667 if (!file) {
4668 SYSCALL_ERROR(DoesNotExist);
4669 return -1;
4670 }
4671
4672 // Symlink traversal
4673 file = traverseSymlink(file, fileLease);
4674 if (!file)
4675 return -1;
4676
4677 if (!VFS::checkAccess(file, false, true, false)) {
4678 // checkAccess does a SYSCALL_ERROR for us.
4679 return -1;
4680 }
4681
4682 VfsMountView::WriteLease mountWrite;
4683 if (fileLease.path() && !mountWrite.acquire(fileLease.path())) {
4684 return -1;
4685 }
4686
4687 Time::Timestamp accessTime;
4688 Time::Timestamp modifyTime;
4689 if (times) {
4690 accessTime = snapshot[0].tv_sec;
4691 modifyTime = snapshot[1].tv_sec;
4692 } else {
4693 accessTime = modifyTime = Time::getTime();
4694 }
4695
4696 file->setAccessedTime(accessTime);
4697 file->setModifiedTime(modifyTime);
4698
4699 return 0;
4700}
4701
4702int posix_unlinkat(int dirfd, const char* pathname, int flags) {
4703 if (flags & ~AT_REMOVEDIR) {
4704 SYSCALL_ERROR(InvalidArgument);
4705 return -1;
4706 }
4707 String copied;
4708 if (!copyUserString(pathname, copied))
4709 return -1;
4710 ResolvedPath start, parent, selected;
4711 DescriptorLease descriptor;
4712 if (!check_dirfd(copied.length() && copied[0] == '/' ? AT_FDCWD : dirfd, descriptor, start))
4713 return -1;
4714 String normalised, basename;
4715 normalisePath(normalised, copied.cstr());
4716 LockGuard<Mutex> unixNamespaceGuard(UnixFilesystem::namespaceLock());
4717 if (!findParentPath(normalised, start.path(), parent, basename))
4718 return -1;
4719 if (!basename.length()) {
4720 syscallError(flags & AT_REMOVEDIR ? Error::DeviceBusy : Error::IsADirectory);
4721 return -1;
4722 }
4723 auto context =
4724 Processor::information().getCurrentThread()->getParent()->acquireFilesystemContext();
4726 options.followFinal = false;
4727 options.crossFinalMount = false;
4728 FilesystemPathRef entry;
4729 if (!VFS::instance().mountView()->resolve(context, parent.path(), basename, options, entry))
4730 return -1;
4731 selected.retain(entry);
4732 File* file = selected.get();
4733 if (copied.length() && copied[copied.length() - 1] == '/' && !file->isDirectory()) {
4734 SYSCALL_ERROR(NotADirectory);
4735 return -1;
4736 }
4737 if (file->isDirectory() != !!(flags & AT_REMOVEDIR)) {
4738 syscallError(file->isDirectory() ? Error::IsADirectory : Error::NotADirectory);
4739 return -1;
4740 }
4741 return VFS::instance().mountView()->remove(parent.path(), basename, file) ? 0 : -1;
4742}
4743
4744int posix_renameat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath) {
4745 String source, destination;
4746 if (!copyUserString(oldpath, source) || !copyUserString(newpath, destination))
4747 return -1;
4748 ResolvedPath oldStart, newStart, oldParent, newParent;
4749 DescriptorLease oldDescriptor, newDescriptor;
4750 if (!check_dirfd(source.length() && source[0] == '/' ? AT_FDCWD : olddirfd, oldDescriptor,
4751 oldStart) ||
4752 !check_dirfd(destination.length() && destination[0] == '/' ? AT_FDCWD : newdirfd,
4753 newDescriptor, newStart))
4754 return -1;
4755 String realSource, realDestination, oldName, newName;
4756 normalisePath(realSource, source.cstr());
4757 normalisePath(realDestination, destination.cstr());
4758 if (!findParentPath(realSource, oldStart.path(), oldParent, oldName) ||
4759 !findParentPath(realDestination, newStart.path(), newParent, newName))
4760 return -1;
4761 LockGuard<Mutex> unixNamespaceGuard(UnixFilesystem::namespaceLock());
4762 const bool directory = (source.length() && source[source.length() - 1] == '/') ||
4763 (destination.length() && destination[destination.length() - 1] == '/');
4764 return VFS::instance().mountView()->rename(oldParent.path(), oldName, newParent.path(), newName,
4765 false, directory)
4766 ? 0
4767 : -1;
4768}
4769
4770int posix_linkat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath, int flags) {
4771 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_FOLLOW)) {
4772 SYSCALL_ERROR(InvalidArgument);
4773 return -1;
4774 }
4775 String source, destination;
4776 if (!copyUserString(oldpath, source) || !copyUserString(newpath, destination))
4777 return -1;
4778 ResolvedPath target, start, parent;
4779 DescriptorLease sourceDescriptor, destinationDescriptor;
4780 const int lookupFlags =
4781 (flags & AT_EMPTY_PATH) | ((flags & AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW);
4782 if (!findAtPath(olddirfd, source, lookupFlags, target, sourceDescriptor))
4783 return -1;
4784 if (!check_dirfd(destination.length() && destination[0] == '/' ? AT_FDCWD : newdirfd,
4785 destinationDescriptor, start))
4786 return -1;
4787 String normalised, basename;
4788 normalisePath(normalised, destination.cstr());
4789 if (!findParentPath(normalised, start.path(), parent, basename))
4790 return -1;
4791 return VFS::instance().mountView()->createLink(parent.path(), basename, target.path()) ? 0 : -1;
4792}
4793
4794int posix_symlinkat(const char* oldpath, int newdirfd, const char* newpath) {
4795 String value, destination;
4796 if (!copyUserString(oldpath, value) || !copyUserString(newpath, destination))
4797 return -1;
4798 ResolvedPath start, parent;
4799 DescriptorLease descriptor;
4800 if (!check_dirfd(destination.length() && destination[0] == '/' ? AT_FDCWD : newdirfd, descriptor,
4801 start))
4802 return -1;
4803 String normalised, basename;
4804 normalisePath(normalised, destination.cstr());
4805 if (!findParentPath(normalised, start.path(), parent, basename))
4806 return -1;
4807 return VFS::instance().mountView()->createSymlink(parent.path(), basename, value) ? 0 : -1;
4808}
4809
4810int posix_readlinkat(int dirfd, const char* pathname, char* buf, size_t bufsiz) {
4811 F_NOTICE("readlinkat");
4812
4813 String pathnameCopy;
4814 if (!copyUserString(pathname, pathnameCopy)) {
4815 F_NOTICE("readlink -> invalid address");
4816 return -1;
4817 }
4818 ResolvedPath cwdLease;
4819 DescriptorLease dirDescriptor;
4820 File* cwd = check_dirfd(pathnameCopy.length() && pathnameCopy[0] == '/' ? AT_FDCWD : dirfd,
4821 dirDescriptor, cwdLease, pathnameCopy.length() ? 0 : AT_EMPTY_PATH);
4822 if (!cwd)
4823 return -1;
4824
4825 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(buf), bufsiz,
4826 PosixSubsystem::SafeWrite)) {
4827 F_NOTICE("readlink -> invalid address");
4828 SYSCALL_ERROR(BadAddress);
4829 return -1;
4830 }
4831
4832 F_NOTICE("readlinkat(" << dirfd << ", " << pathnameCopy << ", "
4833 << reinterpret_cast<uintptr_t>(buf) << ", " << bufsiz << ")");
4834
4835 String realPath;
4836 normalisePath(realPath, pathnameCopy.cstr());
4837
4838 ResolvedPath fileLease;
4839 File* f = pathnameCopy.length() ? findFilePath(realPath, fileLease, cwdLease.path()) : cwd;
4840 if (!f) {
4841 if (!Processor::information().getCurrentThread()->getErrno())
4842 SYSCALL_ERROR(DoesNotExist);
4843 return -1;
4844 }
4845
4846 if (!f->isSymlink()) {
4847 SYSCALL_ERROR(InvalidArgument);
4848 return -1;
4849 }
4850
4851 if (buf == 0)
4852 return -1;
4853
4854 if (!bufsiz) {
4855 SYSCALL_ERROR(InvalidArgument);
4856 return -1;
4857 }
4858 // Symlink target loading is bounded to one PATH_MAX-sized buffer.
4859 const size_t capacity = bufsiz < PATH_MAX ? bufsiz : PATH_MAX;
4861 if (!target) {
4862 SYSCALL_ERROR(OutOfMemory);
4863 return -1;
4864 }
4865 const int length = Symlink::fromFile(f)->followLink(target.get(), capacity);
4866 if (length < 0) {
4867 return length;
4868 }
4869 if (!PosixSubsystem::copyToUser(buf, target.get(), static_cast<size_t>(length))) {
4870 SYSCALL_ERROR(BadAddress);
4871 return -1;
4872 }
4873 return length;
4874}
4875
4876int posix_fchmodat(int dirfd, const char* pathname, mode_t mode, int flags) {
4877 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) {
4878 SYSCALL_ERROR(InvalidArgument);
4879 return -1;
4880 }
4881 String copied;
4882 if ((!pathname && !(flags & AT_EMPTY_PATH)) || (pathname && !copyUserString(pathname, copied))) {
4883 if (!pathname)
4884 SYSCALL_ERROR(BadAddress);
4885 return -1;
4886 }
4887 ResolvedPath selected;
4888 DescriptorLease descriptor;
4889 File* file = findAtPath(dirfd, copied, flags, selected, descriptor);
4890 VfsMountView::WriteLease mountWrite;
4891 const auto path = selected.path() ? selected.path()
4892 : descriptor ? descriptor->openingPath()
4894 if (file && path && !mountWrite.acquire(path)) {
4895 return -1;
4896 }
4897 return file && posix_chmod_file(file, mode) ? 0 : -1;
4898}
4899
4900int posix_faccessat(int dirfd, const char* pathname, int mode, int flags) {
4901 F_NOTICE("faccessat");
4902
4903 constexpr int validModes = R_OK | W_OK | X_OK;
4904 constexpr int validFlags = AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH;
4905 if ((mode & ~validModes) || (flags & ~validFlags)) {
4906 SYSCALL_ERROR(InvalidArgument);
4907 return -1;
4908 }
4909
4910 String pathnameCopy;
4911 if (!copyUserString(pathname, pathnameCopy)) {
4912 F_NOTICE("access -> invalid address");
4913 return -1;
4914 }
4915
4916 F_NOTICE("faccessat(" << dirfd << ", " << pathnameCopy << ", " << mode << ", " << flags << ")");
4917
4918 if (!pathnameCopy.length() && !(flags & AT_EMPTY_PATH)) {
4919 SYSCALL_ERROR(DoesNotExist);
4920 return -1;
4921 }
4922
4923 Process* process = Processor::information().getCurrentThread()->getParent();
4924 PosixSubsystem* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
4925 if (!subsystem) {
4926 ERROR("No subsystem for this process!");
4927 return -1;
4928 }
4929
4930 FilesystemCredentials accessCredentials;
4931 if (flags & AT_EACCESS) {
4932 if (!Process::currentFilesystemCredentials(accessCredentials)) {
4933 SYSCALL_ERROR(PermissionDenied);
4934 return -1;
4935 }
4936 } else if (process->getType() == Process::Posix) {
4937 accessCredentials = static_cast<PosixProcess*>(process)->realFilesystemCredentials();
4938 } else {
4939 if (!Process::currentFilesystemCredentials(accessCredentials)) {
4940 SYSCALL_ERROR(PermissionDenied);
4941 return -1;
4942 }
4943 const int64_t uid = process->getUserId(), gid = process->getGroupId();
4944 if (uid < 0 || gid < 0) {
4945 SYSCALL_ERROR(PermissionDenied);
4946 return -1;
4947 }
4948 accessCredentials.uid = uid;
4949 accessCredentials.gid = gid;
4950 }
4951 Process::FilesystemAccessScope accessScope(accessCredentials);
4952
4953 ResolvedPath fileLease;
4954 DescriptorLease descriptor;
4955 File* file = findAtPath(dirfd, pathnameCopy, flags, fileLease, descriptor);
4956
4957 if (!file) {
4958 F_NOTICE(" -> '" << pathnameCopy << "' does not exist");
4959 if (!Processor::information().getCurrentThread()->getErrno())
4960 SYSCALL_ERROR(DoesNotExist);
4961 return -1;
4962 }
4963
4964 // If we're only checking for existence, we're done here.
4965 if (mode == F_OK) {
4966 F_NOTICE(" -> ok");
4967 return 0;
4968 }
4969 const auto retainedPath = fileLease.path() ? fileLease.path()
4970 : descriptor ? descriptor->openingPath()
4972 auto* view = VfsMountView::fromPath(retainedPath);
4973 if (view && (mode & W_OK) && !file->isCharacterDevice() && !file->isBlockDevice() &&
4974 !file->isPipe() && !file->isFifo() && !file->isSocket() && !view->writable(retainedPath)) {
4975 return -1;
4976 }
4977 if (view && (mode & X_OK) && !file->isDirectory() &&
4978 (view->mountFlags(retainedPath) & VfsMountView::NoExec)) {
4979 SYSCALL_ERROR(PermissionDenied);
4980 return -1;
4981 }
4982
4983 if (!VFS::checkAccess(file, mode & R_OK, mode & W_OK, mode & X_OK, accessCredentials)) {
4984 // checkAccess does a SYSCALL_ERROR for us.
4985 F_NOTICE(" -> not ok");
4986 return -1;
4987 }
4988
4989 F_NOTICE(" -> ok");
4990 return 0;
4991}
4992
4993int posix_fstatat(int dirfd, const char* pathname, struct stat* buf, int flags) {
4994 F_NOTICE("fstatat");
4995 constexpr int noAutomount = 0x800;
4996 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW | noAutomount)) {
4997 SYSCALL_ERROR(InvalidArgument);
4998 return -1;
4999 }
5000 if (!pathname && !(flags & AT_EMPTY_PATH)) {
5001 SYSCALL_ERROR(BadAddress);
5002 return -1;
5003 }
5004
5005 if (!buf || !PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(buf), sizeof(struct stat),
5006 PosixSubsystem::SafeWrite)) {
5007 F_NOTICE("fstat -> invalid address");
5008 SYSCALL_ERROR(BadAddress);
5009 return -1;
5010 }
5011
5012 String pathnameCopy;
5013 if (pathname) {
5014 if (!copyUserString(pathname, pathnameCopy)) {
5015 F_NOTICE("fstat -> invalid address");
5016 return -1;
5017 }
5018 }
5019
5020 if ((flags & AT_EMPTY_PATH) && !pathnameCopy.length() && dirfd != AT_FDCWD) {
5021 DescriptorLease descriptor;
5022 if (!acquireDescriptor(dirfd, descriptor)) {
5023 SYSCALL_ERROR(BadFileDescriptor);
5024 return -1;
5025 }
5026 if (descriptor->getTimerFdImpl() || descriptor->getSignalFdImpl() ||
5027 descriptor->getFanotifyImpl()) {
5028 // Linux's anonymous descriptor types share a pseudo inode, without a
5029 // regular-file type or data extent. Keep its identity independent of
5030 // kernel addresses and outside the VFS's existing short device ids.
5031 struct stat snapshot = {};
5032 snapshot.st_dev = 0x10000;
5033 snapshot.st_ino = 1;
5034 snapshot.st_mode = 0600;
5035 snapshot.st_nlink = 1;
5036 snapshot.st_blksize = PhysicalMemoryManager::getPageSize();
5037 if (!PosixSubsystem::copyToUser(buf, &snapshot, sizeof(snapshot))) {
5038 SYSCALL_ERROR(BadAddress);
5039 return -1;
5040 }
5041 return 0;
5042 }
5043 if (!descriptor->getFile()) {
5044 SYSCALL_ERROR(BadFileDescriptor);
5045 return -1;
5046 }
5047 struct stat snapshot = {};
5048 if (!doStat(0, descriptor->getFile(), &snapshot))
5049 return -1;
5050 if (!PosixSubsystem::copyToUser(buf, &snapshot, sizeof(snapshot))) {
5051 SYSCALL_ERROR(BadAddress);
5052 return -1;
5053 }
5054 return 0;
5055 }
5056
5057 ResolvedPath fileLease;
5058 DescriptorLease descriptor;
5059 File* file = findAtPath(dirfd, pathnameCopy, flags, fileLease, descriptor);
5060 if (!file)
5061 return -1;
5062
5063 struct stat snapshot = {};
5064 if (!doStat(0, file, &snapshot)) {
5065 return -1;
5066 }
5067 if (!PosixSubsystem::copyToUser(buf, &snapshot, sizeof(snapshot))) {
5068 SYSCALL_ERROR(BadAddress);
5069 return -1;
5070 }
5071
5072 F_NOTICE(" -> Success");
5073 return 0;
5074}
5075
5076int posix_mknod(const char* pathname, mode_t mode, dev_t dev) {
5077 return posix_mknodat(AT_FDCWD, pathname, mode, dev);
5078}
5079
5080static int do_statfs(File* file, struct statfs* userBuffer) {
5081 if (!PosixSubsystem::checkAddress(reinterpret_cast<uintptr_t>(userBuffer), sizeof(struct statfs),
5082 PosixSubsystem::SafeWrite)) {
5083 F_NOTICE(" -> invalid address for buf [" << userBuffer << "]");
5084 SYSCALL_ERROR(BadAddress);
5085 return -1;
5086 }
5087
5088 if (!file) {
5089 F_NOTICE(" -> file does not exist");
5090 SYSCALL_ERROR(DoesNotExist);
5091 return -1;
5092 }
5093
5094 Filesystem* pFs = file->getFilesystem();
5095
5096 struct statfs value = {};
5097 struct statfs* buf = &value;
5099 bool bFilled = false;
5100 if (pFs == g_pDevFs) {
5101 F_NOTICE(" -> file '" << file->getName() << "' is on devfs");
5102
5103 // Special handling for devfs
5104 if (file->getName().compare("pts")) {
5105 F_NOTICE(" -> filling statfs struct with /dev/pts data");
5106 ByteSet(buf, 0, sizeof(*buf));
5107 buf->f_type = 0x1CD1; // DEVPTS_SUPER_MAGIC
5108 buf->f_bsize = 4096;
5109 buf->f_namelen = PATH_MAX;
5110 buf->f_frsize = 4096;
5111 bFilled = true;
5112 }
5113 }
5114
5115 if (!bFilled) {
5117 F_NOTICE(" -> filling statfs struct with ext2 data");
5118 ByteSet(buf, 0, sizeof(*buf));
5119 buf->f_type = 0xEF53; // EXT2_SUPER_MAGIC
5120 if (pFs->getDisk()) {
5121 buf->f_bsize = pFs->getDisk()->getBlockSize();
5122 buf->f_blocks = pFs->getDisk()->getSize() / pFs->getDisk()->getBlockSize();
5123 } else {
5124 buf->f_bsize = 4096;
5125 buf->f_blocks = 0x100000; // incorrect...
5126 }
5127 buf->f_bfree = buf->f_blocks;
5128 buf->f_bavail = buf->f_blocks;
5129 buf->f_files = 1234;
5130 buf->f_ffree = 5678;
5131 buf->f_namelen = PATH_MAX;
5132 buf->f_frsize = 0;
5133 }
5134 if (!pFs->getDisk()) {
5135 const auto& kind = pFs->getVolumeLabel();
5136 if (kind == "proc") {
5137 buf->f_type = 0x9fa0;
5138 } else if (kind == "devpts") {
5139 buf->f_type = 0x1cd1;
5140 } else if (kind == "ramfs") {
5141 buf->f_type = 0x858458f6;
5142 }
5143 }
5144
5145 if (!PosixSubsystem::copyToUser(userBuffer, &value, sizeof(value))) {
5146 SYSCALL_ERROR(BadAddress);
5147 return -1;
5148 }
5149 F_NOTICE(" -> ok");
5150 return 0;
5151}
5152
5153int posix_statfs(const char* path, struct statfs* buf) {
5154 F_NOTICE("statfs");
5155
5156 String pathCopy;
5157 if (!copyUserString(path, pathCopy)) {
5158 F_NOTICE(" -> invalid address for path");
5159 return -1;
5160 }
5161
5162 F_NOTICE("statfs(" << pathCopy << ")");
5163 String normalisedPath;
5164 normalisePath(normalisedPath, pathCopy.cstr());
5165 F_NOTICE(" -> actually performing statfs on " << normalisedPath);
5166 ResolvedPath fileLease;
5167 File* file = findFilePath(normalisedPath, fileLease);
5168
5169 return do_statfs(file, buf);
5170}
5171
5172int posix_fstatfs(int fd, struct statfs* buf) {
5173 F_NOTICE("fstatfs(" << fd << ")");
5174
5175 Process* pProcess = Processor::information().getCurrentThread()->getParent();
5176 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
5177 if (!pSubsystem) {
5178 ERROR("No subsystem for this process!");
5179 return -1;
5180 }
5181
5182 DescriptorLease pFd;
5183 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
5184 // Error - no such file descriptor.
5185 SYSCALL_ERROR(BadFileDescriptor);
5186 return -1;
5187 }
5188
5189 return do_statfs(pFd->getFile(), buf);
5190}
5191
5192void generate_mtab(String& result) {
5193 TerminationDeferral lifetime;
5194 result.clear();
5195 auto context =
5196 Processor::information().getCurrentThread()->getParent()->acquireFilesystemContext();
5197 auto* view = VFS::instance().mountView();
5199 if (!view || !context || !view->snapshotMounts(context, mounts))
5200 return;
5201 auto escape = [](const String& value) {
5202 String result;
5203 for (size_t i = 0; i < value.length(); ++i) {
5204 switch (value[i]) {
5205 case ' ':
5206 result += "\\040";
5207 break;
5208 case '\t':
5209 result += "\\011";
5210 break;
5211 case '\n':
5212 result += "\\012";
5213 break;
5214 case '\\':
5215 result += "\\134";
5216 break;
5217 default: {
5218 const char byte[] = {value[i], 0};
5219 result += byte;
5220 break;
5221 }
5222 }
5223 }
5224 return result;
5225 };
5226 for (const auto& mount : mounts) {
5228 if (!mount.backing.pin(pin))
5229 continue;
5230 String source = escape(pin.filesystem()->getVolumeLabel());
5231 String path = escape(mount.path);
5232 String line;
5233 line.Format(
5234 "%s %s unknown %s 0 0\n", source.length() ? source.cstr() : "none", path.cstr(),
5235 (pin.filesystem()->isReadOnly() || (mount.flags & VfsMountView::ReadOnly)) ? "ro" : "rw");
5236 result += line;
5237 }
5238}
Memory-mapped file interface.
bool setPtyLock(File *file, bool locked)
Set the UNIX 98 PTY slave lock associated with a master.
Definition Console.cc:133
void unlockConsole(File *file)
Release a console master locked as above.
Definition Console.cc:116
Definition DevFs.h:269
ReadStatus enumerate(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context)
Definition Directory.cc:224
static Directory * fromFile(File *pF)
Definition Directory.h:151
bool isDetached() const
Definition Directory.h:182
virtual size_t getSize() const
Gets the size of the disk.
Definition Disk.cc:344
virtual size_t getBlockSize() const
Gets the preferred I/O extent of the disk.
Definition Disk.cc:348
OpenFileDescriptionLease acquireOpenFileDescription() const
int fdflags
File descriptor flags (fcntl)
bool eventFdPublished() const
void removeStatusFlag(int flag)
Helper to remove a single flag from the status flags.
IoEvent * ioevent
IO event for reporting changes to files.
SharedPointer< InotifyInstance > getInotifyImpl() const
PositionGuard lockPosition() const
SharedPointer< EventFd > getEventFdImpl() const
int getStatusFlags() const
Get current status flags.
void addStatusFlag(int newFlag)
Helper to add a single flag to the status flags.
void setStatusFlags(int newFlags)
Set status flags, distributing any associated changes as needed.
SharedPointer< NetworkSyscalls > networkImpl
Network syscall implementation for this descriptor (if it's a socket).
size_t fd
Descriptor number.
bool networkPublished() const
Definition File.h:75
virtual void getFullPath(String &result, bool bWithMount=true)
Definition File.cc:1535
WriteGuard lockWrites()
Definition File.cc:420
bool resize(size_t size)
Definition File.cc:1213
void monitor(Thread *pThread, Event *pEvent)
Definition File.cc:1482
virtual bool isSeekable() const
Definition File.cc:820
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:239
virtual int command(const size_t command, void *buffer)
Definition File.cc:1073
bool supportsRegularFileOperations()
Definition File.cc:824
String getName() const
Definition File.cc:782
virtual bool isSocket() const
Definition File.cc:816
virtual bool isSymlink()
Definition File.cc:800
virtual bool isDirectory()
Definition File.cc:804
void setModifiedTime(Time::Timestamp t)
Definition File.cc:771
virtual bool sync()
Definition File.cc:595
void publishEvent(FileEventMask mask, const StringView &name=StringView(), bool targetIsDirectory=false)
Definition File.cc:861
virtual bool isFifo() const
Definition File.cc:812
virtual bool isPipe() const
Definition File.cc:808
void setAccessedTime(Time::Timestamp t)
Definition File.cc:740
virtual bool supports(const size_t command) const
Definition File.cc:1069
virtual File * openForDescriptor(RetainedFile &owner)
Definition File.cc:1361
virtual const String & getVolumeLabel() const =0
Disk * getDisk()
Definition Filesystem.h:145
bool isReadOnly()
Definition Filesystem.h:150
virtual void setLedState(char state)
Definition Keyboard.cc:29
virtual char getLedState()
Definition Keyboard.cc:25
static KeymapManager & instance()
Singleton design.
KeymapEntry * getKeymapEntry(bool bCtrl, bool bShift, bool bAlt, bool bAltGr, uint8_t nCombinator, uint8_t keyCode)
Returns the keymap entry corresponding to given keycode and modifiers.
uint8_t convertPc102ScancodeToHidKeycode(uint8_t scancode, EscapeState &escape)
Converts a pc102 scancode into a HID keycode.
virtual Keyboard * getKeyboard()=0
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
static MemoryMapManager & instance()
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
size_t removeAndRelease(uintptr_t base, size_t length, VmStatus *status=nullptr)
Definition Pipe.h:36
bool waitForReader(bool bCanBlock)
Definition Pipe.cc:320
static Pipe * fromFile(File *pF)
Definition Pipe.h:42
size_t readableBytes()
Definition Pipe.cc:93
size_t getReaderCount()
Definition Pipe.cc:310
virtual void threadException(Thread *pThread, ExceptionType eType, InterruptState *pState=nullptr, uintptr_t faultAddress=0, uintptr_t errorCode=0)
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static size_t readCachedFile(File &file, uint64_t offset, void *destination, size_t count)
size_t getFd(size_t minimum=0)
static UserStringResult copyUserString(const char *userString, String &copy, size_t maxLength)
void freeFd(size_t fdNum)
static bool checkUserAddressRange(uintptr_t addr, size_t count, size_t elementSize, size_t *extent=nullptr)
static bool checkUserBuffer(uintptr_t addr, size_t count, size_t elementSize, size_t flags, size_t *extent=nullptr)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
DescriptorDuplicationResult duplicateFileDescriptor(size_t source, size_t target, bool closeOnExec)
static bool checkAddress(uintptr_t addr, size_t extent, size_t flags)
Process * getParent()
Definition Process.h:620
virtual int64_t getUserId() const
Definition Process.cc:2238
static ProcessorInformation & information()
T * get() const
bool compare(const char *s, size_t len) const
Definition String.cc:202
void setErrno(size_t err)
Definition Thread.h:482
size_t getErrno()
Definition Thread.h:477
Process * getParent() const
Definition Thread.h:340
void getMounts(Vector< MountSnapshot > &mounts) const
Definition VFS.cc:854
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition VFS.cc:1502
static VFS & instance()
Definition VFS.cc:311
A vector / dynamic array.
Definition Vector.h:33
virtual uintptr_t getUserStart() const =0
virtual uintptr_t getKernelStart() const =0
bool activate(size_t n)
Starts the process of activating the given tty.
@ Oct
Definition Log.h:128
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
Structure representing an entry in the keymap table.
Definition cmd.h:30