The Pedigree Project 0.1
filesystem-capability-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "filesystem-capability-syscalls.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/Process.h"
5#include "pedigree/kernel/process/TerminationDeferral.h"
6#include "pedigree/kernel/process/Thread.h"
7#include "pedigree/kernel/processor/Processor.h"
8#include "pedigree/kernel/processor/ProcessorInformation.h"
9#include "pedigree/kernel/syscallError.h"
10
11#include <fcntl.h>
12#include <limits.h>
13
14#include "FileDescriptor.h"
15#include "PosixSubsystem.h"
16#include "ResolvedPath.h"
17#include "UnixFilesystem.h"
18#include "file-syscalls.h"
19#include "modules/system/vfs/File.h"
20#include "modules/system/vfs/MountView.h"
21#include "modules/system/vfs/VFS.h"
22
23namespace {
24constexpr uint64_t MaximumPosition = 0x7fffffffffffffffULL;
25constexpr unsigned RenameNoReplace = 1, RenameExchange = 2, RenameWhiteout = 4;
26
27struct FilesystemResult {
28 FilesystemResult(int result)
29 : value(result),
30 error(result < 0 ? Processor::information().getCurrentThread()->getErrno() : 0) {}
31 int value;
32 int error;
33};
34
35struct RenamePath {
36 ResolvedPath parent;
37 String copied, normalised, basename;
38 DescriptorLease descriptor;
39
40 bool copy(const char* user) {
41 const auto result = PosixSubsystem::copyUserString(user, copied, PATH_MAX);
42 if (result != PosixSubsystem::UserStringSuccess) {
43 syscallError(result == PosixSubsystem::UserStringBadAddress ? Error::BadAddress
44 : Error::NameTooLong);
45 return false;
46 }
47 if (!copied.length()) {
48 SYSCALL_ERROR(DoesNotExist);
49 return false;
50 }
51 normalisePath(normalised, copied.cstr());
52 return true;
53 }
54
55 bool resolve(int fd, VfsMountView& view, const FilesystemContextRef& context) {
56 Process* process = Processor::information().getCurrentThread()->getParent();
58 if (copied[0] != '/' && fd != AT_FDCWD) {
59 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
60 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor) ||
61 !descriptor->getFile()) {
62 SYSCALL_ERROR(BadFileDescriptor);
63 return false;
64 }
65 start = descriptor->openingPath();
66 if (!descriptor->getFile()->isDirectory() || !start) {
67 SYSCALL_ERROR(NotADirectory);
68 return false;
69 }
70 }
71 FilesystemPathRef selected;
72 if (!view.resolveParent(context, start, normalised, selected, basename))
73 return false;
74 parent.retain(selected);
75 return true;
76 }
77};
78FilesystemResult allocate(int fd, int mode, off_t offset, off_t length) {
79 TerminationDeferral lifetime;
80 Thread* thread = Processor::information().getCurrentThread();
81 auto* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
82 DescriptorLease descriptor;
83 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor) ||
84 (descriptor->getStatusFlags() & O_PATH) || !descriptor->getFile()) {
85 SYSCALL_ERROR(BadFileDescriptor);
86 return -1;
87 }
88 if (offset < 0 || length <= 0) {
89 SYSCALL_ERROR(InvalidArgument);
90 return -1;
91 }
92 constexpr unsigned KeepSize = 1;
93 if (static_cast<unsigned>(mode) & ~KeepSize) {
94 SYSCALL_ERROR(OperationNotSupported);
95 return -1;
96 }
97 const int access = descriptor->getStatusFlags() & O_ACCMODE;
98 if (access != O_WRONLY && access != O_RDWR) {
99 SYSCALL_ERROR(BadFileDescriptor);
100 return -1;
101 }
102 VfsMountView::WriteLease mountWrite;
103 if (descriptor->openingPath() && !mountWrite.acquire(descriptor->openingPath())) {
104 return -1;
105 }
106 File* file = descriptor->getFile();
107 if (file->isPipe() || file->isFifo()) {
108 SYSCALL_ERROR(IllegalSeek);
109 return -1;
110 }
111 if (file->isDirectory()) {
112 SYSCALL_ERROR(IsADirectory);
113 return -1;
114 }
115 if (!file->supportsRegularFileOperations()) {
116 SYSCALL_ERROR(DeviceDoesNotExist);
117 return -1;
118 }
119 if (static_cast<uint64_t>(length) > MaximumPosition - static_cast<uint64_t>(offset)) {
120 SYSCALL_ERROR(FileTooLarge);
121 return -1;
122 }
123 thread->setErrno(0);
124 if (!file->allocateRange(offset, length, mode & KeepSize)) {
125 if (!thread->getErrno())
126 SYSCALL_ERROR(IoError);
127 return -1;
128 }
129 thread->setErrno(0);
130 return 0;
131}
132
133FilesystemResult renameWithFlags(int oldDirFd, const char* oldPath, int newDirFd,
134 const char* newPath, unsigned flags) {
135 if ((flags & ~(RenameNoReplace | RenameExchange | RenameWhiteout)) ||
136 ((flags & RenameExchange) && (flags & (RenameNoReplace | RenameWhiteout)))) {
137 SYSCALL_ERROR(InvalidArgument);
138 return -1;
139 }
140 if (flags & (RenameExchange | RenameWhiteout)) {
141 SYSCALL_ERROR(OperationNotSupported);
142 return -1;
143 }
144 if (!flags)
145 return posix_renameat(oldDirFd, oldPath, newDirFd, newPath);
146 TerminationDeferral lifetime;
147 RenamePath source, destination;
148 if (!source.copy(oldPath) || !destination.copy(newPath))
149 return -1;
150 auto* process = Processor::information().getCurrentThread()->getParent();
151 auto context = process->acquireFilesystemContext();
152 auto* view = VFS::instance().mountView();
153 if (!context || !view) {
154 SYSCALL_ERROR(DoesNotExist);
155 return -1;
156 }
157 if (!source.resolve(oldDirFd, *view, context) || !destination.resolve(newDirFd, *view, context))
158 return -1;
159 LockGuard<Mutex> namespaceGuard(UnixFilesystem::namespaceLock());
160 return view->rename(source.parent.path(), source.basename, destination.parent.path(),
161 destination.basename, true,
162 source.copied.endswith('/') || destination.copied.endswith('/'))
163 ? 0
164 : -1;
165}
166
167} // namespace
168
169int posix_fallocate(int fd, int mode, off_t offset, off_t length) {
170 const FilesystemResult result = allocate(fd, mode, offset, length);
171 syscallError(result.error);
172 return result.value;
173}
174
175int posix_renameat2(int oldDirFd, const char* oldPath, int newDirFd, const char* newPath,
176 unsigned flags) {
177 const FilesystemResult result = renameWithFlags(oldDirFd, oldPath, newDirFd, newPath, flags);
178 syscallError(result.error);
179 return result.value;
180}
int getStatusFlags() const
Get current status flags.
Definition File.h:75
bool supportsRegularFileOperations()
Definition File.cc:824
bool allocateRange(size_t offset, size_t length, bool keepSize=false)
virtual bool isDirectory()
Definition File.cc:804
virtual bool isFifo() const
Definition File.cc:812
virtual bool isPipe() const
Definition File.cc:808
static UserStringResult copyUserString(const char *userString, String &copy, size_t maxLength)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
void setErrno(size_t err)
Definition Thread.h:482
size_t getErrno()
Definition Thread.h:477
Process * getParent() const
Definition Thread.h:340
static VFS & instance()
Definition VFS.cc:311