| ... | ... | @@ -4981,19 +4981,15 @@ pub const CopyFileRangeError = error{ |
| 4981 | 4981 | pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize { |
| 4982 | 4982 | const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; |
| 4983 | 4983 | |
| 4984 | | // TODO support for other systems than linux |
| 4985 | | const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false; |
| 4986 | | |
| 4987 | | if (use_c or try_syscall) { |
| 4984 | if (std.Target.current.os.tag == .linux and |
| 4985 | (use_c or has_copy_file_range_syscall.get() != 0)) |
| 4986 | { |
| 4988 | 4987 | const sys = if (use_c) std.c else linux; |
| 4989 | 4988 | |
| 4990 | 4989 | var off_in_copy = @bitCast(i64, off_in); |
| 4991 | 4990 | var off_out_copy = @bitCast(i64, off_out); |
| 4992 | 4991 | |
| 4993 | 4992 | const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags); |
| 4994 | | |
| 4995 | | // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018 |
| 4996 | | |
| 4997 | 4993 | switch (sys.getErrno(rc)) { |
| 4998 | 4994 | 0 => return @intCast(usize, rc), |
| 4999 | 4995 | EBADF => unreachable, |
| ... | ... | @@ -5005,9 +5001,14 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len |
| 5005 | 5001 | EOVERFLOW => return error.Unseekable, |
| 5006 | 5002 | EPERM => return error.PermissionDenied, |
| 5007 | 5003 | ETXTBSY => return error.FileBusy, |
| 5008 | | EINVAL => {}, // these may not be regular files, try fallback |
| 5009 | | EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5010 | | ENOSYS => {}, // syscall added in Linux 4.5, use fallback |
| 5004 | // these may not be regular files, try fallback |
| 5005 | EINVAL => {}, |
| 5006 | // support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5007 | EXDEV => {}, |
| 5008 | // syscall added in Linux 4.5, use fallback |
| 5009 | ENOSYS => { |
| 5010 | has_copy_file_range_syscall.set(0); |
| 5011 | }, |
| 5011 | 5012 | else => |err| return unexpectedErrno(err), |
| 5012 | 5013 | } |
| 5013 | 5014 | } |
| ... | ... | @@ -5021,6 +5022,86 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len |
| 5021 | 5022 | return pwrite(fd_out, buf[0..amt_read], off_out); |
| 5022 | 5023 | } |
| 5023 | 5024 | |
| 5025 | var has_copy_file_range_syscall = std.atomic.Int(u1).init(1); |
| 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 | }; |
| 5031 | |
| 5032 | pub const CopyFileError = error{ |
| 5033 | SystemResources, |
| 5034 | FileTooBig, |
| 5035 | InputOutput, |
| 5036 | IsDir, |
| 5037 | OutOfMemory, |
| 5038 | NoSpaceLeft, |
| 5039 | Unseekable, |
| 5040 | PermissionDenied, |
| 5041 | FileBusy, |
| 5042 | } || FStatError || SendFileError; |
| 5043 | |
| 5044 | /// Transfer all the data between two file descriptors in the most efficient way. |
| 5045 | /// No metadata is transferred over. |
| 5046 | pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileError!void { |
| 5047 | if (comptime std.Target.current.isDarwin()) { |
| 5048 | const rc = system.fcopyfile(fd_in, fd_out, null, system.COPYFILE_DATA); |
| 5049 | switch (errno(rc)) { |
| 5050 | 0 => return, |
| 5051 | EINVAL => unreachable, |
| 5052 | ENOMEM => return error.SystemResources, |
| 5053 | // The source file was not a directory, symbolic link, or regular file. |
| 5054 | // Try with the fallback path before giving up. |
| 5055 | ENOTSUP => {}, |
| 5056 | else => |err| return unexpectedErrno(err), |
| 5057 | } |
| 5058 | } |
| 5059 | |
| 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) { |
| 5065 | // Try copy_file_range first as that works at the FS level and is the |
| 5066 | // most efficient method (if available). |
| 5067 | 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); |
| 5071 | switch (errno(rc)) { |
| 5072 | 0 => {}, |
| 5073 | EBADF => unreachable, |
| 5074 | EFBIG => return error.FileTooBig, |
| 5075 | EIO => return error.InputOutput, |
| 5076 | EISDIR => return error.IsDir, |
| 5077 | ENOMEM => return error.OutOfMemory, |
| 5078 | ENOSPC => return error.NoSpaceLeft, |
| 5079 | EOVERFLOW => return error.Unseekable, |
| 5080 | EPERM => return error.PermissionDenied, |
| 5081 | ETXTBSY => return error.FileBusy, |
| 5082 | // these may not be regular files, try fallback |
| 5083 | EINVAL => break :cfr_loop, |
| 5084 | // support for cross-filesystem copy added in Linux 5.3, use fallback |
| 5085 | EXDEV => break :cfr_loop, |
| 5086 | // syscall added in Linux 4.5, use fallback |
| 5087 | ENOSYS => { |
| 5088 | has_copy_file_range_syscall.set(0); |
| 5089 | break :cfr_loop; |
| 5090 | }, |
| 5091 | else => |err| return unexpectedErrno(err), |
| 5092 | } |
| 5093 | remaining -= rc; |
| 5094 | } |
| 5095 | return; |
| 5096 | } |
| 5097 | } |
| 5098 | |
| 5099 | // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the |
| 5100 | // fallback code will copy the contents chunk by chunk. |
| 5101 | const empty_iovec = [0]iovec_const{}; |
| 5102 | _ = try sendfile(fd_out, fd_in, 0, remaining, &empty_iovec, &empty_iovec, 0); |
| 5103 | } |
| 5104 | |
| 5024 | 5105 | pub const PollError = error{ |
| 5025 | 5106 | /// The kernel had no space to allocate file descriptor tables. |
| 5026 | 5107 | SystemResources, |