| ... | ... | @@ -5024,12 +5024,10 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len |
| 5024 | 5024 | |
| 5025 | 5025 | var has_copy_file_range_syscall = std.atomic.Int(u1).init(1); |
| 5026 | 5026 | |
| 5027 | | pub const CopyFileOptions = struct { |
| 5028 | | /// Size in bytes of the source files, if available saves a call to stat(). |
| 5029 | | file_size: ?u64 = null, |
| 5030 | | }; |
| 5027 | pub const CopyFileOptions = struct {}; |
| 5031 | 5028 | |
| 5032 | 5029 | pub const CopyFileError = error{ |
| 5030 | BadFileHandle, |
| 5033 | 5031 | SystemResources, |
| 5034 | 5032 | FileTooBig, |
| 5035 | 5033 | InputOutput, |
| ... | ... | @@ -5057,20 +5055,18 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr |
| 5057 | 5055 | } |
| 5058 | 5056 | } |
| 5059 | 5057 | |
| 5060 | | const src_file_size = options.file_size orelse |
| 5061 | | @bitCast(u64, (try fstat(fd_in)).size); |
| 5062 | | var remaining = src_file_size; |
| 5063 | | |
| 5064 | 5058 | if (std.Target.current.os.tag == .linux) { |
| 5065 | 5059 | // Try copy_file_range first as that works at the FS level and is the |
| 5066 | 5060 | // most efficient method (if available). |
| 5067 | 5061 | if (has_copy_file_range_syscall.get() != 0) { |
| 5068 | | cfr_loop: while (remaining > 0) { |
| 5069 | | const copy_amt = math.cast(usize, remaining) catch math.maxInt(usize); |
| 5070 | | const rc = linux.copy_file_range(fd_in, null, fd_out, null, copy_amt, 0); |
| 5062 | cfr_loop: while (true) { |
| 5063 | // The kernel checks `file_pos+count` for overflow, use a 32 bit |
| 5064 | // value so that the syscall won't return EINVAL except for |
| 5065 | // impossibly large files. |
| 5066 | const rc = linux.copy_file_range(fd_in, null, fd_out, null, math.maxInt(u32), 0); |
| 5071 | 5067 | switch (errno(rc)) { |
| 5072 | 5068 | 0 => {}, |
| 5073 | | EBADF => unreachable, |
| 5069 | EBADF => return error.BadFileHandle, |
| 5074 | 5070 | EFBIG => return error.FileTooBig, |
| 5075 | 5071 | EIO => return error.InputOutput, |
| 5076 | 5072 | EISDIR => return error.IsDir, |
| ... | ... | @@ -5079,27 +5075,34 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr |
| 5079 | 5075 | EOVERFLOW => return error.Unseekable, |
| 5080 | 5076 | EPERM => return error.PermissionDenied, |
| 5081 | 5077 | ETXTBSY => return error.FileBusy, |
| 5082 | | // these may not be regular files, try fallback |
| 5078 | // These may not be regular files, try fallback |
| 5083 | 5079 | EINVAL => break :cfr_loop, |
| 5084 | | // support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5080 | // Support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5085 | 5081 | EXDEV => break :cfr_loop, |
| 5086 | | // syscall added in Linux 4.5, use fallback |
| 5082 | // Syscall added in Linux 4.5, use fallback |
| 5087 | 5083 | ENOSYS => { |
| 5088 | 5084 | has_copy_file_range_syscall.set(0); |
| 5089 | 5085 | break :cfr_loop; |
| 5090 | 5086 | }, |
| 5091 | 5087 | else => |err| return unexpectedErrno(err), |
| 5092 | 5088 | } |
| 5093 | | remaining -= rc; |
| 5089 | // Terminate when no data was copied |
| 5090 | if (rc == 0) return; |
| 5094 | 5091 | } |
| 5095 | | return; |
| 5092 | // This point is reached when an error occurred, hopefully no data |
| 5093 | // was transferred yet |
| 5096 | 5094 | } |
| 5097 | 5095 | } |
| 5098 | 5096 | |
| 5099 | 5097 | // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the |
| 5100 | 5098 | // fallback code will copy the contents chunk by chunk. |
| 5101 | 5099 | const empty_iovec = [0]iovec_const{}; |
| 5102 | | _ = try sendfile(fd_out, fd_in, 0, remaining, &empty_iovec, &empty_iovec, 0); |
| 5100 | var offset: u64 = 0; |
| 5101 | sendfile_loop: while (true) { |
| 5102 | const amt = try sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0); |
| 5103 | if (amt == 0) break :sendfile_loop; |
| 5104 | offset += amt; |
| 5105 | } |
| 5103 | 5106 | } |
| 5104 | 5107 | |
| 5105 | 5108 | pub const PollError = error{ |