| ... | @@ -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,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 | var has_copy_file_range_syscall = std.atomic.Int(u1).init(1); | 5025 | var has_copy_file_range_syscall = std.atomic.Int(u1).init(1); |
| 5026 | | 5026 | |
| 5027 | pub const CopyFileOptions = struct { | 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 | }; | | |
| 5031 | | 5028 | |
| 5032 | pub const CopyFileError = error{ | 5029 | pub const CopyFileError = error{ |
| | 5030 | BadFileHandle, |
| 5033 | SystemResources, | 5031 | SystemResources, |
| 5034 | FileTooBig, | 5032 | FileTooBig, |
| 5035 | InputOutput, | 5033 | InputOutput, |
| ... | @@ -5057,20 +5055,18 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr | ... | @@ -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 | if (std.Target.current.os.tag == .linux) { | 5058 | if (std.Target.current.os.tag == .linux) { |
| 5065 | // Try copy_file_range first as that works at the FS level and is the | 5059 | // Try copy_file_range first as that works at the FS level and is the |
| 5066 | // most efficient method (if available). | 5060 | // most efficient method (if available). |
| 5067 | if (has_copy_file_range_syscall.get() != 0) { | 5061 | if (has_copy_file_range_syscall.get() != 0) { |
| 5068 | cfr_loop: while (remaining > 0) { | 5062 | cfr_loop: while (true) { |
| 5069 | const copy_amt = math.cast(usize, remaining) catch math.maxInt(usize); | 5063 | // The kernel checks `file_pos+count` for overflow, use a 32 bit |
| 5070 | const rc = linux.copy_file_range(fd_in, null, fd_out, null, copy_amt, 0); | 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 | switch (errno(rc)) { | 5067 | switch (errno(rc)) { |
| 5072 | 0 => {}, | 5068 | 0 => {}, |
| 5073 | EBADF => unreachable, | 5069 | EBADF => return error.BadFileHandle, |
| 5074 | EFBIG => return error.FileTooBig, | 5070 | EFBIG => return error.FileTooBig, |
| 5075 | EIO => return error.InputOutput, | 5071 | EIO => return error.InputOutput, |
| 5076 | EISDIR => return error.IsDir, | 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,27 +5075,34 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr |
| 5079 | EOVERFLOW => return error.Unseekable, | 5075 | EOVERFLOW => return error.Unseekable, |
| 5080 | EPERM => return error.PermissionDenied, | 5076 | EPERM => return error.PermissionDenied, |
| 5081 | ETXTBSY => return error.FileBusy, | 5077 | ETXTBSY => return error.FileBusy, |
| 5082 | // these may not be regular files, try fallback | 5078 | // These may not be regular files, try fallback |
| 5083 | EINVAL => break :cfr_loop, | 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 | EXDEV => break :cfr_loop, | 5081 | EXDEV => break :cfr_loop, |
| 5086 | // syscall added in Linux 4.5, use fallback | 5082 | // Syscall added in Linux 4.5, use fallback |
| 5087 | ENOSYS => { | 5083 | ENOSYS => { |
| 5088 | has_copy_file_range_syscall.set(0); | 5084 | has_copy_file_range_syscall.set(0); |
| 5089 | break :cfr_loop; | 5085 | break :cfr_loop; |
| 5090 | }, | 5086 | }, |
| 5091 | else => |err| return unexpectedErrno(err), | 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 | // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the | 5097 | // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the |
| 5100 | // fallback code will copy the contents chunk by chunk. | 5098 | // fallback code will copy the contents chunk by chunk. |
| 5101 | const empty_iovec = [0]iovec_const{}; | 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 | pub const PollError = error{ | 5108 | pub const PollError = error{ |