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#include "pedigree/kernel/utilities/Pointers.h"
9#include "pedigree/kernel/utilities/assert.h"
13#include "FileDescriptor.h"
14#include "PosixSubsystem.h"
15#include "modules/system/vfs/File.h"
16#include "modules/system/vfs/MountView.h"
17#include "net-syscalls.h"
18#include "transfer-syscalls.h"
21constexpr uint64_t MaximumPosition = 0x7fffffffffffffffULL;
22constexpr size_t MaximumTransfer = 0x7ffff000;
23constexpr size_t BufferCapacity = 64 * 1024;
25using Endpoint = PositionGuard::Endpoint;
27bool signedRange(uint64_t position,
size_t count) {
28 if (position > MaximumPosition || count > MaximumPosition - position) {
29 SYSCALL_ERROR(InvalidArgument);
35bool regularFile(
File* file) {
37 SYSCALL_ERROR(IsADirectory);
41 SYSCALL_ERROR(InvalidArgument);
48 uint64_t& inputPosition, uint64_t& outputPosition,
size_t count,
52 const size_t capacity = count < BufferCapacity ? count : BufferCapacity;
55 SYSCALL_ERROR(OutOfMemory);
60 size_t transferred = 0;
61 while (transferred < count) {
62 if (thread->getInterruptionReason() == Thread::InterruptedBySignal ||
64 SYSCALL_ERROR(Interrupted);
67 const size_t remaining = count - transferred;
68 const size_t requested = remaining < capacity ? remaining : capacity;
71 input->
read(inputPosition, requested,
reinterpret_cast<uintptr_t
>(buffer.get()),
true);
72 const size_t inputError = thread->
getErrno();
73 assert(read <= requested);
76 if (thread->getInterruptionReason() == Thread::InterruptedBySignal ||
78 SYSCALL_ERROR(Interrupted);
82 size_t writable = read;
86 if (outputPosition >= limit) {
87 SYSCALL_ERROR(FileTooLarge);
90 if (writable > limit - outputPosition)
91 writable =
static_cast<size_t>(limit - outputPosition);
94 const ssize_t written =
95 writer ?
static_cast<ssize_t
>(writer->write(
96 outputPosition, writable,
reinterpret_cast<uintptr_t
>(buffer.get()),
true))
97 : posix_send_descriptor(output, buffer.get(), read, 0, true);
98 const size_t outputError = thread->
getErrno();
99 if (!writer && outputError == Error::BrokenPipe)
103 syscallError(thread->getInterruptionReason() == Thread::InterruptedBySignal ||
110 assert(
static_cast<size_t>(written) <= writable);
112 inputPosition += written;
113 outputPosition += written;
114 transferred += written;
116 if (
static_cast<size_t>(written) < read || read < requested || inputError || outputError)
121 return static_cast<ssize_t
>(transferred);
125 if (thread->getInterruptionReason() == Thread::InterruptedBySignal ||
127 SYSCALL_ERROR(Interrupted);
135 return copy(&writer);
137 return copy(
nullptr);
140ssize_t finishTransfer(
Thread* thread,
PosixSubsystem* subsystem, ssize_t result,
bool pipeSignal) {
141 const size_t error = result < 0 ? thread->
getErrno() : 0;
142 thread->clearInterruption();
150 int64_t* explicitPosition,
size_t count,
bool& pipeSignal) {
153 SYSCALL_ERROR(BadFileDescriptor);
157 if (!inputDescription) {
158 SYSCALL_ERROR(BadFileDescriptor);
162 const int inputAccess = inputFlags & O_ACCMODE;
163 if ((inputFlags & O_PATH) || (inputAccess != O_RDONLY && inputAccess != O_RDWR)) {
164 SYSCALL_ERROR(BadFileDescriptor);
167 File* inputFile = inputDescription->getFile();
168 if (explicitPosition && inputFile && !inputFile->
isSeekable()) {
169 SYSCALL_ERROR(IllegalSeek);
172 if (!regularFile(inputFile))
174 const uint64_t initialInput =
175 explicitPosition ?
static_cast<uint64_t
>(*explicitPosition) : input->
getOffset();
176 if (!signedRange(initialInput, count))
181 SYSCALL_ERROR(BadFileDescriptor);
185 if (output->openingPath() && output->getFile() &&
186 output->getFile()->supportsRegularFileOperations() && !output->getFile()->isBlockDevice() &&
187 !mountWrite.acquire(output->openingPath())) {
190 auto outputDescription = output->acquireOpenFileDescription();
191 if (!outputDescription) {
192 SYSCALL_ERROR(BadFileDescriptor);
195 auto socket = outputDescription->getNetworkImpl();
196 File* outputFile = socket ? nullptr : outputDescription->getFile();
198 if (socket->getType() != SOCK_STREAM ||
199 (socket->getDomain() != AF_UNIX && socket->getDomain() != AF_INET &&
200 socket->getDomain() != AF_INET6)) {
201 SYSCALL_ERROR(InvalidArgument);
204 }
else if (!regularFile(outputFile)) {
208 PositionGuard positions(inputDescription, outputDescription, !explicitPosition, !socket);
209 const int outputFlags = positions.statusFlags(Endpoint::Output);
210 const int outputAccess = outputFlags & O_ACCMODE;
213 if ((outputFlags & O_PATH) || (!socket && outputAccess != O_WRONLY && outputAccess != O_RDWR)) {
214 SYSCALL_ERROR(BadFileDescriptor);
217 if (outputFlags & O_APPEND) {
218 SYSCALL_ERROR(InvalidArgument);
221 uint64_t inputPosition = explicitPosition ?
static_cast<uint64_t
>(*explicitPosition)
222 : positions.offset(Endpoint::Input);
223 uint64_t outputPosition = socket ? 0 : positions.offset(Endpoint::Output);
224 if (!signedRange(inputPosition, count))
226 if (count > MaximumTransfer)
227 count = MaximumTransfer;
231 if (inputPosition > limit || count > limit - inputPosition) {
232 if (inputPosition >= limit) {
233 SYSCALL_ERROR(ValueTooLarge);
236 count =
static_cast<size_t>(limit - inputPosition);
238 if (!signedRange(outputPosition, count))
241 struct sockaddr_storage peer = {};
242 socklen_t length =
sizeof(peer);
244 if (socket->getpeername(&peer, &length) < 0)
247 const ssize_t result = moveBytes(thread, inputFile, output, outputFile, inputPosition,
248 outputPosition, count, pipeSignal);
251 positions.commitOffset(Endpoint::Output, outputPosition);
252 if (explicitPosition)
253 *explicitPosition =
static_cast<int64_t
>(inputPosition);
254 else if (socket || !positions.sameDescription())
255 positions.commitOffset(Endpoint::Input, inputPosition);
261ssize_t posix_sendfile(
int outputFd,
int inputFd, int64_t* offset,
size_t count) {
265 int64_t position = 0;
266 thread->clearInterruption();
269 SYSCALL_ERROR(BadAddress);
272 bool pipeSignal =
false;
273 ssize_t result = sendFile(subsystem, thread, outputFd, inputFd, offset ? &position : nullptr,
277 SYSCALL_ERROR(BadAddress);
280 return finishTransfer(thread, subsystem, result, pipeSignal);
283ssize_t posix_copy_file_range(
int inputFd, int64_t* inputOffset,
int outputFd,
284 int64_t* outputOffset,
size_t count,
unsigned flags) {
288 thread->clearInterruption();
290 bool pipeSignal =
false;
291 const ssize_t result = [&]() -> ssize_t {
295 SYSCALL_ERROR(BadFileDescriptor);
298 int64_t importedInput = 0, importedOutput = 0;
303 SYSCALL_ERROR(BadAddress);
307 SYSCALL_ERROR(InvalidArgument);
312 if (output->openingPath() && output->getFile() &&
313 output->getFile()->supportsRegularFileOperations() && !output->getFile()->isBlockDevice() &&
314 !mountWrite.acquire(output->openingPath())) {
317 auto outputDescription = output->acquireOpenFileDescription();
318 if (!inputDescription || !outputDescription) {
319 SYSCALL_ERROR(BadFileDescriptor);
322 File* inputFile = inputDescription->getFile();
323 File* outputFile = outputDescription->getFile();
324 PositionGuard positions(inputDescription, outputDescription, !inputOffset,
true);
325 const int inputFlags = positions.statusFlags(Endpoint::Input);
326 const int outputFlags = positions.statusFlags(Endpoint::Output);
327 if ((inputFlags & O_PATH) || (outputFlags & O_PATH)) {
328 SYSCALL_ERROR(BadFileDescriptor);
332 SYSCALL_ERROR(IsADirectory);
335 if (!regularFile(inputFile) || !regularFile(outputFile))
337 const int inputAccess = inputFlags & O_ACCMODE;
338 const int outputAccess = outputFlags & O_ACCMODE;
339 if ((inputAccess != O_RDONLY && inputAccess != O_RDWR) ||
340 (outputAccess != O_WRONLY && outputAccess != O_RDWR) || (outputFlags & O_APPEND)) {
341 SYSCALL_ERROR(BadFileDescriptor);
344 if (inputFile->getFilesystem() != outputFile->getFilesystem()) {
345 SYSCALL_ERROR(CrossDeviceLink);
348 uint64_t inputPosition =
349 inputOffset ?
static_cast<uint64_t
>(importedInput) : positions.offset(Endpoint::Input);
350 uint64_t outputPosition =
351 outputOffset ?
static_cast<uint64_t
>(importedOutput) : positions.offset(Endpoint::Output);
352 if (count > ~uint64_t(0) - inputPosition || count > ~uint64_t(0) - outputPosition) {
353 SYSCALL_ERROR(ValueTooLarge);
356 const uint64_t inputSize = inputFile->getSize();
357 if (inputPosition <= MaximumPosition && inputPosition >= inputSize)
359 else if (count > inputSize - inputPosition)
360 count =
static_cast<size_t>(inputSize - inputPosition);
362 if (outputPosition <= MaximumPosition) {
363 if (outputPosition >= outputLimit) {
364 SYSCALL_ERROR(FileTooLarge);
367 if (count > outputLimit - outputPosition)
368 count =
static_cast<size_t>(outputLimit - outputPosition);
371 outputPosition + count > inputPosition && outputPosition < inputPosition + count) {
372 SYSCALL_ERROR(InvalidArgument);
375 if (!signedRange(inputPosition, count) || !signedRange(outputPosition, count))
377 if (count > MaximumTransfer)
378 count = MaximumTransfer;
379 ssize_t moved = moveBytes(thread, inputFile, output, outputFile, inputPosition, outputPosition,
387 importedInput =
static_cast<int64_t
>(inputPosition);
390 positions.commitOffset(Endpoint::Input, inputPosition);
393 importedOutput =
static_cast<int64_t
>(outputPosition);
397 positions.commitOffset(Endpoint::Output, outputPosition);
400 SYSCALL_ERROR(BadAddress);
405 return finishTransfer(thread, subsystem, result, pipeSignal);
OpenFileDescriptionLease acquireOpenFileDescription() const
int getStatusFlags() const
Get current status flags.
uint64_t getOffset() const
virtual bool isSeekable() const
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
bool supportsRegularFileOperations()
virtual bool isDirectory()
virtual uintptr_t futexIdentity()
virtual uint64_t maximumFileSize() const
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)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
void setErrno(size_t err)
@ Continue
No unwind necessary, carry on as normal.
UnwindType getUnwindState()
Process * getParent() const