]> git.ipfire.org Git - thirdparty/git.git/commitdiff
compat/posix: introduce writev(3p) wrapper
authorPatrick Steinhardt <ps@pks.im>
Thu, 16 Jul 2026 07:52:19 +0000 (09:52 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 16 Jul 2026 20:59:13 +0000 (13:59 -0700)
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>
Makefile
compat/posix.h
compat/writev.c [new file with mode: 0644]
config.mak.uname
contrib/buildsystems/CMakeLists.txt
meson.build

index 1f3f099f5c5705e96bd51ef87e9edc4be6782ba6..eda5ecc5b4ad32897d5beccbfb36cbb887c65bf3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2033,6 +2033,10 @@ ifdef NO_PREAD
        COMPAT_CFLAGS += -DNO_PREAD
        COMPAT_OBJS += compat/pread.o
 endif
+ifdef NO_WRITEV
+       COMPAT_CFLAGS += -DNO_WRITEV
+       COMPAT_OBJS += compat/writev.o
+endif
 ifdef NO_FAST_WORKING_DIRECTORY
        BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
 endif
index e2e794cad7d41980ad55333a4a1e62034cd9bca8..71cc7316204187441fdfb20fa6c0280f28e856f1 100644 (file)
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/statvfs.h>
+#ifndef NO_WRITEV
+#include <sys/uio.h>
+#endif
 #include <termios.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
@@ -334,6 +337,17 @@ int git_lstat(const char *, struct stat *);
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
 
+#ifdef NO_WRITEV
+#define writev git_writev
+#define iovec git_iovec
+struct git_iovec {
+       void *iov_base;
+       size_t iov_len;
+};
+
+ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
+#endif
+
 #ifdef NO_SETENV
 #define setenv gitsetenv
 int gitsetenv(const char *, const char *, int);
diff --git a/compat/writev.c b/compat/writev.c
new file mode 100644 (file)
index 0000000..ab2e223
--- /dev/null
@@ -0,0 +1,44 @@
+#include "../git-compat-util.h"
+#include "../wrapper.h"
+
+ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt)
+{
+       size_t total_written = 0;
+       size_t sum = 0;
+
+       /*
+        * According to writev(3p), the syscall shall error with EINVAL in case
+        * the sum of `iov_len` overflows `ssize_t`.
+        */
+       for (int i = 0; i < iovcnt; i++) {
+               if (iov[i].iov_len > maximum_signed_value_of_type(ssize_t) ||
+                   iov[i].iov_len + sum > maximum_signed_value_of_type(ssize_t)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               sum += iov[i].iov_len;
+       }
+
+       for (int i = 0; i < iovcnt; i++) {
+               const char *bytes = iov[i].iov_base;
+               size_t iovec_written = 0;
+
+               while (iovec_written < iov[i].iov_len) {
+                       ssize_t bytes_written = xwrite(fd, bytes + iovec_written,
+                                                      iov[i].iov_len - iovec_written);
+                       if (bytes_written < 0) {
+                               if (total_written)
+                                       goto out;
+                               return bytes_written;
+                       }
+                       if (!bytes_written)
+                               goto out;
+                       iovec_written += bytes_written;
+                       total_written += bytes_written;
+               }
+       }
+
+out:
+       return (ssize_t) total_written;
+}
index 9ebd240378ca5976907f4572ac814c7716aa2c66..95ef6e64dcabff60ee9f0ce2e75a886df374b9c6 100644 (file)
@@ -483,6 +483,7 @@ ifeq ($(uname_S),Windows)
        SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
        HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
+       NO_WRITEV = YesPlease
        NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_POLL = YesPlease
@@ -697,6 +698,7 @@ ifeq ($(uname_S),MINGW)
        pathsep = ;
        HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
+       NO_WRITEV = YesPlease
        NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_POLL = YesPlease
index a57c4b464fa456e7eeef8a47538eb490495e1d32..8f56203f34d9bcb4346fdfe01258b8087497c999 100644 (file)
@@ -378,7 +378,7 @@ endif()
 #function checks
 set(function_checks
        strcasestr memmem strlcpy strtoimax strtoumax strtoull
-       setenv mkdtemp poll pread memmem)
+       setenv mkdtemp poll pread memmem writev)
 
 #unsetenv,hstrerror are incompatible with windows build
 if(NOT WIN32)
@@ -423,6 +423,10 @@ if(NOT HAVE_MEMMEM)
        list(APPEND compat_SOURCES compat/memmem.c)
 endif()
 
+if(NOT HAVE_WRITEV)
+       list(APPEND compat_SOURCES compat/writev.c)
+endif()
+
 if(NOT WIN32)
        if(NOT HAVE_UNSETENV)
                list(APPEND compat_SOURCES compat/unsetenv.c)
index 9434b56960ba80aab63e36a17ffc30cb8b5c1ef7..43373924aa79c3d4eda3650c63fc029fdcf40e54 100644 (file)
@@ -1448,6 +1448,7 @@ checkfuncs = {
   'initgroups' : [],
   'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
   'pread' : ['pread.c'],
+  'writev' : ['writev.c'],
 }
 
 if host_machine.system() == 'windows'