Serenity Operating System
at master 126 lines 4.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <Kernel/Debug.h> 8#include <Kernel/FileSystem/OpenFileDescription.h> 9#include <Kernel/PerformanceManager.h> 10#include <Kernel/Process.h> 11 12namespace Kernel { 13 14using BlockFlags = Thread::FileBlocker::BlockFlags; 15 16static ErrorOr<NonnullRefPtr<OpenFileDescription>> open_readable_file_description(auto& fds, int fd) 17{ 18 auto description = TRY(fds.with_shared([&](auto& fds) { return fds.open_file_description(fd); })); 19 if (!description->is_readable()) 20 return EBADF; 21 if (description->is_directory()) 22 return EISDIR; 23 return description; 24} 25 26static ErrorOr<void> check_blocked_read(OpenFileDescription* description) 27{ 28 if (description->is_blocking()) { 29 if (!description->can_read()) { 30 auto unblock_flags = BlockFlags::None; 31 if (Thread::current()->block<Thread::ReadBlocker>({}, *description, unblock_flags).was_interrupted()) 32 return EINTR; 33 if (!has_flag(unblock_flags, BlockFlags::Read)) 34 return EAGAIN; 35 // TODO: handle exceptions in unblock_flags 36 } 37 } 38 return {}; 39} 40 41ErrorOr<FlatPtr> Process::sys$readv(int fd, Userspace<const struct iovec*> iov, int iov_count) 42{ 43 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); 44 TRY(require_promise(Pledge::stdio)); 45 if (iov_count < 0) 46 return EINVAL; 47 48 if (iov_count > IOV_MAX) 49 return EFAULT; 50 51 u64 total_length = 0; 52 Vector<iovec, 32> vecs; 53 TRY(vecs.try_resize(iov_count)); 54 TRY(copy_n_from_user(vecs.data(), iov, iov_count)); 55 for (auto& vec : vecs) { 56 total_length += vec.iov_len; 57 if (total_length > NumericLimits<i32>::max()) 58 return EINVAL; 59 } 60 61 auto description = TRY(open_readable_file_description(fds(), fd)); 62 63 int nread = 0; 64 for (auto& vec : vecs) { 65 TRY(check_blocked_read(description)); 66 auto buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len)); 67 auto nread_here = TRY(description->read(buffer, vec.iov_len)); 68 nread += nread_here; 69 } 70 71 return nread; 72} 73 74ErrorOr<FlatPtr> Process::sys$read(int fd, Userspace<u8*> buffer, size_t size) 75{ 76 auto const start_timestamp = TimeManagement::the().uptime_ms(); 77 auto result = read_impl(fd, buffer, size); 78 79 if (Thread::current()->is_profiling_suppressed()) 80 return result; 81 82 auto description = TRY(open_readable_file_description(fds(), fd)); 83 PerformanceManager::add_read_event(*Thread::current(), fd, size, description, start_timestamp, result); 84 85 return result; 86} 87 88ErrorOr<FlatPtr> Process::read_impl(int fd, Userspace<u8*> buffer, size_t size) 89{ 90 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); 91 TRY(require_promise(Pledge::stdio)); 92 if (size == 0) 93 return 0; 94 if (size > NumericLimits<ssize_t>::max()) 95 return EINVAL; 96 dbgln_if(IO_DEBUG, "sys$read({}, {}, {})", fd, buffer.ptr(), size); 97 auto description = TRY(open_readable_file_description(fds(), fd)); 98 99 TRY(check_blocked_read(description)); 100 auto user_buffer = TRY(UserOrKernelBuffer::for_user_buffer(buffer, size)); 101 return TRY(description->read(user_buffer, size)); 102} 103 104// NOTE: The offset is passed by pointer because off_t is 64bit, 105// hence it can't be passed by register on 32bit platforms. 106ErrorOr<FlatPtr> Process::sys$pread(int fd, Userspace<u8*> buffer, size_t size, Userspace<off_t const*> userspace_offset) 107{ 108 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); 109 TRY(require_promise(Pledge::stdio)); 110 if (size == 0) 111 return 0; 112 if (size > NumericLimits<ssize_t>::max()) 113 return EINVAL; 114 auto offset = TRY(copy_typed_from_user(userspace_offset)); 115 if (offset < 0) 116 return EINVAL; 117 dbgln_if(IO_DEBUG, "sys$pread({}, {}, {}, {})", fd, buffer.ptr(), size, offset); 118 auto description = TRY(open_readable_file_description(fds(), fd)); 119 if (!description->file().is_seekable()) 120 return EINVAL; 121 TRY(check_blocked_read(description)); 122 auto user_buffer = TRY(UserOrKernelBuffer::for_user_buffer(buffer, size)); 123 return TRY(description->read(user_buffer, offset, size)); 124} 125 126}