write.cpp 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/NumericLimits.h>
  7. #include <Kernel/Debug.h>
  8. #include <Kernel/FileSystem/FileDescription.h>
  9. #include <Kernel/Process.h>
  10. namespace Kernel {
  11. KResultOr<FlatPtr> Process::sys$writev(int fd, Userspace<const struct iovec*> iov, int iov_count)
  12. {
  13. VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
  14. REQUIRE_PROMISE(stdio);
  15. if (iov_count < 0)
  16. return EINVAL;
  17. // Arbitrary pain threshold.
  18. if (iov_count > (int)MiB)
  19. return EFAULT;
  20. u64 total_length = 0;
  21. Vector<iovec, 32> vecs;
  22. if (!vecs.try_resize(iov_count))
  23. return ENOMEM;
  24. if (!copy_n_from_user(vecs.data(), iov, iov_count))
  25. return EFAULT;
  26. for (auto& vec : vecs) {
  27. total_length += vec.iov_len;
  28. if (total_length > NumericLimits<i32>::max())
  29. return EINVAL;
  30. }
  31. auto description = fds().file_description(fd);
  32. if (!description)
  33. return EBADF;
  34. if (!description->is_writable())
  35. return EBADF;
  36. int nwritten = 0;
  37. for (auto& vec : vecs) {
  38. auto buffer = UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len);
  39. if (!buffer.has_value())
  40. return EFAULT;
  41. auto result = do_write(*description, buffer.value(), vec.iov_len);
  42. if (result.is_error()) {
  43. if (nwritten == 0)
  44. return result.error();
  45. return nwritten;
  46. }
  47. nwritten += result.value();
  48. }
  49. return nwritten;
  50. }
  51. KResultOr<FlatPtr> Process::do_write(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size)
  52. {
  53. size_t total_nwritten = 0;
  54. if (description.should_append() && description.file().is_seekable()) {
  55. auto seek_result = description.seek(0, SEEK_END);
  56. if (seek_result.is_error())
  57. return seek_result.error();
  58. }
  59. while (total_nwritten < data_size) {
  60. while (!description.can_write()) {
  61. if (!description.is_blocking()) {
  62. if (total_nwritten > 0)
  63. return total_nwritten;
  64. else
  65. return EAGAIN;
  66. }
  67. auto unblock_flags = Thread::FileBlocker::BlockFlags::None;
  68. if (Thread::current()->block<Thread::WriteBlocker>({}, description, unblock_flags).was_interrupted()) {
  69. if (total_nwritten == 0)
  70. return EINTR;
  71. }
  72. // TODO: handle exceptions in unblock_flags
  73. }
  74. auto nwritten_or_error = description.write(data.offset(total_nwritten), data_size - total_nwritten);
  75. if (nwritten_or_error.is_error()) {
  76. if (total_nwritten > 0)
  77. return total_nwritten;
  78. if (nwritten_or_error.error() == EAGAIN)
  79. continue;
  80. return nwritten_or_error.error();
  81. }
  82. VERIFY(nwritten_or_error.value() > 0);
  83. total_nwritten += nwritten_or_error.value();
  84. }
  85. return total_nwritten;
  86. }
  87. KResultOr<FlatPtr> Process::sys$write(int fd, Userspace<const u8*> data, size_t size)
  88. {
  89. VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
  90. REQUIRE_PROMISE(stdio);
  91. if (size == 0)
  92. return 0;
  93. if (size > NumericLimits<ssize_t>::max())
  94. return EINVAL;
  95. dbgln_if(IO_DEBUG, "sys$write({}, {}, {})", fd, data.ptr(), size);
  96. auto description = fds().file_description(fd);
  97. if (!description)
  98. return EBADF;
  99. if (!description->is_writable())
  100. return EBADF;
  101. auto buffer = UserOrKernelBuffer::for_user_buffer(data, static_cast<size_t>(size));
  102. if (!buffer.has_value())
  103. return EFAULT;
  104. return do_write(*description, buffer.value(), size);
  105. }
  106. }