Commit 72c4b80d31

daurnimator <quae@daurnimator.com>
2021-08-22 21:22:53
std.os: (p)writev should perform partial writes if iov.len > IOV_MAX
Co-authored-by: Veikka Tuominen <git@vexu.eu>
1 parent 62e3d67
lib/std/os/bits/darwin.zig
@@ -235,6 +235,7 @@ pub const host_t = mach_port_t;
 pub const CALENDAR_CLOCK = 1;
 
 pub const PATH_MAX = 1024;
+pub const IOV_MAX = 16;
 
 pub const STDIN_FILENO = 0;
 pub const STDOUT_FILENO = 1;
lib/std/os/bits/dragonfly.zig
@@ -168,6 +168,7 @@ pub const SA_NOCLDWAIT = 0x0020;
 pub const SA_SIGINFO = 0x0040;
 
 pub const PATH_MAX = 1024;
+pub const IOV_MAX = KERN_IOV_MAX;
 
 pub const ino_t = c_ulong;
 
lib/std/os/bits/freebsd.zig
@@ -238,8 +238,10 @@ pub const CTL_DEBUG = 5;
 
 pub const KERN_PROC = 14; // struct: process entries
 pub const KERN_PROC_PATHNAME = 12; // path to executable
+pub const KERN_IOV_MAX = 35;
 
 pub const PATH_MAX = 1024;
+pub const IOV_MAX = KERN_IOV_MAX;
 
 pub const STDIN_FILENO = 0;
 pub const STDOUT_FILENO = 1;
lib/std/os/bits/netbsd.zig
@@ -405,8 +405,10 @@ pub const CTL_DEBUG = 5;
 
 pub const KERN_PROC_ARGS = 48; // struct: process argv/env
 pub const KERN_PROC_PATHNAME = 5; // path to executable
+pub const KERN_IOV_MAX = 38;
 
 pub const PATH_MAX = 1024;
+pub const IOV_MAX = KERN_IOV_MAX;
 
 pub const STDIN_FILENO = 0;
 pub const STDOUT_FILENO = 1;
lib/std/os/bits/wasi.zig
@@ -76,6 +76,8 @@ pub const kernel_stat = struct {
     }
 };
 
+pub const IOV_MAX = 1024;
+
 pub const AT_REMOVEDIR: u32 = 0x4;
 pub const AT_FDCWD: fd_t = -2;
 
lib/std/os/test.zig
@@ -786,3 +786,17 @@ test "dup & dup2" {
     var buf: [7]u8 = undefined;
     try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]);
 }
+
+test "writev longer than IOV_MAX" {
+    if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
+
+    var tmp = tmpDir(.{});
+    defer tmp.cleanup();
+
+    var file = try tmp.dir.createFile("pwritev", .{});
+    defer file.close();
+
+    const iovecs = [_]os.iovec_const{.{ .iov_base = "a", .iov_len = 1 }} ** (os.IOV_MAX + 1);
+    const amt = try file.writev(&iovecs);
+    try testing.expectEqual(@as(usize, os.IOV_MAX), amt);
+}
lib/std/os.zig
@@ -787,7 +787,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
 ///
-/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
+/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
     if (std.Target.current.os.tag == .windows) {
         // TODO improve this to use WriteFileScatter
@@ -816,7 +816,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
         }
     }
 
-    const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
+    const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @intCast(u31, iov.len);
     while (true) {
         const rc = system.writev(fd, iov.ptr, iov_count);
         switch (errno(rc)) {
@@ -954,7 +954,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
 /// * Darwin
 /// * Windows
 ///
-/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
+/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
     const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) {
         .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
@@ -997,7 +997,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
     else
         system.pwritev;
 
-    const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
+    const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @intCast(u31, iov.len);
     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
     while (true) {
         const rc = pwritev_sym(fd, iov.ptr, iov_count, ioffset);