15ffd83dbSDimitry Andric //===-- PipePosix.cpp -----------------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "lldb/Host/posix/PipePosix.h" 1006c3fb27SDimitry Andric #include "lldb/Host/FileSystem.h" 110b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h" 120b57cec5SDimitry Andric #include "lldb/Utility/SelectHelper.h" 130b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 140b57cec5SDimitry Andric #include "llvm/Support/Errno.h" 150b57cec5SDimitry Andric #include <functional> 160b57cec5SDimitry Andric #include <thread> 170b57cec5SDimitry Andric 18fe6060f1SDimitry Andric #include <cerrno> 19fe6060f1SDimitry Andric #include <climits> 200b57cec5SDimitry Andric #include <fcntl.h> 210b57cec5SDimitry Andric #include <sys/stat.h> 220b57cec5SDimitry Andric #include <sys/types.h> 230b57cec5SDimitry Andric #include <unistd.h> 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric using namespace lldb; 260b57cec5SDimitry Andric using namespace lldb_private; 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric int PipePosix::kInvalidDescriptor = -1; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE 310b57cec5SDimitry Andric 320b57cec5SDimitry Andric // pipe2 is supported by a limited set of platforms 330b57cec5SDimitry Andric // TODO: Add more platforms that support pipe2. 345f757f3fSDimitry Andric #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ 355f757f3fSDimitry Andric defined(__OpenBSD__) 360b57cec5SDimitry Andric #define PIPE2_SUPPORTED 1 370b57cec5SDimitry Andric #else 380b57cec5SDimitry Andric #define PIPE2_SUPPORTED 0 390b57cec5SDimitry Andric #endif 400b57cec5SDimitry Andric 41349cc55cSDimitry Andric static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED 44349cc55cSDimitry Andric static bool SetCloexecFlag(int fd) { 450b57cec5SDimitry Andric int flags = ::fcntl(fd, F_GETFD); 460b57cec5SDimitry Andric if (flags == -1) 470b57cec5SDimitry Andric return false; 480b57cec5SDimitry Andric return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric #endif 510b57cec5SDimitry Andric 52349cc55cSDimitry Andric static std::chrono::time_point<std::chrono::steady_clock> Now() { 530b57cec5SDimitry Andric return std::chrono::steady_clock::now(); 540b57cec5SDimitry Andric } 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric PipePosix::PipePosix() 570b57cec5SDimitry Andric : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {} 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write) 600b57cec5SDimitry Andric : m_fds{read, write} {} 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric PipePosix::PipePosix(PipePosix &&pipe_posix) 630b57cec5SDimitry Andric : PipeBase{std::move(pipe_posix)}, 640b57cec5SDimitry Andric m_fds{pipe_posix.ReleaseReadFileDescriptor(), 650b57cec5SDimitry Andric pipe_posix.ReleaseWriteFileDescriptor()} {} 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) { 685f757f3fSDimitry Andric std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard( 695f757f3fSDimitry Andric m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex, 705f757f3fSDimitry Andric pipe_posix.m_write_mutex); 715f757f3fSDimitry Andric 720b57cec5SDimitry Andric PipeBase::operator=(std::move(pipe_posix)); 735f757f3fSDimitry Andric m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked(); 745f757f3fSDimitry Andric m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked(); 750b57cec5SDimitry Andric return *this; 760b57cec5SDimitry Andric } 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric PipePosix::~PipePosix() { Close(); } 790b57cec5SDimitry Andric 800b57cec5SDimitry Andric Status PipePosix::CreateNew(bool child_processes_inherit) { 815f757f3fSDimitry Andric std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 825f757f3fSDimitry Andric if (CanReadUnlocked() || CanWriteUnlocked()) 830b57cec5SDimitry Andric return Status(EINVAL, eErrorTypePOSIX); 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric Status error; 860b57cec5SDimitry Andric #if PIPE2_SUPPORTED 870b57cec5SDimitry Andric if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) 880b57cec5SDimitry Andric return error; 890b57cec5SDimitry Andric #else 900b57cec5SDimitry Andric if (::pipe(m_fds) == 0) { 910b57cec5SDimitry Andric #ifdef FD_CLOEXEC 920b57cec5SDimitry Andric if (!child_processes_inherit) { 930b57cec5SDimitry Andric if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) { 940b57cec5SDimitry Andric error.SetErrorToErrno(); 955f757f3fSDimitry Andric CloseUnlocked(); 960b57cec5SDimitry Andric return error; 970b57cec5SDimitry Andric } 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric #endif 1000b57cec5SDimitry Andric return error; 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric #endif 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric error.SetErrorToErrno(); 1050b57cec5SDimitry Andric m_fds[READ] = PipePosix::kInvalidDescriptor; 1060b57cec5SDimitry Andric m_fds[WRITE] = PipePosix::kInvalidDescriptor; 1070b57cec5SDimitry Andric return error; 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) { 111*0fca6ea1SDimitry Andric std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 1125f757f3fSDimitry Andric if (CanReadUnlocked() || CanWriteUnlocked()) 1130b57cec5SDimitry Andric return Status("Pipe is already opened"); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric Status error; 116e8d8bef9SDimitry Andric if (::mkfifo(name.str().c_str(), 0660) != 0) 1170b57cec5SDimitry Andric error.SetErrorToErrno(); 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric return error; 1200b57cec5SDimitry Andric } 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix, 1230b57cec5SDimitry Andric bool child_process_inherit, 1240b57cec5SDimitry Andric llvm::SmallVectorImpl<char> &name) { 1250b57cec5SDimitry Andric llvm::SmallString<128> named_pipe_path; 1260b57cec5SDimitry Andric llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str()); 1270b57cec5SDimitry Andric FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 1280b57cec5SDimitry Andric if (!tmpdir_file_spec) 1290b57cec5SDimitry Andric tmpdir_file_spec.AppendPathComponent("/tmp"); 1300b57cec5SDimitry Andric tmpdir_file_spec.AppendPathComponent(pipe_spec); 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric // It's possible that another process creates the target path after we've 1330b57cec5SDimitry Andric // verified it's available but before we create it, in which case we should 1340b57cec5SDimitry Andric // try again. 1350b57cec5SDimitry Andric Status error; 1360b57cec5SDimitry Andric do { 137e8d8bef9SDimitry Andric llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path, 138e8d8bef9SDimitry Andric /*MakeAbsolute=*/false); 1390b57cec5SDimitry Andric error = CreateNew(named_pipe_path, child_process_inherit); 1400b57cec5SDimitry Andric } while (error.GetError() == EEXIST); 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric if (error.Success()) 1430b57cec5SDimitry Andric name = named_pipe_path; 1440b57cec5SDimitry Andric return error; 1450b57cec5SDimitry Andric } 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric Status PipePosix::OpenAsReader(llvm::StringRef name, 1480b57cec5SDimitry Andric bool child_process_inherit) { 149*0fca6ea1SDimitry Andric std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 1505f757f3fSDimitry Andric 1515f757f3fSDimitry Andric if (CanReadUnlocked() || CanWriteUnlocked()) 1520b57cec5SDimitry Andric return Status("Pipe is already opened"); 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric int flags = O_RDONLY | O_NONBLOCK; 1550b57cec5SDimitry Andric if (!child_process_inherit) 1560b57cec5SDimitry Andric flags |= O_CLOEXEC; 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric Status error; 15906c3fb27SDimitry Andric int fd = FileSystem::Instance().Open(name.str().c_str(), flags); 1600b57cec5SDimitry Andric if (fd != -1) 1610b57cec5SDimitry Andric m_fds[READ] = fd; 1620b57cec5SDimitry Andric else 1630b57cec5SDimitry Andric error.SetErrorToErrno(); 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric return error; 1660b57cec5SDimitry Andric } 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric Status 1690b57cec5SDimitry Andric PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, 1700b57cec5SDimitry Andric bool child_process_inherit, 1710b57cec5SDimitry Andric const std::chrono::microseconds &timeout) { 1725f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 1735f757f3fSDimitry Andric if (CanReadUnlocked() || CanWriteUnlocked()) 1740b57cec5SDimitry Andric return Status("Pipe is already opened"); 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric int flags = O_WRONLY | O_NONBLOCK; 1770b57cec5SDimitry Andric if (!child_process_inherit) 1780b57cec5SDimitry Andric flags |= O_CLOEXEC; 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric using namespace std::chrono; 1810b57cec5SDimitry Andric const auto finish_time = Now() + timeout; 1820b57cec5SDimitry Andric 1835f757f3fSDimitry Andric while (!CanWriteUnlocked()) { 1840b57cec5SDimitry Andric if (timeout != microseconds::zero()) { 1850b57cec5SDimitry Andric const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); 1860b57cec5SDimitry Andric if (dur <= 0) 1870b57cec5SDimitry Andric return Status("timeout exceeded - reader hasn't opened so far"); 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric errno = 0; 191e8d8bef9SDimitry Andric int fd = ::open(name.str().c_str(), flags); 1920b57cec5SDimitry Andric if (fd == -1) { 1930b57cec5SDimitry Andric const auto errno_copy = errno; 1940b57cec5SDimitry Andric // We may get ENXIO if a reader side of the pipe hasn't opened yet. 1950b57cec5SDimitry Andric if (errno_copy != ENXIO && errno_copy != EINTR) 1960b57cec5SDimitry Andric return Status(errno_copy, eErrorTypePOSIX); 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric std::this_thread::sleep_for( 1990b57cec5SDimitry Andric milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); 2000b57cec5SDimitry Andric } else { 2010b57cec5SDimitry Andric m_fds[WRITE] = fd; 2020b57cec5SDimitry Andric } 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric return Status(); 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric 2085f757f3fSDimitry Andric int PipePosix::GetReadFileDescriptor() const { 2095f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_read_mutex); 2105f757f3fSDimitry Andric return GetReadFileDescriptorUnlocked(); 2115f757f3fSDimitry Andric } 2120b57cec5SDimitry Andric 2135f757f3fSDimitry Andric int PipePosix::GetReadFileDescriptorUnlocked() const { 2145f757f3fSDimitry Andric return m_fds[READ]; 2155f757f3fSDimitry Andric } 2165f757f3fSDimitry Andric 2175f757f3fSDimitry Andric int PipePosix::GetWriteFileDescriptor() const { 2185f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 2195f757f3fSDimitry Andric return GetWriteFileDescriptorUnlocked(); 2205f757f3fSDimitry Andric } 2215f757f3fSDimitry Andric 2225f757f3fSDimitry Andric int PipePosix::GetWriteFileDescriptorUnlocked() const { 2235f757f3fSDimitry Andric return m_fds[WRITE]; 2245f757f3fSDimitry Andric } 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric int PipePosix::ReleaseReadFileDescriptor() { 2275f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_read_mutex); 2285f757f3fSDimitry Andric return ReleaseReadFileDescriptorUnlocked(); 2295f757f3fSDimitry Andric } 2305f757f3fSDimitry Andric 2315f757f3fSDimitry Andric int PipePosix::ReleaseReadFileDescriptorUnlocked() { 2320b57cec5SDimitry Andric const int fd = m_fds[READ]; 2330b57cec5SDimitry Andric m_fds[READ] = PipePosix::kInvalidDescriptor; 2340b57cec5SDimitry Andric return fd; 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric int PipePosix::ReleaseWriteFileDescriptor() { 2385f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 2395f757f3fSDimitry Andric return ReleaseWriteFileDescriptorUnlocked(); 2405f757f3fSDimitry Andric } 2415f757f3fSDimitry Andric 2425f757f3fSDimitry Andric int PipePosix::ReleaseWriteFileDescriptorUnlocked() { 2430b57cec5SDimitry Andric const int fd = m_fds[WRITE]; 2440b57cec5SDimitry Andric m_fds[WRITE] = PipePosix::kInvalidDescriptor; 2450b57cec5SDimitry Andric return fd; 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric void PipePosix::Close() { 2495f757f3fSDimitry Andric std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 2505f757f3fSDimitry Andric CloseUnlocked(); 2515f757f3fSDimitry Andric } 2525f757f3fSDimitry Andric 2535f757f3fSDimitry Andric void PipePosix::CloseUnlocked() { 2545f757f3fSDimitry Andric CloseReadFileDescriptorUnlocked(); 2555f757f3fSDimitry Andric CloseWriteFileDescriptorUnlocked(); 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric Status PipePosix::Delete(llvm::StringRef name) { 2590b57cec5SDimitry Andric return llvm::sys::fs::remove(name); 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric bool PipePosix::CanRead() const { 2635f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_read_mutex); 2645f757f3fSDimitry Andric return CanReadUnlocked(); 2655f757f3fSDimitry Andric } 2665f757f3fSDimitry Andric 2675f757f3fSDimitry Andric bool PipePosix::CanReadUnlocked() const { 2680b57cec5SDimitry Andric return m_fds[READ] != PipePosix::kInvalidDescriptor; 2690b57cec5SDimitry Andric } 2700b57cec5SDimitry Andric 2710b57cec5SDimitry Andric bool PipePosix::CanWrite() const { 2725f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 2735f757f3fSDimitry Andric return CanWriteUnlocked(); 2745f757f3fSDimitry Andric } 2755f757f3fSDimitry Andric 2765f757f3fSDimitry Andric bool PipePosix::CanWriteUnlocked() const { 2770b57cec5SDimitry Andric return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric void PipePosix::CloseReadFileDescriptor() { 2815f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_read_mutex); 2825f757f3fSDimitry Andric CloseReadFileDescriptorUnlocked(); 2835f757f3fSDimitry Andric } 2845f757f3fSDimitry Andric void PipePosix::CloseReadFileDescriptorUnlocked() { 2855f757f3fSDimitry Andric if (CanReadUnlocked()) { 2860b57cec5SDimitry Andric close(m_fds[READ]); 2870b57cec5SDimitry Andric m_fds[READ] = PipePosix::kInvalidDescriptor; 2880b57cec5SDimitry Andric } 2890b57cec5SDimitry Andric } 2900b57cec5SDimitry Andric 2910b57cec5SDimitry Andric void PipePosix::CloseWriteFileDescriptor() { 2925f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 2935f757f3fSDimitry Andric CloseWriteFileDescriptorUnlocked(); 2945f757f3fSDimitry Andric } 2955f757f3fSDimitry Andric 2965f757f3fSDimitry Andric void PipePosix::CloseWriteFileDescriptorUnlocked() { 2975f757f3fSDimitry Andric if (CanWriteUnlocked()) { 2980b57cec5SDimitry Andric close(m_fds[WRITE]); 2990b57cec5SDimitry Andric m_fds[WRITE] = PipePosix::kInvalidDescriptor; 3000b57cec5SDimitry Andric } 3010b57cec5SDimitry Andric } 3020b57cec5SDimitry Andric 3030b57cec5SDimitry Andric Status PipePosix::ReadWithTimeout(void *buf, size_t size, 3040b57cec5SDimitry Andric const std::chrono::microseconds &timeout, 3050b57cec5SDimitry Andric size_t &bytes_read) { 3065f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_read_mutex); 3070b57cec5SDimitry Andric bytes_read = 0; 3085f757f3fSDimitry Andric if (!CanReadUnlocked()) 3090b57cec5SDimitry Andric return Status(EINVAL, eErrorTypePOSIX); 3100b57cec5SDimitry Andric 3115f757f3fSDimitry Andric const int fd = GetReadFileDescriptorUnlocked(); 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric SelectHelper select_helper; 3140b57cec5SDimitry Andric select_helper.SetTimeout(timeout); 3150b57cec5SDimitry Andric select_helper.FDSetRead(fd); 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric Status error; 3180b57cec5SDimitry Andric while (error.Success()) { 3190b57cec5SDimitry Andric error = select_helper.Select(); 3200b57cec5SDimitry Andric if (error.Success()) { 321480093f4SDimitry Andric auto result = 322480093f4SDimitry Andric ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read); 3230b57cec5SDimitry Andric if (result != -1) { 3240b57cec5SDimitry Andric bytes_read += result; 3250b57cec5SDimitry Andric if (bytes_read == size || result == 0) 3260b57cec5SDimitry Andric break; 3270b57cec5SDimitry Andric } else if (errno == EINTR) { 3280b57cec5SDimitry Andric continue; 3290b57cec5SDimitry Andric } else { 3300b57cec5SDimitry Andric error.SetErrorToErrno(); 3310b57cec5SDimitry Andric break; 3320b57cec5SDimitry Andric } 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric } 3350b57cec5SDimitry Andric return error; 3360b57cec5SDimitry Andric } 3370b57cec5SDimitry Andric 3380b57cec5SDimitry Andric Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { 3395f757f3fSDimitry Andric std::lock_guard<std::mutex> guard(m_write_mutex); 3400b57cec5SDimitry Andric bytes_written = 0; 3415f757f3fSDimitry Andric if (!CanWriteUnlocked()) 3420b57cec5SDimitry Andric return Status(EINVAL, eErrorTypePOSIX); 3430b57cec5SDimitry Andric 3445f757f3fSDimitry Andric const int fd = GetWriteFileDescriptorUnlocked(); 3450b57cec5SDimitry Andric SelectHelper select_helper; 3460b57cec5SDimitry Andric select_helper.SetTimeout(std::chrono::seconds(0)); 3470b57cec5SDimitry Andric select_helper.FDSetWrite(fd); 3480b57cec5SDimitry Andric 3490b57cec5SDimitry Andric Status error; 3500b57cec5SDimitry Andric while (error.Success()) { 3510b57cec5SDimitry Andric error = select_helper.Select(); 3520b57cec5SDimitry Andric if (error.Success()) { 353480093f4SDimitry Andric auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, 3540b57cec5SDimitry Andric size - bytes_written); 3550b57cec5SDimitry Andric if (result != -1) { 3560b57cec5SDimitry Andric bytes_written += result; 3570b57cec5SDimitry Andric if (bytes_written == size) 3580b57cec5SDimitry Andric break; 3590b57cec5SDimitry Andric } else if (errno == EINTR) { 3600b57cec5SDimitry Andric continue; 3610b57cec5SDimitry Andric } else { 3620b57cec5SDimitry Andric error.SetErrorToErrno(); 3630b57cec5SDimitry Andric } 3640b57cec5SDimitry Andric } 3650b57cec5SDimitry Andric } 3660b57cec5SDimitry Andric return error; 3670b57cec5SDimitry Andric } 368