The Pedigree Project 0.1
file-sync-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Process.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8
9#include <fcntl.h>
10
11#include "FileDescriptor.h"
12#include "PosixSubsystem.h"
13#include "file-sync-syscalls.h"
14#include "modules/system/vfs/File.h"
15
16namespace {
17constexpr uint64_t MaximumPosition = 0x7fffffffffffffffULL;
18constexpr size_t PrefetchWindow = 2 * 1024 * 1024;
19constexpr int AdviceNormal = 0, AdviceWillNeed = 3, AdviceNoReuse = 5;
20
21bool acquireSyncDescriptor(int fd, DescriptorLease& descriptor) {
22 Thread* thread = Processor::information().getCurrentThread();
23 auto* subsystem = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
24 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor) ||
25 (descriptor->getStatusFlags() & O_PATH)) {
26 SYSCALL_ERROR(BadFileDescriptor);
27 return false;
28 }
29 return true;
30}
31
32int finishSync(Thread* thread, bool succeeded) {
33 if (!succeeded) {
34 if (!thread->getErrno())
35 SYSCALL_ERROR(IoError);
36 return -1;
37 }
38 thread->setErrno(0);
39 return 0;
40}
41
42int prefetch(File* file, uint64_t offset, uint64_t length, bool throughEof) {
43 Thread* thread = Processor::information().getCurrentThread();
44 // Freeze the EOF bound while a bounded cache fill runs, without changing the OFD position.
45 auto guard = file->lockWrites();
46 const uint64_t size = file->getSize();
47 if (offset >= size)
48 return 0;
49 uint64_t count = size - offset;
50 if (!throughEof && length < count)
51 count = length;
52 if (count > PrefetchWindow)
53 count = PrefetchWindow;
54 thread->setErrno(0);
55 const uint64_t read = file->read(offset, count, 0);
56 return finishSync(thread, read == count);
57}
58} // namespace
59
60int posix_fdatasync(int fd) {
61 TerminationDeferral lifetime;
62 DescriptorLease descriptor;
63 if (!acquireSyncDescriptor(fd, descriptor))
64 return -1;
65 File* file = descriptor->getFile();
66 if (!file || (!file->supportsRegularFileOperations() && !file->isDirectory())) {
67 SYSCALL_ERROR(InvalidArgument);
68 return -1;
69 }
70 Thread* thread = Processor::information().getCurrentThread();
71 thread->setErrno(0);
72 // The existing backend also flushes metadata not required for data retrieval.
73 return finishSync(thread, file->sync());
74}
75
76int posix_readahead(int fd, off_t offset, size_t count) {
77 TerminationDeferral lifetime;
78 DescriptorLease descriptor;
79 if (!acquireSyncDescriptor(fd, descriptor))
80 return -1;
81 const int access = descriptor->getStatusFlags() & O_ACCMODE;
82 if (access != O_RDONLY && access != O_RDWR) {
83 SYSCALL_ERROR(BadFileDescriptor);
84 return -1;
85 }
86 File* file = descriptor->getFile();
87 if (!file || !file->supportsRegularFileOperations() || offset < 0 || count > MaximumPosition) {
88 SYSCALL_ERROR(InvalidArgument);
89 return -1;
90 }
91 // Linux routes this through WILLNEED, whose zero length covers the suffix.
92 return prefetch(file, offset, count, !count);
93}
94
95int posix_fadvise64(int fd, off_t offset, off_t length, int advice) {
96 TerminationDeferral lifetime;
97 DescriptorLease descriptor;
98 if (!acquireSyncDescriptor(fd, descriptor))
99 return -1;
100 File* file = descriptor->getFile();
101 if (file && (file->isPipe() || file->isFifo())) {
102 SYSCALL_ERROR(IllegalSeek);
103 return -1;
104 }
105 if (!file || !file->supportsRegularFileOperations() || offset < 0 || length < 0 ||
106 advice < AdviceNormal || advice > AdviceNoReuse) {
107 SYSCALL_ERROR(InvalidArgument);
108 return -1;
109 }
110 if (advice != AdviceWillNeed) {
111 SYSCALL_ERROR(OperationNotSupported);
112 return -1;
113 }
114 return prefetch(file, offset, length, !length);
115}
116
117int posix_sync_file_range(int fd, off_t offset, off_t length, unsigned flags) {
118 TerminationDeferral lifetime;
119 DescriptorLease descriptor;
120 if (!acquireSyncDescriptor(fd, descriptor))
121 return -1;
122 constexpr unsigned ValidFlags = 1 | 2 | 4;
123 if ((flags & ~ValidFlags) || offset < 0 || length < 0 ||
124 static_cast<uint64_t>(length) > MaximumPosition - static_cast<uint64_t>(offset)) {
125 SYSCALL_ERROR(InvalidArgument);
126 return -1;
127 }
128 File* file = descriptor->getFile();
129 if (!file || (!file->supportsRegularFileOperations() && !file->isDirectory())) {
130 SYSCALL_ERROR(IllegalSeek);
131 return -1;
132 }
133 if (!flags)
134 return 0;
135 Thread* thread = Processor::information().getCurrentThread();
136 thread->setErrno(0);
137 // All admitted combinations complete range writeback synchronously, including waits.
138 return finishSync(thread, file->syncRange(offset, length));
139}
int getStatusFlags() const
Get current status flags.
Definition File.h:75
WriteGuard lockWrites()
Definition File.cc:420
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:239
bool supportsRegularFileOperations()
Definition File.cc:824
bool syncRange(size_t offset, size_t length)
Definition File.cc:599
virtual bool isDirectory()
Definition File.cc:804
virtual bool sync()
Definition File.cc:595
virtual bool isFifo() const
Definition File.cc:812
virtual bool isPipe() const
Definition File.cc:808
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