Skip to content

Commit 68186bb

Browse files
committed
std: implement sendfile on linux
This changset adds a `sendfile(2)` syscall bindings to the linux bits component. Where available, the `sendfile64(2)` syscall will be transparently called. A wrapping function has also been added to the std.os to transform errno returns to Zig errors. Change-Id: I86769fc4382c0771e3656e7b21137bafd99a4411
1 parent 7a1cde7 commit 68186bb

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

lib/std/os.zig

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3111,6 +3111,84 @@ pub fn send(
31113111
return sendto(sockfd, buf, flags, null, 0);
31123112
}
31133113

3114+
pub const SendFileError = error{
3115+
/// There was an unspecified error while reading from infd.
3116+
InputOutput,
3117+
3118+
/// There was insufficient resources for processing.
3119+
SystemResources,
3120+
3121+
/// The value provided for count overflows the maximum size of either
3122+
/// infd or outfd.
3123+
Overflow,
3124+
3125+
/// Offset was provided, but infd is not seekable.
3126+
Unseekable,
3127+
3128+
/// The outfd is marked nonblocking and the requested operation would block, and
3129+
/// there is no global event loop configured.
3130+
WouldBlock,
3131+
} || WriteError || UnexpectedError;
3132+
3133+
pub const sf_hdtr = struct {
3134+
headers: []iovec_const,
3135+
trailers: []iovec_const,
3136+
};
3137+
3138+
/// Transfer data between file descriptors.
3139+
///
3140+
/// The `sendfile` call copies `count` bytes from one file descriptor to another within the kernel. This can
3141+
/// be more performant than transferring data from the kernel to user space and back, such as with
3142+
/// `read` and `write` calls.
3143+
///
3144+
/// The `infd` should be a file descriptor opened for reading, and `outfd` should be a file descriptor
3145+
/// opened for writing. Copying will begin at `offset`, if not null, which will be updated to reflect
3146+
/// the number of bytes read. If `offset` is null, the copying will begin at the current seek position,
3147+
/// and the file position will be updated.
3148+
pub fn sendfile(infd: fd_t, outfd: fd_t, offset: u64, count: usize, optional_hdtr: ?*const sf_hdtr, flags: u32) SendFileError!usize {
3149+
// XXX: check if offset is > length of file, return 0 bytes written
3150+
// XXX: document systems where headers are sent atomically.
3151+
var rc: usize = undefined;
3152+
var err: usize = undefined;
3153+
if (builtin.os == .linux) {
3154+
while (true) {
3155+
try lseek_SET(infd, offset);
3156+
3157+
if (optional_hdtr) |hdtr| {
3158+
try writev(outfd, hdtr.headers);
3159+
}
3160+
3161+
rc = system.sendfile(outfd, infd, null, count);
3162+
err = errno(rc);
3163+
3164+
if (optional_hdtr) |hdtr| {
3165+
try writev(outfd, hdtr.trailers);
3166+
}
3167+
3168+
switch (err) {
3169+
0 => return @intCast(usize, rc),
3170+
else => return unexpectedErrno(err),
3171+
3172+
EBADF => unreachable,
3173+
EINVAL => unreachable,
3174+
EFAULT => unreachable,
3175+
EAGAIN => if (std.event.Loop.instance) |loop| {
3176+
loop.waitUntilFdWritable(outfd);
3177+
continue;
3178+
} else {
3179+
return error.WouldBlock;
3180+
},
3181+
EIO => return error.InputOutput,
3182+
ENOMEM => return error.SystemResources,
3183+
EOVERFLOW => return error.Overflow,
3184+
ESPIPE => return error.Unseekable,
3185+
}
3186+
}
3187+
} else {
3188+
@compileError("sendfile unimplemented for this target");
3189+
}
3190+
}
3191+
31143192
pub const PollError = error{
31153193
/// The kernel had no space to allocate file descriptor tables.
31163194
SystemResources,

lib/std/os/linux.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,14 @@ pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const s
846846
return syscall6(SYS_sendto, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen));
847847
}
848848

849+
pub fn sendfile(outfd: i32, infd: i32, offset: ?*u64, count: usize) usize {
850+
if (@hasDecl(@This(), "SYS_sendfile64")) {
851+
return syscall4(SYS_sendfile64, @bitCast(usize, @as(isize, outfd)), @bitCast(usize, @as(isize, infd)), @ptrToInt(offset), count);
852+
} else {
853+
return syscall4(SYS_sendfile, @bitCast(usize, @as(isize, outfd)), @bitCast(usize, @as(isize, infd)), @ptrToInt(offset), count);
854+
}
855+
}
856+
849857
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
850858
if (builtin.arch == .i386) {
851859
return socketcall(SC_socketpair, &[4]usize{ @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]) });

0 commit comments

Comments
 (0)