Skip to content

Commit fe10fde

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 c522699 commit fe10fde

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

lib/std/c/freebsd.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
88
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
99
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
1010

11+
pub const sf_hdtr = extern struct {
12+
headers: [*]iovec_const,
13+
hdr_cnt: c_int,
14+
trailers: [*]iovec_const,
15+
trl_cnt: c_int,
16+
};
17+
pub extern "c" fn sendfile(fd: c_int, s: c_int, offset: u64, nbytes: usize, sf_hdtr: ?*sf_hdtr, sbytes: ?*u64, flags: c_int) c_int;
18+
1119
pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int;
1220
pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
1321

lib/std/os.zig

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3111,6 +3111,121 @@ 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+
// XXX: compute new offset on EINTR/EAGAIN
3152+
var rc: usize = undefined;
3153+
var err: usize = undefined;
3154+
if (builtin.os == .linux) {
3155+
while (true) {
3156+
try lseek_SET(infd, offset);
3157+
3158+
if (optional_hdtr) |hdtr| {
3159+
try writev(outfd, hdtr.headers);
3160+
}
3161+
3162+
rc = system.sendfile(outfd, infd, null, count);
3163+
err = errno(rc);
3164+
3165+
if (optional_hdtr) |hdtr| {
3166+
try writev(outfd, hdtr.trailers);
3167+
}
3168+
3169+
switch (err) {
3170+
0 => return @intCast(usize, rc),
3171+
else => return unexpectedErrno(err),
3172+
3173+
EBADF => unreachable,
3174+
EINVAL => unreachable,
3175+
EFAULT => unreachable,
3176+
EAGAIN => if (std.event.Loop.instance) |loop| {
3177+
loop.waitUntilFdWritable(outfd);
3178+
continue;
3179+
} else {
3180+
return error.WouldBlock;
3181+
},
3182+
EIO => return error.InputOutput,
3183+
ENOMEM => return error.SystemResources,
3184+
EOVERFLOW => return error.Overflow,
3185+
ESPIPE => return error.Unseekable,
3186+
}
3187+
}
3188+
} else if (builtin.os == .freebsd) {
3189+
while (true) {
3190+
var rcount: u64 = 0;
3191+
var hdtr: std.c.sf_hdtr = undefined;
3192+
if (optional_hdtr) |h| {
3193+
hdtr = std.c.sf_hdtr{
3194+
.headers = h.headers.ptr,
3195+
.hdr_cnt = @intCast(c_int, h.headers.len),
3196+
.trailers = h.trailers.ptr,
3197+
.trl_cnt = @intCast(c_int, h.trailers.len),
3198+
};
3199+
}
3200+
err = errno(system.sendfile(infd, outfd, offset, count, &hdtr, &rcount, @intCast(c_int, flags)));
3201+
switch (err) {
3202+
0 => return @intCast(usize, rcount),
3203+
else => return unexpectedErrno(err),
3204+
3205+
EBADF => unreachable,
3206+
EFAULT => unreachable,
3207+
EINVAL => unreachable,
3208+
ENOTCAPABLE => unreachable,
3209+
ENOTCONN => unreachable,
3210+
ENOTSOCK => unreachable,
3211+
EAGAIN => if (std.event.Loop.instance) |loop| {
3212+
loop.waitUntilFdWritable(outfd);
3213+
continue;
3214+
} else {
3215+
return error.WouldBlock;
3216+
},
3217+
EBUSY => return error.DeviceBusy,
3218+
EINTR => continue,
3219+
EIO => return error.InputOutput,
3220+
ENOBUFS => return error.SystemResources,
3221+
EPIPE => return error.BrokenPipe,
3222+
}
3223+
}
3224+
} else {
3225+
@compileError("sendfile unimplemented for this target");
3226+
}
3227+
}
3228+
31143229
pub const PollError = error{
31153230
/// The kernel had no space to allocate file descriptor tables.
31163231
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)