8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/utilities/utility.h"
19#include "modules/subsys/posix/FileDescriptor.h"
20#include "modules/subsys/posix/PosixProcess.h"
21#include "modules/subsys/posix/PosixSubsystem.h"
22#include "modules/subsys/posix/UnixFilesystem.h"
23#include "modules/subsys/posix/file-syscalls.h"
24#include "modules/subsys/posix/pipe-syscalls.h"
30constexpr size_t ReadDescriptor = 110;
31constexpr size_t WriteDescriptor = 111;
32constexpr size_t ReadWriteDescriptor = 112;
33constexpr size_t NonBlockingWriteDescriptor = 113;
34constexpr size_t LargeFileSize = (
static_cast<size_t>(1) << 33) + 513;
35constexpr uintptr_t LargeInode = (
static_cast<uintptr_t
>(1) << 33) + 0x5678;
37class ContractFile final :
public File {
40 :
File(
String(
"contract-file"), 0, 0, 0, LargeInode, filesystem, LargeFileSize, parent),
43 m_Bytes{
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h'} {}
49 bool sync()
override {
55 size_t backendError = 0;
56 uint64_t maximumWrite = ~static_cast<uint64_t>(0);
57 bool syncSucceeds =
true;
64 uint64_t
readBytewise(uint64_t offset, uint64_t size, uintptr_t buffer,
bool)
override {
66 if (offset >=
sizeof(m_Bytes)) {
69 if (size >
sizeof(m_Bytes) - offset) {
70 size =
sizeof(m_Bytes) - offset;
72 MemoryCopy(
reinterpret_cast<void*
>(buffer), m_Bytes + offset, size);
76 uint64_t
writeBytewise(uint64_t offset, uint64_t size, uintptr_t buffer,
bool)
override {
81 if (size > maximumWrite) {
84 if (offset >=
sizeof(m_Bytes)) {
87 if (size >
sizeof(m_Bytes) - offset) {
88 size =
sizeof(m_Bytes) - offset;
90 MemoryCopy(m_Bytes + offset,
reinterpret_cast<void*
>(buffer), size);
98struct ContractContext {
99 explicit ContractContext(ContractFile* target)
117bool expectBadDescriptor(
Thread* thread,
int result) {
118 return result == -1 && thread->
getErrno() == Error::BadFileDescriptor;
121int contractWorker(
void* parameter) {
122 ContractContext* context =
reinterpret_cast<ContractContext*
>(parameter);
126 uintptr_t address = 0;
127 if (!process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
128 context->returned += 1;
131 uintptr_t mappedAddress = address;
133 MemoryMappedObject::Read | MemoryMappedObject::Write) ||
134 mappedAddress != address) {
136 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
137 context->returned += 1;
141 char* data =
reinterpret_cast<char*
>(address);
142 struct iovec* vector =
reinterpret_cast<struct iovec*
>(address + 64);
143 struct stat* status =
reinterpret_cast<struct stat*
>(address + 128);
144 char* path =
reinterpret_cast<char*
>(address + 512);
145 int* pipes =
reinterpret_cast<int*
>(address + 768);
147 vector->iov_base = data;
150 bool denied = expectBadDescriptor(thread, posix_read(WriteDescriptor, data, 1));
151 denied &= expectBadDescriptor(thread, posix_readv(WriteDescriptor, vector, 1));
152 denied &= expectBadDescriptor(thread, posix_write(ReadDescriptor, data, 1));
153 denied &= expectBadDescriptor(thread, posix_writev(ReadDescriptor, vector, 1));
154 denied &= expectBadDescriptor(thread, posix_read(WriteDescriptor, data, 0));
155 denied &= expectBadDescriptor(thread, posix_write(ReadDescriptor, data, 0));
156 denied &= expectBadDescriptor(thread, posix_readv(WriteDescriptor, vector, 0));
157 denied &= expectBadDescriptor(thread, posix_writev(ReadDescriptor, vector, 0));
159 denied &= expectBadDescriptor(thread, posix_readv(WriteDescriptor, vector, 1));
160 denied &= expectBadDescriptor(thread, posix_writev(ReadDescriptor, vector, 1));
161 context->denied = denied && !context->file->readCalls && !context->file->writeCalls;
164 bool allowed = posix_read(ReadDescriptor, data, 1) == 1 && *data ==
'a';
165 allowed &= posix_readv(ReadDescriptor, vector, 1) == 1 && *data ==
'b';
167 allowed &= posix_write(WriteDescriptor, data, 1) == 1;
169 allowed &= posix_writev(WriteDescriptor, vector, 1) == 1;
170 allowed &= posix_read(ReadWriteDescriptor, data, 1) == 1 && *data ==
'w';
172 allowed &= posix_write(ReadWriteDescriptor, data, 1) == 1;
173 allowed &= posix_readv(ReadWriteDescriptor, vector, 1) == 1 && *data ==
'c';
175 allowed &= posix_writev(ReadWriteDescriptor, vector, 1) == 1;
176 allowed &= posix_read(ReadDescriptor, data, 0) == 0;
177 allowed &= posix_write(WriteDescriptor, data, 0) == 0;
178 allowed &= posix_readv(ReadDescriptor, vector, 0) == 0;
179 allowed &= posix_writev(WriteDescriptor, vector, 0) == 0;
180 context->allowed = allowed;
182 StringCopy(path,
"/contract-write-only");
184 const int deniedOpen = posix_openat(AT_FDCWD, path, O_RDONLY | O_CLOEXEC | O_NONBLOCK, 0);
185 bool open = deniedOpen == -1 && thread->
getErrno() == Error::PermissionDenied;
186 if (deniedOpen >= 0) {
187 posix_close(deniedOpen);
189 const int writeOpen = posix_openat(AT_FDCWD, path, O_WRONLY | O_CLOEXEC, 0);
190 open &= writeOpen >= 0;
191 if (writeOpen >= 0) {
192 open &= posix_close(writeOpen) == 0;
194 StringCopy(path,
"/contract-file");
195 const int readOpen = posix_openat(AT_FDCWD, path, O_RDONLY | O_CLOEXEC | O_NONBLOCK, 0);
196 open &= readOpen >= 0;
198 open &= posix_close(readOpen) == 0;
200 context->open = open;
202 bool metadata = posix_fstat(ReadDescriptor, status) == 0 && S_ISREG(status->st_mode) &&
203 status->st_size ==
static_cast<off_t
>(LargeFileSize) &&
204 status->st_ino == LargeInode && status->st_blksize == 4096 &&
205 status->st_blocks ==
static_cast<blkcnt_t
>(LargeFileSize / 512 + 1);
206 if (posix_pipe(pipes) == 0) {
207 metadata &= posix_fstat(pipes[0], status) == 0 && S_ISFIFO(status->st_mode);
208 metadata &= posix_close(pipes[0]) == 0;
209 metadata &= posix_close(pipes[1]) == 0;
213 context->metadata = metadata;
215 bool writeErrors =
true;
216 context->file->backendError = Error::NoSpaceLeftOnDevice;
217 context->file->maximumWrite = 0;
219 const size_t writeDescriptors[] = {WriteDescriptor, NonBlockingWriteDescriptor};
220 for (
size_t descriptor : writeDescriptors) {
222 posix_write(descriptor, data, 1) == -1 && thread->
getErrno() == Error::NoSpaceLeftOnDevice;
223 writeErrors &= posix_pwrite64(descriptor, data, 1, 0) == -1 &&
224 thread->
getErrno() == Error::NoSpaceLeftOnDevice;
225 writeErrors &= posix_writev(descriptor, vector, 1) == -1 &&
226 thread->
getErrno() == Error::NoSpaceLeftOnDevice;
227 writeErrors &= posix_pwritev(descriptor, vector, 1, 0) == -1 &&
228 thread->
getErrno() == Error::NoSpaceLeftOnDevice;
230 context->file->backendError = 0;
231 context->file->maximumWrite = ~static_cast<uint64_t>(0);
233 writeErrors &= posix_write(WriteDescriptor, data, 1) == 1 && thread->
getErrno() == 0;
235 writeErrors &= posix_pwrite64(WriteDescriptor, data, 1, 0) == 1 && thread->
getErrno() == 0;
237 writeErrors &= posix_writev(WriteDescriptor, vector, 1) == 1 && thread->
getErrno() == 0;
239 writeErrors &= posix_pwritev(WriteDescriptor, vector, 1, 0) == 1 && thread->
getErrno() == 0;
241 context->file->backendError = Error::IoError;
242 context->file->maximumWrite = 1;
244 writeErrors &= posix_write(WriteDescriptor, data, 2) == 1 && thread->
getErrno() == 0;
245 writeErrors &= posix_pwrite64(WriteDescriptor, data, 2, 0) == 1 && thread->
getErrno() == 0;
246 writeErrors &= posix_writev(WriteDescriptor, vector, 1) == 1 && thread->
getErrno() == 0;
247 writeErrors &= posix_pwritev(WriteDescriptor, vector, 1, 0) == 1 && thread->
getErrno() == 0;
248 context->file->backendError = 0;
249 context->file->maximumWrite = ~static_cast<uint64_t>(0);
250 context->file->syncSucceeds =
false;
251 writeErrors &= posix_fsync(ReadDescriptor) == -1 && thread->
getErrno() == Error::IoError;
252 context->file->syncSucceeds =
true;
253 writeErrors &= posix_fsync(ReadDescriptor) == 0;
254 writeErrors &= posix_fsync(-1) == -1 && thread->
getErrno() == Error::BadFileDescriptor;
255 context->writeErrors = writeErrors;
258 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
259 context->returned += 1;
264bool runHostedFileContractRegressions(
Process* kernelProcess) {
267 VFS::HostedRootViewScope fixture;
269 if (!fixture.open(filesystem)) {
275 ContractFile* file =
new ContractFile(filesystem, root);
277 file->setPermissions(FILE_UR | FILE_UW);
278 const bool fileAdded = directory->addEntry(file->getName(), file);
279 File* writeOnly =
new File(
String(
"contract-write-only"), 0, 0, 0, 2, filesystem, 0, root);
280 writeOnly->setUid(200);
281 writeOnly->setPermissions(FILE_UW);
282 const bool writeOnlyAdded = directory->addEntry(writeOnly->
getName(), writeOnly);
285 new PosixProcess(kernelProcess,
true, Process::FilesystemContextMode::Deferred);
287 process->setSubsystem(subsystem);
288 subsystem->
setAbi(PosixSubsystem::LinuxAbi);
289 const bool contextInstalled = fixture.installContext(*process);
290 process->setUserId(200);
291 process->setEffectiveUserId(200);
299 NonBlockingWriteDescriptor,
300 new FileDescriptor(file, 0, NonBlockingWriteDescriptor, 0, O_WRONLY | O_NONBLOCK));
302 ContractContext context(file);
303 Thread*
worker =
new Thread(process, contractWorker, &context,
nullptr,
false,
true,
true);
304 worker->setName(
"hosted file contracts");
305 const bool started = fileAdded && writeOnlyAdded && contextInstalled &&
worker->start();
306 const bool joined = started &&
worker->joinForCompletion();
310 bool passed = started && joined && context.returned == 1 && context.denied && context.allowed &&
311 context.open && context.metadata && context.writeErrors;
314 const bool rootRestored = fixture.close();
316 FATAL(
"Hosted filesystem fixture retained owners after teardown");
322 ERROR(
"HOSTED-SYSCALL-TEST: FAIL file-contracts: denied="
323 << context.denied <<
", allowed=" << context.allowed <<
", open=" << context.open
324 <<
", metadata=" << context.metadata <<
", writeErrors=" << context.writeErrors);
327 NOTICE(
"HOSTED-SYSCALL-TEST: PASS file-contracts");
Memory-mapped file interface.
static Directory * fromFile(File *pF)
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isBytewise() const
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual size_t getBlockSize() const
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
void freeMultipleFds(bool bOnlyCloExec=false, size_t iFirst=0, size_t iLast=-1)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
static ProcessorInformation & information()
void setErrno(size_t err)
Process * getParent() const
virtual File * getRoot() const
Filesystem * getRootFilesystem() const