diff --git a/libc/assert.h b/libc/assert.h index ce6933b2a..e6ef79237 100644 --- a/libc/assert.h +++ b/libc/assert.h @@ -17,6 +17,20 @@ void __assert_fail(const char *, const char *, int) hidden relegated; #define static_assert _Static_assert #endif +#define _unassert(x) \ + do { \ + if (__builtin_expect(!(x), 0)) { \ + unreachable; \ + } \ + } while (0) + +#define _npassert(x) \ + do { \ + if (__builtin_expect(!(x), 0)) { \ + notpossible; \ + } \ + } while (0) + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_ASSERT_H_ */ diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 25ec37f4c..349c00988 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -16,12 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -33,74 +36,89 @@ #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" +#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { - static bool once, demodernize; - int i, err; - ssize_t rc; - size_t got, toto; + int e, i; + size_t got; + bool masked; + ssize_t rc, toto; + sigset_t mask, oldmask; + + if (fd < 0) { + return ebadf(); + } + + if (iovlen < 0) { + return einval(); + } + + if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { + return efault(); + } - if (fd < 0) return einval(); - if (iovlen < 0) return einval(); - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) return efault(); if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_read)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off); - } else if (IsWindows()) { + } + + if (IsMetal()) { + return espipe(); // must be serial or console if not zipos + } + + if (IsWindows()) { if (fd < g_fds.n) { return sys_read_nt(g_fds.p + fd, iov, iovlen, off); } else { return ebadf(); } - } else if (IsMetal()) { - return enosys(); } - if (iovlen == 1) { - return sys_pread(fd, iov[0].iov_base, iov[0].iov_len, off, off); - } - - /* - * NT, 2018-era XNU, and 2007-era Linux don't support this system call - */ - if (!__vforked && !once) { - err = errno; - rc = sys_preadv(fd, iov, iovlen, off, off); - if (rc == -1 && errno == ENOSYS) { - errno = err; - once = true; - demodernize = true; - STRACE("demodernizing %s() due to %s", "preadv", "ENOSYS"); - } else { - once = true; - return rc; - } - } - - if (!demodernize) { - return sys_preadv(fd, iov, iovlen, off, off); + while (iovlen && !iov->iov_len) { + --iovlen; + ++iov; } if (!iovlen) { - return sys_pread(fd, NULL, 0, off, off); + return sys_pread(fd, 0, 0, off, off); } + if (iovlen == 1) { + return sys_pread(fd, iov->iov_base, iov->iov_len, off, off); + } + + e = errno; + rc = sys_preadv(fd, iov, iovlen, off, off); + if (rc != -1 || errno != ENOSYS) return rc; + errno = e; + for (toto = i = 0; i < iovlen; ++i) { rc = sys_pread(fd, iov[i].iov_base, iov[i].iov_len, off, off); if (rc == -1) { - if (toto && (errno == EINTR || errno == EAGAIN)) { - return toto; - } else { - return -1; + if (!toto) { + toto = -1; } + break; } + got = rc; toto += got; + off += got; if (got != iov[i].iov_len) { break; } + + if (!masked) { + sigfillset(&mask); + _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); + masked = true; + } + } + + if (masked) { + _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index 120da1dba..eda149625 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -16,11 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -31,75 +33,90 @@ #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" +#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { - static bool once, demodernize; - int i, err; - ssize_t rc; - size_t sent, toto; + int i, e; + bool masked; + size_t sent; + ssize_t rc, toto; + sigset_t mask, oldmask; + + if (fd < 0) { + return ebadf(); + } + + if (iovlen < 0) { + return einval(); + } + + if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { + return efault(); + } - if (fd < 0) return einval(); - if (iovlen < 0) return einval(); - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) return efault(); if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_write)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off); - } else if (IsWindows()) { + } + + if (IsWindows()) { if (fd < g_fds.n) { return sys_write_nt(fd, iov, iovlen, off); } else { return ebadf(); } - } else if (IsMetal()) { - return enosys(); } - if (iovlen == 1) { - return sys_pwrite(fd, iov[0].iov_base, iov[0].iov_len, off, off); + if (IsMetal()) { + return espipe(); // must be serial or console if not zipos } - /* - * NT, 2018-era XNU, and 2007-era Linux don't support this system call - */ - if (!once) { - err = errno; - rc = sys_pwritev(fd, iov, iovlen, off, off); - if (rc == -1 && errno == ENOSYS) { - errno = err; - once = true; - demodernize = true; - STRACE("demodernizing %s() due to %s", "pwritev", "ENOSYS"); - } else { - once = true; - return rc; - } - } - - if (!demodernize) { - return sys_pwritev(fd, iov, iovlen, off, off); + while (iovlen && !iov->iov_len) { + --iovlen; + ++iov; } if (!iovlen) { - return sys_pwrite(fd, NULL, 0, off, off); + return sys_pwrite(fd, 0, 0, off, off); } + if (iovlen == 1) { + return sys_pwrite(fd, iov->iov_base, iov->iov_len, off, off); + } + + e = errno; + rc = sys_pwritev(fd, iov, iovlen, off, off); + if (rc != -1 || errno != ENOSYS) return rc; + errno = e; + for (toto = i = 0; i < iovlen; ++i) { rc = sys_pwrite(fd, iov[i].iov_base, iov[i].iov_len, off, off); if (rc == -1) { - if (toto && (errno == EINTR || errno == EAGAIN)) { - return toto; - } else { - return -1; + if (!toto) { + toto = -1; } + break; } + sent = rc; toto += sent; + off += sent; if (sent != iov[i].iov_len) { break; } + + if (!masked) { + sigfillset(&mask); + _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); + masked = true; + } + } + + if (masked) { + _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/test/libc/calls/preadv_test.c b/test/libc/calls/preadv_test.c new file mode 100644 index 000000000..334918e93 --- /dev/null +++ b/test/libc/calls/preadv_test.c @@ -0,0 +1,88 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/iovec.h" +#include "libc/errno.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(preadv, ebadf) { + EXPECT_SYS(EBADF, -1, preadv(-1, 0, 0, 0)); + EXPECT_SYS(EBADF, -1, pwritev(-1, 0, 0, 0)); +} + +TEST(preadv, testOneIovecWorks_itDelegatesToPread) { + char b1[2] = "hi"; + struct iovec v[1] = {{b1, 2}}; + ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); + EXPECT_SYS(0, 2, pwritev(3, v, 1, 0)); + b1[0] = 0; + b1[1] = 0; + EXPECT_SYS(0, 0, preadv(3, 0, 0, 0)); + EXPECT_SYS(0, 0, pwritev(3, 0, 0, 0)); + EXPECT_SYS(0, 2, preadv(3, v, 1, 0)); + ASSERT_EQ('h', b1[0]); + ASSERT_EQ('i', b1[1]); + b1[0] = 0; + b1[1] = 0x55; + EXPECT_SYS(0, 1, preadv(3, v, 1, 1)); + ASSERT_EQ('i', b1[0]); + ASSERT_EQ(0x55, b1[1]); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(preadv, testTwoIovecWorks_itMustDoItsThing) { + char b1[1] = "h"; + char b2[1] = "i"; + struct iovec v[2] = {{b1, 1}, {b2, 1}}; + ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); + EXPECT_SYS(0, 2, pwritev(3, v, 2, 0)); + b1[0] = 0; + b2[0] = 0; + EXPECT_SYS(0, 2, preadv(3, v, 2, 0)); + ASSERT_EQ('h', b1[0]); + ASSERT_EQ('i', b2[0]); + b1[0] = 0; + b2[0] = 0x55; + EXPECT_SYS(0, 1, preadv(3, v, 2, 1)); + ASSERT_EQ('i', b1[0]); + ASSERT_EQ(0x55, b2[0]); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(pwritev, writePastEof_zeroExtendsFile_alsoDoesntChangeFilePointer) { + char b[6] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; + char s[2] = "hi"; + struct iovec v[1] = {{s, 2}}; + ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS(EINVAL, -1, pwritev(3, v, 1, -999)); + ASSERT_SYS(0, 2, pwritev(3, v, 1, 0)); + ASSERT_SYS(0, 2, pwritev(3, v, 1, 4)); + ASSERT_SYS(0, 2, preadv(3, v, 1, 0)); + ASSERT_SYS(0, 6, read(3, b, 6)); + ASSERT_EQ('h', b[0]); + ASSERT_EQ('i', b[1]); + ASSERT_EQ(0, b[2]); + ASSERT_EQ(0, b[3]); + ASSERT_EQ('h', b[4]); + ASSERT_EQ('i', b[5]); + ASSERT_SYS(0, 0, close(3)); +}