authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-03 19:51:22+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-03 19:51:22+02:00
log8b4f5f039df65980d0a0ec6add1caf4c9bf2468c
tree100a497fee7a76a750100da7f23b190043530799
parent0f248e0988b24bf4fcdaadd0e47127462206711e

Alternative strategy to avoid calling stat()

This is an optimization as it avoids an extra syscall, but it's also a workaround for fstat being not available on Windows.

2 files changed, 22 insertions(+), 19 deletions(-)

lib/std/fs.zig+1-1
...@@ -1823,7 +1823,7 @@ pub const Dir = struct {...@@ -1823,7 +1823,7 @@ pub const Dir = struct {
1823 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });1823 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
1824 defer atomic_file.deinit();1824 defer atomic_file.deinit();
18251825
1826 try os.copy_file(in_file.handle, atomic_file.file.handle, .{ .file_size = size });1826 try os.copy_file(in_file.handle, atomic_file.file.handle, .{});
1827 return atomic_file.finish();1827 return atomic_file.finish();
1828 }1828 }
18291829
lib/std/os.zig+21-18
...@@ -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
50245024
5025var has_copy_file_range_syscall = std.atomic.Int(u1).init(1);5025var has_copy_file_range_syscall = std.atomic.Int(u1).init(1);
50265026
5027pub const CopyFileOptions = struct {5027pub const CopyFileOptions = struct {};
5028 /// Size in bytes of the source files, if available saves a call to stat().
5029 file_size: ?u64 = null,
5030};
50315028
5032pub const CopyFileError = error{5029pub 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 }
50595057
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 the5059 // 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 fallback5078 // 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 fallback5080 // 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 fallback5082 // 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 }
50985096
5099 // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the5097 // 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}
51045107
5105pub const PollError = error{5108pub const PollError = error{