Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/NumericLimits.h>
8#include <Kernel/Debug.h>
9#include <Kernel/FileSystem/OpenFileDescription.h>
10#include <Kernel/Process.h>
11
12namespace Kernel {
13
14// NOTE: The offset is passed by pointer because off_t is 64bit,
15// hence it can't be passed by register on 32bit platforms.
16ErrorOr<FlatPtr> Process::sys$pwritev(int fd, Userspace<const struct iovec*> iov, int iov_count, Userspace<off_t const*> userspace_offset)
17{
18 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
19 TRY(require_promise(Pledge::stdio));
20 if (iov_count < 0)
21 return EINVAL;
22
23 if (iov_count > IOV_MAX)
24 return EFAULT;
25
26 u64 total_length = 0;
27 Vector<iovec, 32> vecs;
28 TRY(vecs.try_resize(iov_count));
29 TRY(copy_n_from_user(vecs.data(), iov, iov_count));
30 for (auto& vec : vecs) {
31 total_length += vec.iov_len;
32 if (total_length > NumericLimits<i32>::max())
33 return EINVAL;
34 }
35
36 // NOTE: Negative offset means "operate like writev" which seeks the file.
37 auto base_offset = TRY(copy_typed_from_user(userspace_offset));
38 auto description = TRY(open_file_description(fd));
39 if (!description->is_writable())
40 return EBADF;
41 if (base_offset >= 0 && !description->file().is_seekable())
42 return EINVAL;
43
44 int nwritten = 0;
45 off_t current_offset = base_offset;
46 for (auto& vec : vecs) {
47 auto buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len));
48 auto result = do_write(*description, buffer, vec.iov_len, base_offset >= 0 ? current_offset : Optional<off_t> {});
49 if (result.is_error()) {
50 if (nwritten == 0)
51 return result.release_error();
52 return nwritten;
53 }
54 nwritten += result.value();
55 current_offset += result.value();
56 }
57
58 return nwritten;
59}
60
61ErrorOr<FlatPtr> Process::do_write(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size, Optional<off_t> offset)
62{
63 size_t total_nwritten = 0;
64
65 if (description.should_append() && description.file().is_seekable()) {
66 TRY(description.seek(0, SEEK_END));
67 }
68
69 while (total_nwritten < data_size) {
70 while (!description.can_write()) {
71 if (!description.is_blocking()) {
72 if (total_nwritten > 0)
73 return total_nwritten;
74 return EAGAIN;
75 }
76 auto unblock_flags = Thread::FileBlocker::BlockFlags::None;
77 if (Thread::current()->block<Thread::WriteBlocker>({}, description, unblock_flags).was_interrupted()) {
78 if (total_nwritten == 0)
79 return EINTR;
80 }
81 // TODO: handle exceptions in unblock_flags
82 }
83 auto nwritten_or_error = offset.has_value()
84 ? description.write(offset.value() + total_nwritten, data.offset(total_nwritten), data_size - total_nwritten)
85 : description.write(data.offset(total_nwritten), data_size - total_nwritten);
86 if (nwritten_or_error.is_error()) {
87 if (total_nwritten > 0)
88 return total_nwritten;
89 if (nwritten_or_error.error().code() == EAGAIN)
90 continue;
91 if (nwritten_or_error.error().code() == EPIPE)
92 Thread::current()->send_signal(SIGPIPE, &Process::current());
93 return nwritten_or_error.release_error();
94 }
95 VERIFY(nwritten_or_error.value() > 0);
96 total_nwritten += nwritten_or_error.value();
97 }
98 return total_nwritten;
99}
100
101ErrorOr<FlatPtr> Process::sys$write(int fd, Userspace<u8 const*> data, size_t size)
102{
103 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
104 TRY(require_promise(Pledge::stdio));
105 if (size == 0)
106 return 0;
107 if (size > NumericLimits<ssize_t>::max())
108 return EINVAL;
109
110 dbgln_if(IO_DEBUG, "sys$write({}, {}, {})", fd, data.ptr(), size);
111 auto description = TRY(open_file_description(fd));
112 if (!description->is_writable())
113 return EBADF;
114
115 auto buffer = TRY(UserOrKernelBuffer::for_user_buffer(data, static_cast<size_t>(size)));
116 return do_write(*description, buffer, size);
117}
118
119}