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 Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
Jul 16, 2026 at 09:52 UTC
1ed0bc4e3b297c9dcfaf697760a8a12c1253c091
6 files changed
+70
-1
Makefile
+4
@@ -2033,6 +2033,10 @@ ifdef NO_PREAD
2033
COMPAT_CFLAGS += -DNO_PREAD
2034
COMPAT_OBJS += compat/pread.o
2035
endif
2036
+ifdef NO_WRITEV
2037
+ COMPAT_CFLAGS += -DNO_WRITEV
2038
+ COMPAT_OBJS += compat/writev.o
2039
+endif
2040
ifdef NO_FAST_WORKING_DIRECTORY
2041
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
2042
endif
compat/posix.h
+14
@@ -148,6 +148,9 @@
148
#include <sys/socket.h>
149
#include <sys/ioctl.h>
150
#include <sys/statvfs.h>
151
+#ifndef NO_WRITEV
152
+#include <sys/uio.h>
153
+#endif
154
#include <termios.h>
155
#ifndef NO_SYS_SELECT_H
156
#include <sys/select.h>
@@ -334,6 +337,17 @@ int git_lstat(const char *, struct stat *);
337
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
338
#endif
339
340
+#ifdef NO_WRITEV
341
+#define writev git_writev
342
+#define iovec git_iovec
343
+struct git_iovec {
344
+ void *iov_base;
345
+ size_t iov_len;
346
+};
347
+
348
+ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
349
+#endif
350
+
351
#ifdef NO_SETENV
352
#define setenv gitsetenv
353
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
@@ -483,6 +483,7 @@ ifeq ($(uname_S),Windows)
483
SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
484
HAVE_ALLOCA_H = YesPlease
485
NO_PREAD = YesPlease
486
+ NO_WRITEV = YesPlease
487
NEEDS_CRYPTO_WITH_SSL = YesPlease
488
NO_LIBGEN_H = YesPlease
489
NO_POLL = YesPlease
@@ -697,6 +698,7 @@ ifeq ($(uname_S),MINGW)
698
pathsep = ;
699
HAVE_ALLOCA_H = YesPlease
700
NO_PREAD = YesPlease
701
+ NO_WRITEV = YesPlease
702
NEEDS_CRYPTO_WITH_SSL = YesPlease
703
NO_LIBGEN_H = YesPlease
704
NO_POLL = YesPlease
contrib/buildsystems/CMakeLists.txt
+5
-1
@@ -378,7 +378,7 @@ endif()
378
#function checks
379
set(function_checks
380
strcasestr memmem strlcpy strtoimax strtoumax strtoull
381
- setenv mkdtemp poll pread memmem)
381
+ setenv mkdtemp poll pread memmem writev)
382
383
#unsetenv,hstrerror are incompatible with windows build
384
if(NOT WIN32)
@@ -423,6 +423,10 @@ if(NOT HAVE_MEMMEM)
423
list(APPEND compat_SOURCES compat/memmem.c)
424
endif()
425
426
+if(NOT HAVE_WRITEV)
427
+ list(APPEND compat_SOURCES compat/writev.c)
428
+endif()
429
+
430
if(NOT WIN32)
431
if(NOT HAVE_UNSETENV)
432
list(APPEND compat_SOURCES compat/unsetenv.c)
meson.build
+1
@@ -1448,6 +1448,7 @@ checkfuncs = {
1448
'initgroups' : [],
1449
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
1450
'pread' : ['pread.c'],
1451
+ 'writev' : ['writev.c'],
1452
}
1453
1454
if host_machine.system() == 'windows'