authorgravatar for terinjokes@gmail.comTerin Stock <terinjokes@gmail.com> 2020-01-10 02:03:56-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-02 12:54:50-05:00
logbd287dd1942f0a72e6bd9dc8475bd4e7d34fa5f8
tree7288fbd6c28ee5b67e880f2bf7f4c0bc2f269377
parent00be934569d25e3b041091ff63a4cf6c456d1403
signaturelock-open Commit is signed but in an unrecognized format.

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

3 files changed, 131 insertions(+), 0 deletions(-)

lib/std/c/freebsd.zig+8
......@@ -8,6 +8,14 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
88pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
99pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
1010
11pub const sf_hdtr = extern struct {
12 headers: [*]iovec_const,
13 hdr_cnt: c_int,
14 trailers: [*]iovec_const,
15 trl_cnt: c_int,
16};
17pub 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
1119pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int;
1220pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
1321
lib/std/os.zig+115
......@@ -3498,6 +3498,121 @@ pub fn send(
34983498 return sendto(sockfd, buf, flags, null, 0);
34993499}
35003500
3501pub const SendFileError = error{
3502 /// There was an unspecified error while reading from infd.
3503 InputOutput,
3504
3505 /// There was insufficient resources for processing.
3506 SystemResources,
3507
3508 /// The value provided for count overflows the maximum size of either
3509 /// infd or outfd.
3510 Overflow,
3511
3512 /// Offset was provided, but infd is not seekable.
3513 Unseekable,
3514
3515 /// The outfd is marked nonblocking and the requested operation would block, and
3516 /// there is no global event loop configured.
3517 WouldBlock,
3518} || WriteError || UnexpectedError;
3519
3520pub const sf_hdtr = struct {
3521 headers: []iovec_const,
3522 trailers: []iovec_const,
3523};
3524
3525/// Transfer data between file descriptors.
3526///
3527/// The `sendfile` call copies `count` bytes from one file descriptor to another within the kernel. This can
3528/// be more performant than transferring data from the kernel to user space and back, such as with
3529/// `read` and `write` calls.
3530///
3531/// The `infd` should be a file descriptor opened for reading, and `outfd` should be a file descriptor
3532/// opened for writing. Copying will begin at `offset`, if not null, which will be updated to reflect
3533/// the number of bytes read. If `offset` is null, the copying will begin at the current seek position,
3534/// and the file position will be updated.
3535pub fn sendfile(infd: fd_t, outfd: fd_t, offset: u64, count: usize, optional_hdtr: ?*const sf_hdtr, flags: u32) SendFileError!usize {
3536 // XXX: check if offset is > length of file, return 0 bytes written
3537 // XXX: document systems where headers are sent atomically.
3538 // XXX: compute new offset on EINTR/EAGAIN
3539 var rc: usize = undefined;
3540 var err: usize = undefined;
3541 if (builtin.os == .linux) {
3542 while (true) {
3543 try lseek_SET(infd, offset);
3544
3545 if (optional_hdtr) |hdtr| {
3546 try writev(outfd, hdtr.headers);
3547 }
3548
3549 rc = system.sendfile(outfd, infd, null, count);
3550 err = errno(rc);
3551
3552 if (optional_hdtr) |hdtr| {
3553 try writev(outfd, hdtr.trailers);
3554 }
3555
3556 switch (err) {
3557 0 => return @intCast(usize, rc),
3558 else => return unexpectedErrno(err),
3559
3560 EBADF => unreachable,
3561 EINVAL => unreachable,
3562 EFAULT => unreachable,
3563 EAGAIN => if (std.event.Loop.instance) |loop| {
3564 loop.waitUntilFdWritable(outfd);
3565 continue;
3566 } else {
3567 return error.WouldBlock;
3568 },
3569 EIO => return error.InputOutput,
3570 ENOMEM => return error.SystemResources,
3571 EOVERFLOW => return error.Overflow,
3572 ESPIPE => return error.Unseekable,
3573 }
3574 }
3575 } else if (builtin.os == .freebsd) {
3576 while (true) {
3577 var rcount: u64 = 0;
3578 var hdtr: std.c.sf_hdtr = undefined;
3579 if (optional_hdtr) |h| {
3580 hdtr = std.c.sf_hdtr{
3581 .headers = h.headers.ptr,
3582 .hdr_cnt = @intCast(c_int, h.headers.len),
3583 .trailers = h.trailers.ptr,
3584 .trl_cnt = @intCast(c_int, h.trailers.len),
3585 };
3586 }
3587 err = errno(system.sendfile(infd, outfd, offset, count, &hdtr, &rcount, @intCast(c_int, flags)));
3588 switch (err) {
3589 0 => return @intCast(usize, rcount),
3590 else => return unexpectedErrno(err),
3591
3592 EBADF => unreachable,
3593 EFAULT => unreachable,
3594 EINVAL => unreachable,
3595 ENOTCAPABLE => unreachable,
3596 ENOTCONN => unreachable,
3597 ENOTSOCK => unreachable,
3598 EAGAIN => if (std.event.Loop.instance) |loop| {
3599 loop.waitUntilFdWritable(outfd);
3600 continue;
3601 } else {
3602 return error.WouldBlock;
3603 },
3604 EBUSY => return error.DeviceBusy,
3605 EINTR => continue,
3606 EIO => return error.InputOutput,
3607 ENOBUFS => return error.SystemResources,
3608 EPIPE => return error.BrokenPipe,
3609 }
3610 }
3611 } else {
3612 @compileError("sendfile unimplemented for this target");
3613 }
3614}
3615
35013616pub const PollError = error{
35023617 /// The kernel had no space to allocate file descriptor tables.
35033618 SystemResources,
lib/std/os/linux.zig+8
......@@ -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
849pub 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
849857pub 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]) });