Kernel+LibC+Tests: Implement pwritev(2)

While this isn't really POSIX, it's needed by the Zig port and was
simple enough to implement.
This commit is contained in:
sin-ack 2022-10-01 13:21:20 +00:00 committed by Andrew Kaster
parent 70337f3a4b
commit 9b425b860c
Notes: sideshowbarker 2024-07-17 03:23:36 +09:00
6 changed files with 28 additions and 12 deletions

View file

@ -195,7 +195,7 @@ enum class NeedsBigProcessLock {
S(utimensat, NeedsBigProcessLock::No) \
S(waitid, NeedsBigProcessLock::Yes) \
S(write, NeedsBigProcessLock::Yes) \
S(writev, NeedsBigProcessLock::Yes) \
S(pwritev, NeedsBigProcessLock::Yes) \
S(yield, NeedsBigProcessLock::No)
namespace Syscall {

View file

@ -317,7 +317,7 @@ public:
ErrorOr<FlatPtr> sys$pread(int fd, Userspace<u8*>, size_t, Userspace<off_t const*>);
ErrorOr<FlatPtr> sys$readv(int fd, Userspace<const struct iovec*> iov, int iov_count);
ErrorOr<FlatPtr> sys$write(int fd, Userspace<u8 const*>, size_t);
ErrorOr<FlatPtr> sys$writev(int fd, Userspace<const struct iovec*> iov, int iov_count);
ErrorOr<FlatPtr> sys$pwritev(int fd, Userspace<const struct iovec*> iov, int iov_count, Userspace<off_t const*>);
ErrorOr<FlatPtr> sys$fstat(int fd, Userspace<stat*>);
ErrorOr<FlatPtr> sys$stat(Userspace<Syscall::SC_stat_params const*>);
ErrorOr<FlatPtr> sys$lseek(int fd, Userspace<off_t*>, int whence);
@ -602,7 +602,7 @@ private:
void delete_perf_events_buffer();
ErrorOr<void> do_exec(NonnullLockRefPtr<OpenFileDescription> main_program_description, NonnullOwnPtrVector<KString> arguments, NonnullOwnPtrVector<KString> environment, LockRefPtr<OpenFileDescription> interpreter_description, Thread*& new_main_thread, u32& prev_flags, const ElfW(Ehdr) & main_program_header);
ErrorOr<FlatPtr> do_write(OpenFileDescription&, UserOrKernelBuffer const&, size_t);
ErrorOr<FlatPtr> do_write(OpenFileDescription&, UserOrKernelBuffer const&, size_t, Optional<off_t> = {});
ErrorOr<FlatPtr> do_statvfs(FileSystem const& path, Custody const*, statvfs* buf);

View file

@ -11,7 +11,9 @@
namespace Kernel {
ErrorOr<FlatPtr> Process::sys$writev(int fd, Userspace<const struct iovec*> iov, int iov_count)
// NOTE: The offset is passed by pointer because off_t is 64bit,
// hence it can't be passed by register on 32bit platforms.
ErrorOr<FlatPtr> Process::sys$pwritev(int fd, Userspace<const struct iovec*> iov, int iov_count, Userspace<off_t const*> userspace_offset)
{
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
TRY(require_promise(Pledge::stdio));
@ -31,26 +33,32 @@ ErrorOr<FlatPtr> Process::sys$writev(int fd, Userspace<const struct iovec*> iov,
return EINVAL;
}
// NOTE: Negative offset means "operate like writev" which seeks the file.
auto base_offset = TRY(copy_typed_from_user(userspace_offset));
auto description = TRY(open_file_description(fd));
if (!description->is_writable())
return EBADF;
if (base_offset >= 0 && !description->file().is_seekable())
return EINVAL;
int nwritten = 0;
off_t current_offset = base_offset;
for (auto& vec : vecs) {
auto buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len));
auto result = do_write(*description, buffer, vec.iov_len);
auto result = do_write(*description, buffer, vec.iov_len, base_offset >= 0 ? current_offset : Optional<off_t> {});
if (result.is_error()) {
if (nwritten == 0)
return result.release_error();
return nwritten;
}
nwritten += result.value();
current_offset += result.value();
}
return nwritten;
}
ErrorOr<FlatPtr> Process::do_write(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size)
ErrorOr<FlatPtr> Process::do_write(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size, Optional<off_t> offset)
{
size_t total_nwritten = 0;
@ -72,7 +80,9 @@ ErrorOr<FlatPtr> Process::do_write(OpenFileDescription& description, UserOrKerne
}
// TODO: handle exceptions in unblock_flags
}
auto nwritten_or_error = description.write(data.offset(total_nwritten), data_size - total_nwritten);
auto nwritten_or_error = offset.has_value()
? description.write(offset.value() + total_nwritten, data.offset(total_nwritten), data_size - total_nwritten)
: description.write(data.offset(total_nwritten), data_size - total_nwritten);
if (nwritten_or_error.is_error()) {
if (total_nwritten > 0)
return total_nwritten;

View file

@ -44,7 +44,7 @@ static bool is_bad_idea(int fn, size_t const* direct_sc_args, size_t const* fake
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
return direct_sc_args[0] == 1;
case SC_write:
case SC_writev:
case SC_pwritev:
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
return direct_sc_args[0] == 0;
case SC_pledge:

View file

@ -13,10 +13,7 @@ extern "C" {
ssize_t writev(int fd, const struct iovec* iov, int iov_count)
{
__pthread_maybe_cancel();
int rc = syscall(SC_writev, fd, iov, iov_count);
__RETURN_WITH_ERRNO(rc, rc, -1);
return pwritev(fd, iov, iov_count, -1);
}
ssize_t readv(int fd, const struct iovec* iov, int iov_count)
@ -26,4 +23,12 @@ ssize_t readv(int fd, const struct iovec* iov, int iov_count)
int rc = syscall(SC_readv, fd, iov, iov_count);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
ssize_t pwritev(int fd, struct iovec const* iov, int iov_count, off_t offset)
{
__pthread_maybe_cancel();
int rc = syscall(SC_pwritev, fd, iov, iov_count, &offset);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
}

View file

@ -13,5 +13,6 @@ __BEGIN_DECLS
ssize_t writev(int fd, const struct iovec*, int iov_count);
ssize_t readv(int fd, const struct iovec*, int iov_count);
ssize_t pwritev(int fd, const struct iovec*, int iov_count, off_t);
__END_DECLS