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/MountView.h"
16#include "modules/system/vfs/Pipe.h"
17#include "net-syscalls.h"
18#include "pipe-transfer-syscalls.h"
21constexpr unsigned KnownFlags = 0xf;
22constexpr unsigned Nonblock = 2;
23constexpr uint64_t MaximumPosition = 0x7fffffffffffffffULL;
24using Status = PipeBuffer::Status;
26using PositionSide = PositionGuard::Endpoint;
37bool acquireEndpoint(
PosixSubsystem* subsystem,
int fd, Endpoint& endpoint) {
39 SYSCALL_ERROR(BadFileDescriptor);
42 endpoint.description = endpoint.descriptor->acquireOpenFileDescription();
43 if (!endpoint.description) {
44 SYSCALL_ERROR(BadFileDescriptor);
47 endpoint.flags = endpoint.descriptor->getStatusFlags();
48 if (endpoint.flags & O_PATH) {
49 SYSCALL_ERROR(BadFileDescriptor);
52 endpoint.file = endpoint.description->getFile();
53 endpoint.socket = endpoint.description->getNetworkImpl();
54 if (endpoint.file && (endpoint.file->isPipe() || endpoint.file->isFifo()))
59bool accessAllowed(
const Endpoint& endpoint,
bool writing) {
60 const int mode = endpoint.flags & O_ACCMODE;
61 if (endpoint.socket || mode == O_RDWR || mode == (writing ? O_WRONLY : O_RDONLY))
63 SYSCALL_ERROR(BadFileDescriptor);
67bool validRange(uint64_t offset,
size_t count) {
68 if (offset > MaximumPosition || count > MaximumPosition - offset) {
69 SYSCALL_ERROR(InvalidArgument);
75bool interrupted(
Thread* thread) {
76 if (thread->getInterruptionReason() == Thread::InterruptedBySignal ||
78 SYSCALL_ERROR(Interrupted);
86 return static_cast<ssize_t
>(result.count);
87 switch (result.status) {
95 SYSCALL_ERROR(BrokenPipe);
97 case Status::WouldBlock:
98 SYSCALL_ERROR(NoMoreProcesses);
100 case Status::Interrupted:
101 SYSCALL_ERROR(Interrupted);
103 case Status::Invalid:
104 SYSCALL_ERROR(InvalidArgument);
111 const size_t error = result < 0 ? thread->
getErrno() : 0;
112 thread->clearInterruption();
119ssize_t mixedSplice(
Thread* thread, Endpoint& input, Endpoint& output, int64_t* explicitPosition,
120 size_t count,
unsigned flags,
bool& pipeSignal) {
121 const bool writingPipe = output.pipe !=
nullptr;
122 Pipe* pipe = writingPipe ? output.pipe : input.pipe;
123 Endpoint& regular = writingPipe ? input : output;
124 const bool socket = !writingPipe && bool(output.socket);
126 if (explicitPosition || output.socket->getType() != SOCK_STREAM ||
127 (output.socket->getDomain() != AF_UNIX && output.socket->getDomain() != AF_INET &&
128 output.socket->getDomain() != AF_INET6)) {
129 SYSCALL_ERROR(InvalidArgument);
132 }
else if (!regular.file || !regular.file->supportsRegularFileOperations()) {
133 SYSCALL_ERROR(InvalidArgument);
136 if (!writingPipe && (output.flags & O_APPEND)) {
137 SYSCALL_ERROR(InvalidArgument);
140 const uint64_t initial = explicitPosition ?
static_cast<uint64_t
>(*explicitPosition)
142 : regular.descriptor->getOffset();
143 if (!validRange(initial, count))
146 struct sockaddr_storage peer = {};
147 socklen_t length =
sizeof(peer);
149 if (output.socket->getpeername(&peer, &length) < 0)
152 const size_t maximum = count < PipeBuffer::Capacity ? count : PipeBuffer::Capacity;
155 SYSCALL_ERROR(OutOfMemory);
158 const bool canBlock =
159 !(flags & Nonblock) && !((writingPipe ? output.flags : input.flags) & O_NONBLOCK);
162 if (interrupted(thread))
165 const auto ready = pipe->waitTransfer(writingPipe, canBlock);
166 if (ready.status != Status::Ready)
167 return pipeResult(ready, writingPipe, pipeSignal);
169 const ssize_t result = [&]() -> ssize_t {
170 PositionGuard positions(input.description, output.description,
171 writingPipe && !explicitPosition, !writingPipe && !socket);
172 if (!writingPipe && (positions.statusFlags(PositionSide::Output) & O_APPEND)) {
173 SYSCALL_ERROR(InvalidArgument);
177 explicitPosition ?
static_cast<uint64_t
>(*explicitPosition)
179 : positions.offset(writingPipe ? PositionSide::Input : PositionSide::Output);
180 if (!validRange(position, count) || interrupted(thread))
183 if (output.file && !output.pipe && output.descriptor->openingPath() &&
184 !mountWrite.acquire(output.descriptor->openingPath())) {
190 const auto reserved = pipe->reserveWrite(maximum,
false, reservation);
191 if (reserved.status == Status::WouldBlock) {
195 if (reserved.status != Status::Ready)
196 return pipeResult(reserved,
true, pipeSignal);
198 const size_t read = regular.file->read(position, reservation.size(),
199 reinterpret_cast<uintptr_t
>(scratch.get()),
true);
200 assert(read <= reservation.size());
202 return thread->
getErrno() || interrupted(thread) ? -1 : 0;
203 if (interrupted(thread))
205 moved = pipeResult(reservation.commit(scratch.get(), read),
true, pipeSignal);
209 const auto reserved = pipe->reserveRead(maximum,
false, reservation);
210 if (reserved.status == Status::WouldBlock) {
214 if (reserved.status != Status::Ready)
215 return pipeResult(reserved,
false, pipeSignal);
216 size_t amount = reservation.size();
218 const uint64_t limit = regular.file->maximumFileSize();
219 if (position >= limit) {
220 SYSCALL_ERROR(FileTooLarge);
223 if (amount > limit - position)
224 amount =
static_cast<size_t>(limit - position);
226 reservation.copyTo(scratch.get(), amount);
227 if (interrupted(thread))
230 const ssize_t written =
231 writer ?
static_cast<ssize_t
>(writer->write(
232 position, amount,
reinterpret_cast<uintptr_t
>(scratch.get()),
true))
233 : posix_send_descriptor(output.descriptor, scratch.get(), amount, 0, true);
234 if (!writer && thread->
getErrno() == Error::BrokenPipe)
237 if (!thread->
getErrno() && !interrupted(thread))
238 SYSCALL_ERROR(IoError);
241 assert(
static_cast<size_t>(written) <= amount);
242 reservation.consume(
static_cast<size_t>(written));
246 moved = fromPipe(
nullptr);
248 auto writer = regular.file->lockWrites();
249 moved = fromPipe(&writer);
253 position +=
static_cast<size_t>(moved);
254 if (explicitPosition)
255 *explicitPosition =
static_cast<int64_t
>(position);
257 positions.commitOffset(writingPipe ? PositionSide::Input : PositionSide::Output,
269ssize_t posix_splice(
int inputFd, int64_t* inputOffset,
int outputFd, int64_t* outputOffset,
270 size_t count,
unsigned flags) {
274 thread->clearInterruption();
278 if (flags & ~KnownFlags) {
279 SYSCALL_ERROR(InvalidArgument);
282 bool pipeSignal =
false;
283 const ssize_t result = [&]() -> ssize_t {
284 Endpoint input, output;
285 if (!acquireEndpoint(subsystem, inputFd, input) ||
286 !acquireEndpoint(subsystem, outputFd, output))
288 if ((input.pipe && inputOffset) || (output.pipe && outputOffset)) {
289 SYSCALL_ERROR(IllegalSeek);
292 int64_t inputPosition = 0, outputPosition = 0;
297 SYSCALL_ERROR(BadAddress);
300 if (!accessAllowed(input,
false) || !accessAllowed(output,
true))
303 if (output.file && !output.pipe && output.descriptor->openingPath() &&
304 !mountWrite.acquire(output.descriptor->openingPath())) {
308 if (input.pipe && output.pipe) {
309 const bool canBlock = !(flags & Nonblock) && !((input.flags | output.flags) & O_NONBLOCK);
311 pipeResult(input.pipe->transferTo(*output.pipe, count,
true, canBlock),
true, pipeSignal);
312 }
else if (input.pipe || output.pipe) {
313 int64_t* position = input.pipe ? (outputOffset ? &outputPosition :
nullptr)
314 : (inputOffset ? &inputPosition : nullptr);
315 moved = mixedSplice(thread, input, output, position, count, flags, pipeSignal);
317 SYSCALL_ERROR(InvalidArgument);
327 SYSCALL_ERROR(BadAddress);
332 return finish(thread, subsystem, result, pipeSignal);
335ssize_t posix_tee(
int inputFd,
int outputFd,
size_t count,
unsigned flags) {
339 thread->clearInterruption();
341 if (flags & ~KnownFlags) {
342 SYSCALL_ERROR(InvalidArgument);
347 bool pipeSignal =
false;
348 const ssize_t result = [&]() -> ssize_t {
349 Endpoint input, output;
350 if (!acquireEndpoint(subsystem, inputFd, input) ||
351 !acquireEndpoint(subsystem, outputFd, output) || !accessAllowed(input,
false) ||
352 !accessAllowed(output,
true))
354 if (!input.pipe || !output.pipe) {
355 SYSCALL_ERROR(InvalidArgument);
358 const bool canBlock = !(flags & Nonblock) && !((input.flags | output.flags) & O_NONBLOCK);
359 return pipeResult(input.pipe->transferTo(*output.pipe, count,
false, canBlock),
true,
362 return finish(thread, subsystem, result, pipeSignal);
static Pipe * fromFile(File *pF)
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