compat/posix: introduce writev(3p) wrapper

In a subsequent commit we're going to add the first caller to writev(3p). Introduce a compatibility wrapper for this syscall that we can use on systems that don't have this syscall. The syscall exists on modern Unixes like Linux and macOS, and seemingly even for NonStop according to [1]. It doesn't seem to exist on Windows though. [1]: http://nonstoptools.com/manuals/OSS-SystemCalls.pdf [2]: https://www.gnu.org/software/gnulib/manual/html_node/writev.html Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Mar 13, 2026 at 07:45 UTC 3b9b2c2a29a1d529ca9884fa0a6529f6e2496abe
5 files changed +65
Makefile
+4
@@ -2015,6 +2015,10 @@ ifdef NO_PREAD
2015 COMPAT_CFLAGS += -DNO_PREAD
2016 COMPAT_OBJS += compat/pread.o
2017 endif
2018 +ifdef NO_WRITEV
2019 + COMPAT_CFLAGS += -DNO_WRITEV
2020 + COMPAT_OBJS += compat/writev.o
2021 +endif
2022 ifdef NO_FAST_WORKING_DIRECTORY
2023 BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
2024 endif
compat/posix.h
+14
@@ -137,6 +137,9 @@
137 #include <sys/socket.h>
138 #include <sys/ioctl.h>
139 #include <sys/statvfs.h>
140 +#ifndef NO_WRITEV
141 +#include <sys/uio.h>
142 +#endif
143 #include <termios.h>
144 #ifndef NO_SYS_SELECT_H
145 #include <sys/select.h>
@@ -323,6 +326,17 @@ int git_lstat(const char *, struct stat *);
326 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
327 #endif
328
329 +#ifdef NO_WRITEV
330 +#define writev git_writev
331 +#define iovec git_iovec
332 +struct git_iovec {
333 + void *iov_base;
334 + size_t iov_len;
335 +};
336 +
337 +ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
338 +#endif
339 +
340 #ifdef NO_SETENV
341 #define setenv gitsetenv
342 int gitsetenv(const char *, const char *, int);
compat/writev.c new
+44
@@ -0,0 +1,44 @@
1 +#include "../git-compat-util.h"
2 +#include "../wrapper.h"
3 +
4 +ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt)
5 +{
6 + size_t total_written = 0;
7 + size_t sum = 0;
8 +
9 + /*
10 + * According to writev(3p), the syscall shall error with EINVAL in case
11 + * the sum of `iov_len` overflows `ssize_t`.
12 + */
13 + for (int i = 0; i < iovcnt; i++) {
14 + if (iov[i].iov_len > maximum_signed_value_of_type(ssize_t) ||
15 + iov[i].iov_len + sum > maximum_signed_value_of_type(ssize_t)) {
16 + errno = EINVAL;
17 + return -1;
18 + }
19 +
20 + sum += iov[i].iov_len;
21 + }
22 +
23 + for (int i = 0; i < iovcnt; i++) {
24 + const char *bytes = iov[i].iov_base;
25 + size_t iovec_written = 0;
26 +
27 + while (iovec_written < iov[i].iov_len) {
28 + ssize_t bytes_written = xwrite(fd, bytes + iovec_written,
29 + iov[i].iov_len - iovec_written);
30 + if (bytes_written < 0) {
31 + if (total_written)
32 + goto out;
33 + return bytes_written;
34 + }
35 + if (!bytes_written)
36 + goto out;
37 + iovec_written += bytes_written;
38 + total_written += bytes_written;
39 + }
40 + }
41 +
42 +out:
43 + return (ssize_t) total_written;
44 +}
config.mak.uname
+2
@@ -457,6 +457,7 @@ ifeq ($(uname_S),Windows)
457 SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
458 HAVE_ALLOCA_H = YesPlease
459 NO_PREAD = YesPlease
460 + NO_WRITEV = YesPlease
461 NEEDS_CRYPTO_WITH_SSL = YesPlease
462 NO_LIBGEN_H = YesPlease
463 NO_POLL = YesPlease
@@ -672,6 +673,7 @@ ifeq ($(uname_S),MINGW)
673 pathsep = ;
674 HAVE_ALLOCA_H = YesPlease
675 NO_PREAD = YesPlease
676 + NO_WRITEV = YesPlease
677 NEEDS_CRYPTO_WITH_SSL = YesPlease
678 NO_LIBGEN_H = YesPlease
679 NO_POLL = YesPlease
meson.build
+1
@@ -1405,6 +1405,7 @@ checkfuncs = {
1405 'initgroups' : [],
1406 'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
1407 'pread' : ['pread.c'],
1408 + 'writev' : ['writev.c'],
1409 }
1410
1411 if host_machine.system() == 'windows'