authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 15:28:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-09 12:04:10-07:00
log5c0ac90721ec45c6712535a99cb90df48e59e121
tree02a2b6fe745af5d17fdffdf65307c917a69cac71
parentacd6ffdf69332d61b51f64f4707b472c147b7034

std: fix File.Writer sendfile with buffered contents

* File.Writer.seekBy passed wrong offset to setPosAdjustingBuffer. * File.Writer.sendFile incorrectly used non-logical position. Related to 1d764c1fdf04829cec5974d82cec901825a80e49 Test case provided by: Co-authored-by: Kendall Condon <goon.pri.low@gmail.com>

2 files changed, 40 insertions(+), 9 deletions(-)

lib/std/fs/File.zig+9-9
...@@ -1242,7 +1242,7 @@ pub const Reader = struct {...@@ -1242,7 +1242,7 @@ pub const Reader = struct {
1242 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {1242 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
1243 switch (r.mode) {1243 switch (r.mode) {
1244 .positional, .positional_reading => {1244 .positional, .positional_reading => {
1245 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));1245 setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
1246 },1246 },
1247 .streaming, .streaming_reading => {1247 .streaming, .streaming_reading => {
1248 if (posix.SEEK == void) {1248 if (posix.SEEK == void) {
...@@ -1251,7 +1251,7 @@ pub const Reader = struct {...@@ -1251,7 +1251,7 @@ pub const Reader = struct {
1251 }1251 }
1252 const seek_err = r.seek_err orelse e: {1252 const seek_err = r.seek_err orelse e: {
1253 if (posix.lseek_CUR(r.file.handle, offset)) |_| {1253 if (posix.lseek_CUR(r.file.handle, offset)) |_| {
1254 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));1254 setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
1255 return;1255 return;
1256 } else |err| {1256 } else |err| {
1257 r.seek_err = err;1257 r.seek_err = err;
...@@ -1275,16 +1275,16 @@ pub const Reader = struct {...@@ -1275,16 +1275,16 @@ pub const Reader = struct {
1275 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {1275 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
1276 switch (r.mode) {1276 switch (r.mode) {
1277 .positional, .positional_reading => {1277 .positional, .positional_reading => {
1278 setPosAdjustingBuffer(r, offset);1278 setLogicalPos(r, offset);
1279 },1279 },
1280 .streaming, .streaming_reading => {1280 .streaming, .streaming_reading => {
1281 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos));1281 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - logicalPos(r)));
1282 if (r.seek_err) |err| return err;1282 if (r.seek_err) |err| return err;
1283 posix.lseek_SET(r.file.handle, offset) catch |err| {1283 posix.lseek_SET(r.file.handle, offset) catch |err| {
1284 r.seek_err = err;1284 r.seek_err = err;
1285 return err;1285 return err;
1286 };1286 };
1287 setPosAdjustingBuffer(r, offset);1287 setLogicalPos(r, offset);
1288 },1288 },
1289 .failure => return r.seek_err.?,1289 .failure => return r.seek_err.?,
1290 }1290 }
...@@ -1294,7 +1294,7 @@ pub const Reader = struct {...@@ -1294,7 +1294,7 @@ pub const Reader = struct {
1294 return r.pos - r.interface.bufferedLen();1294 return r.pos - r.interface.bufferedLen();
1295 }1295 }
12961296
1297 fn setPosAdjustingBuffer(r: *Reader, offset: u64) void {1297 fn setLogicalPos(r: *Reader, offset: u64) void {
1298 const logical_pos = logicalPos(r);1298 const logical_pos = logicalPos(r);
1299 if (offset < logical_pos or offset >= r.pos) {1299 if (offset < logical_pos or offset >= r.pos) {
1300 r.interface.seek = 0;1300 r.interface.seek = 0;
...@@ -1855,7 +1855,7 @@ pub const Writer = struct {...@@ -1855,7 +1855,7 @@ pub const Writer = struct {
1855 return error.EndOfStream;1855 return error.EndOfStream;
1856 }1856 }
1857 const consumed = io_w.consume(@intCast(sbytes));1857 const consumed = io_w.consume(@intCast(sbytes));
1858 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;1858 file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed;
1859 return consumed;1859 return consumed;
1860 }1860 }
18611861
...@@ -1916,7 +1916,7 @@ pub const Writer = struct {...@@ -1916,7 +1916,7 @@ pub const Writer = struct {
1916 return error.EndOfStream;1916 return error.EndOfStream;
1917 }1917 }
1918 const consumed = io_w.consume(@bitCast(len));1918 const consumed = io_w.consume(@bitCast(len));
1919 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;1919 file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed;
1920 return consumed;1920 return consumed;
1921 }1921 }
19221922
...@@ -2049,7 +2049,7 @@ pub const Writer = struct {...@@ -2049,7 +2049,7 @@ pub const Writer = struct {
2049 reader_buffered: []const u8,2049 reader_buffered: []const u8,
2050 ) std.Io.Writer.FileError!usize {2050 ) std.Io.Writer.FileError!usize {
2051 const n = try drain(io_w, &.{reader_buffered}, 1);2051 const n = try drain(io_w, &.{reader_buffered}, 1);
2052 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;2052 file_reader.seekBy(@intCast(n)) catch return error.ReadFailed;
2053 return n;2053 return n;
2054 }2054 }
20552055
lib/std/fs/test.zig+31
...@@ -2145,3 +2145,34 @@ test "seekBy" {...@@ -2145,3 +2145,34 @@ test "seekBy" {
2145 try testing.expectEqual(15, n);2145 try testing.expectEqual(15, n);
2146 try testing.expectEqualStrings("t's test seekBy", buffer[0..15]);2146 try testing.expectEqualStrings("t's test seekBy", buffer[0..15]);
2147}2147}
2148
2149test "File.Writer sendfile with buffered contents" {
2150 var tmp_dir = testing.tmpDir(.{});
2151 defer tmp_dir.cleanup();
2152
2153 {
2154 try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" });
2155 const in = try tmp_dir.dir.openFile("a", .{});
2156 defer in.close();
2157 const out = try tmp_dir.dir.createFile("b", .{});
2158 defer out.close();
2159
2160 var in_buf: [2]u8 = undefined;
2161 var in_r = in.reader(&in_buf);
2162 _ = try in_r.getSize(); // Catch seeks past end by populating size
2163 try in_r.interface.fill(2);
2164
2165 var out_buf: [1]u8 = undefined;
2166 var out_w = out.writerStreaming(&out_buf);
2167 try out_w.interface.writeByte('a');
2168 try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));
2169 try out_w.interface.flush();
2170 }
2171
2172 var check = try tmp_dir.dir.openFile("b", .{});
2173 defer check.close();
2174 var check_buf: [4]u8 = undefined;
2175 var check_r = check.reader(&check_buf);
2176 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));
2177 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());
2178}